knapsack_pro 2.16.0 → 2.18.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +1 -1
  3. data/CHANGELOG.md +32 -0
  4. data/README.md +4 -4
  5. data/knapsack_pro.gemspec +4 -5
  6. data/lib/knapsack_pro/adapters/rspec_adapter.rb +21 -0
  7. data/lib/knapsack_pro/base_allocator_builder.rb +1 -0
  8. data/lib/knapsack_pro/config/ci/circle.rb +1 -2
  9. data/lib/knapsack_pro/config/env.rb +8 -0
  10. data/lib/knapsack_pro/repository_adapters/git_adapter.rb +2 -1
  11. data/lib/knapsack_pro/runners/base_runner.rb +4 -0
  12. data/lib/knapsack_pro/runners/cucumber_runner.rb +3 -0
  13. data/lib/knapsack_pro/runners/minitest_runner.rb +3 -0
  14. data/lib/knapsack_pro/runners/queue/base_runner.rb +4 -0
  15. data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +6 -6
  16. data/lib/knapsack_pro/runners/queue/minitest_runner.rb +3 -1
  17. data/lib/knapsack_pro/runners/queue/rspec_runner.rb +5 -21
  18. data/lib/knapsack_pro/runners/rspec_runner.rb +6 -0
  19. data/lib/knapsack_pro/runners/spinach_runner.rb +4 -1
  20. data/lib/knapsack_pro/runners/test_unit_runner.rb +16 -2
  21. data/lib/knapsack_pro/tracker.rb +54 -1
  22. data/lib/knapsack_pro/version.rb +1 -1
  23. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +98 -0
  24. data/spec/knapsack_pro/adapters/test_unit_adapter_spec.rb +8 -1
  25. data/spec/knapsack_pro/config/ci/circle_spec.rb +3 -3
  26. data/spec/knapsack_pro/config/env_spec.rb +25 -0
  27. data/spec/knapsack_pro/repository_adapters/git_adapter_spec.rb +3 -3
  28. data/spec/knapsack_pro/runners/cucumber_runner_spec.rb +9 -1
  29. data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +6 -2
  30. data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +2 -0
  31. data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +4 -17
  32. data/spec/knapsack_pro/runners/rspec_runner_spec.rb +13 -4
  33. data/spec/knapsack_pro/runners/spinach_runner_spec.rb +14 -3
  34. data/spec/knapsack_pro/runners/test_unit_runner_spec.rb +1 -3
  35. data/spec/knapsack_pro/tracker_spec.rb +67 -2
  36. data/spec/spec_helper.rb +0 -3
  37. metadata +21 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 534699a91609a71a2621b9625a3ff555a58bf385728be90adce4cfe6d6b771dd
4
- data.tar.gz: 913fc4a85433a50f3639bdc087995f9dc5e482b33d7768cfdb84c0f767326c2c
3
+ metadata.gz: 6a298724c7651190199978edc99b96116abcbb827d12237e128f2c5d87297847
4
+ data.tar.gz: 2750130109b114747278d86fd4bc2477d143b2f1f3564f7b36d224f5044e29cf
5
5
  SHA512:
6
- metadata.gz: da5e5ef1c262d97b6214eddff32c8aa2031fe122ad1597a1cf4bd2a939fe82f9fd6b0c7287861f5373521f731318a0e856a331220ae0076eef9cdf8323ab0548
7
- data.tar.gz: 2e058c2c3b82c9c97481ab84873c652fb3ab4844521c4746efeaca2dccca8b265b58ba427d44af56b130769bf56c6cbacd84b516b1e6b44d099a8a33b2ef340b
6
+ metadata.gz: 7742dbaf98eb6bc1aeda1c15c2dd3b8f627fe27c91fa58a4f46e71d2ff4839d5899005b36deddcb619745689dd6426f9319df1b70ad26f317be2cea2e6922799
7
+ data.tar.gz: a102bc5ab00cabc5ec4f7007e52081fd97ca194ea8e84072b2df8fb66664c6e320ea7b76d0970aa702ed9549e36bd5c702a7156de914b5fbdf9fe00c43de8351
data/.circleci/config.yml CHANGED
@@ -8,7 +8,7 @@ jobs:
8
8
  parallelism: 1
9
9
  docker:
10
10
  # specify the version you desire here
11
- - image: circleci/ruby:2.7.0
11
+ - image: circleci/ruby:3.0.1
12
12
  environment:
13
13
  CODECLIMATE_REPO_TOKEN: b6626e682a8e97e0c5978febc92c3526792a2d018b41b8e1b52689da37fb7d92
14
14
 
data/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Change Log
2
2
 
3
+ ### 2.18.2
4
+
5
+ * Track all test files assigned to a CI node in Regular Mode including pending test files in order to retry proper set of tests on the retried CI node
6
+
7
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/152
8
+
9
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.18.1...v2.18.2
10
+
11
+ ### 2.18.1
12
+
13
+ * Ensure RSpec is loaded to check its version for RSpec split by test examples feature
14
+
15
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/151
16
+
17
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.18.0...v2.18.1
18
+
19
+ ### 2.18.0
20
+
21
+ * Do not allow to use the RSpec tag option together with the RSpec split by test examples feature in knapsack_pro gem in Regular Mode
22
+
23
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/148
24
+
25
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.17.0...v2.18.0
26
+
27
+ ### 2.17.0
28
+
29
+ * Use Ruby 3 in development and add small improvements
30
+
31
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/147
32
+
33
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.16.0...v2.17.0
34
+
3
35
  ### 2.16.0
4
36
 
5
37
  * Improve test time execution tracking for RSpec
data/README.md CHANGED
@@ -440,13 +440,13 @@ If your test suite is very long and the RSpec output is too long for your CI nod
440
440
  ### Additional info about queue mode
441
441
 
442
442
  * You should use a separate API token for queue mode than for regular mode to avoid problems with test suite split (especially in case you would like to go back to regular mode).
443
- There might be some cached test suite splits for git commits you have run in past for API token you used in queue mode because of the [flag `KNAPSACK_PRO_FIXED_TEST_SUITE_SPLIT=true` for regular mode which is default](#knapsack_pro_fixed_test_suite_splite-test-suite-split-based-on-seed).
443
+ There might be some cached test suite splits for git commits you have run in past for API token you used in queue mode because of the [flag `KNAPSACK_PRO_FIXED_TEST_SUITE_SPLIT=true` for regular mode which is default](#knapsack_pro_fixed_test_suite_split-test-suite-split-based-on-seed).
444
444
 
445
445
  * If you are not using one of the [supported CI providers](#supported-ci-providers) then please note that the knapsack_pro gem doesn't have a CI build ID in order to generate a queue for each particular CI build. This may result in two different CI builds taking tests from the same queue when CI builds are running at the same time against the same git commit.
446
446
 
447
447
  To avoid this you should specify a unique `KNAPSACK_PRO_CI_NODE_BUILD_ID` environment variable for each CI build. This mean that each CI node that is part of particular CI build should have the same value for `KNAPSACK_PRO_CI_NODE_BUILD_ID`.
448
448
 
449
- * Note that in the Queue Mode by default you cannot retry the failed CI node with exactly the same subset of tests that were run on the CI node in the first place. It's possible in regular mode ([read more](#knapsack_pro_fixed_test_suite_splite-test-suite-split-based-on-seed)). If you want to have similar behavior in Queue Mode you need to explicitly [enable it](#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node).
449
+ * Note that in the Queue Mode by default you cannot retry the failed CI node with exactly the same subset of tests that were run on the CI node in the first place. It's possible in regular mode ([read more](#knapsack_pro_fixed_test_suite_split-test-suite-split-based-on-seed)). If you want to have similar behavior in Queue Mode you need to explicitly [enable it](#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node).
450
450
 
451
451
  By default the Queue Mode works this way:
452
452
 
@@ -626,7 +626,7 @@ This is only for maintainer of knapsack_pro gem. Not for the end users.
626
626
 
627
627
  ### Required CI configuration if you use retry single failed CI node feature on your CI server when KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true (in Queue Mode) or KNAPSACK_PRO_FIXED_TEST_SUITE_SPLIT=true (in Regular Mode)
628
628
 
629
- Read below required configuration step if you use Queue Mode and you set [`KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true`](#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node) or you use Regular Mode which has by default [`KNAPSACK_PRO_FIXED_TEST_SUITE_SPLIT=true`](#knapsack_pro_fixed_test_suite_splite-test-suite-split-based-on-seed).
629
+ Read below required configuration step if you use Queue Mode and you set [`KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true`](#knapsack_pro_fixed_queue_split-remember-queue-split-on-retry-ci-node) or you use Regular Mode which has by default [`KNAPSACK_PRO_FIXED_TEST_SUITE_SPLIT=true`](#knapsack_pro_fixed_test_suite_split-test-suite-split-based-on-seed).
630
630
 
631
631
  * __IMPORTANT:__ If you use __the feature to retry only a single failed CI node__ on your CI server (for instance you use Buildkite and you use [auto-retry](https://buildkite.com/docs/pipelines/command-step#retry-attributes) for the failed job) then you need to be aware of [a race condition that could happen](https://github.com/KnapsackPro/knapsack_pro-ruby/pull/100). knapsack_pro should not allow running tests in Fallback Mode in the case when the failed CI node was retried to prevent running the wrong set of tests.
632
632
 
@@ -838,7 +838,7 @@ Here you can read how to configure [junit formatter](https://knapsackpro.com/faq
838
838
  # spec_helper.rb or rails_helper.rb
839
839
 
840
840
  # TODO This must be the same path as value for rspec --out argument
841
- # Note the path should not contain sign ~, for instance path ~/project/tmp/rspec.xml may not work. Please use full path instead.
841
+ # Note the path should not contain ~ char, for instance path ~/project/tmp/rspec.xml may not work. Please use full path instead.
842
842
  TMP_RSPEC_XML_REPORT = 'tmp/rspec.xml'
843
843
  # move results to FINAL_RSPEC_XML_REPORT so the results won't accumulate with duplicated xml tags in TMP_RSPEC_XML_REPORT
844
844
  FINAL_RSPEC_XML_REPORT = 'tmp/rspec_final_results.xml'
data/knapsack_pro.gemspec CHANGED
@@ -29,14 +29,13 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_development_dependency 'bundler', '>= 1.6'
31
31
  spec.add_development_dependency 'rspec', '~> 3.0', '>= 2.10.0'
32
- spec.add_development_dependency 'rspec-its', '~> 1.2'
32
+ spec.add_development_dependency 'rspec-its', '~> 1.3'
33
33
  spec.add_development_dependency 'cucumber', '>= 0'
34
34
  spec.add_development_dependency 'spinach', '>= 0.8'
35
35
  spec.add_development_dependency 'minitest', '>= 5.0.0'
36
36
  spec.add_development_dependency 'test-unit', '>= 3.0.0'
37
- spec.add_development_dependency 'codeclimate-test-reporter', '~> 0'
38
37
  spec.add_development_dependency 'pry', '~> 0'
39
- spec.add_development_dependency 'vcr', '~> 2.9'
40
- spec.add_development_dependency 'webmock', '~> 1.21'
41
- spec.add_development_dependency 'timecop', '>= 0.1.0'
38
+ spec.add_development_dependency 'vcr', '>= 6.0'
39
+ spec.add_development_dependency 'webmock', '>= 3.13'
40
+ spec.add_development_dependency 'timecop', '>= 0.9.4'
42
41
  end
@@ -3,6 +3,27 @@ module KnapsackPro
3
3
  class RSpecAdapter < BaseAdapter
4
4
  TEST_DIR_PATTERN = 'spec/**{,/*/**}/*_spec.rb'
5
5
 
6
+ def self.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
7
+ if KnapsackPro::Config::Env.rspec_split_by_test_examples? && has_tag_option?(cli_args)
8
+ error_message = 'It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it#warning-dont-use-rspec-tag-option'
9
+ KnapsackPro.logger.error(error_message)
10
+ raise error_message
11
+ end
12
+ end
13
+
14
+ def self.has_tag_option?(cli_args)
15
+ # use start_with? because user can define tag option in a few ways:
16
+ # -t mytag
17
+ # -tmytag
18
+ # --tag mytag
19
+ # --tag=mytag
20
+ cli_args.any? { |arg| arg.start_with?('-t') || arg.start_with?('--tag') }
21
+ end
22
+
23
+ def self.has_format_option?(cli_args)
24
+ cli_args.any? { |arg| arg.start_with?('-f') || arg.start_with?('--format') }
25
+ end
26
+
6
27
  def self.test_path(example_group)
7
28
  if defined?(::Turnip) && ::Turnip::VERSION.to_i < 2
8
29
  unless example_group[:turnip]
@@ -34,6 +34,7 @@ module KnapsackPro
34
34
  test_files_to_run = all_test_files_to_run
35
35
 
36
36
  if adapter_class == KnapsackPro::Adapters::RSpecAdapter && KnapsackPro::Config::Env.rspec_split_by_test_examples?
37
+ require 'rspec/core/version'
37
38
  unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
38
39
  raise 'RSpec >= 3.3.0 is required to split test files by test examples. Learn more: https://github.com/KnapsackPro/knapsack_pro-ruby#split-test-files-by-test-cases'
39
40
  end
@@ -23,8 +23,7 @@ module KnapsackPro
23
23
  end
24
24
 
25
25
  def project_dir
26
- project_repo_name = ENV['CIRCLE_PROJECT_REPONAME']
27
- "/home/ubuntu/#{project_repo_name}" if project_repo_name
26
+ ENV['CIRCLE_WORKING_DIRECTORY']
28
27
  end
29
28
  end
30
29
  end
@@ -257,6 +257,14 @@ module KnapsackPro
257
257
  ENV['KNAPSACK_PRO_LOG_DIR']
258
258
  end
259
259
 
260
+ def test_runner_adapter
261
+ ENV['KNAPSACK_PRO_TEST_RUNNER_ADAPTER']
262
+ end
263
+
264
+ def set_test_runner_adapter(adapter_class)
265
+ ENV['KNAPSACK_PRO_TEST_RUNNER_ADAPTER'] = adapter_class.to_s.split('::').last
266
+ end
267
+
260
268
  private
261
269
 
262
270
  def required_env(env_name)
@@ -17,7 +17,8 @@ module KnapsackPro
17
17
  private
18
18
 
19
19
  def working_dir
20
- KnapsackPro::Config::Env.project_dir
20
+ dir = KnapsackPro::Config::Env.project_dir
21
+ File.expand_path(dir)
21
22
  end
22
23
  end
23
24
  end
@@ -35,6 +35,10 @@ module KnapsackPro
35
35
 
36
36
  attr_reader :allocator_builder,
37
37
  :allocator
38
+
39
+ def self.child_status
40
+ $?
41
+ end
38
42
  end
39
43
  end
40
44
  end
@@ -6,11 +6,14 @@ module KnapsackPro
6
6
  ENV['KNAPSACK_PRO_RECORDING_ENABLED'] = 'true'
7
7
 
8
8
  adapter_class = KnapsackPro::Adapters::CucumberAdapter
9
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
9
10
  runner = new(adapter_class)
10
11
 
11
12
  if runner.test_files_to_execute_exist?
12
13
  adapter_class.verify_bind_method_called
13
14
 
15
+ KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
16
+
14
17
  require 'cucumber/rake/task'
15
18
 
16
19
  task_name = 'knapsack_pro:cucumber_run'
@@ -6,11 +6,14 @@ module KnapsackPro
6
6
  ENV['KNAPSACK_PRO_RECORDING_ENABLED'] = 'true'
7
7
 
8
8
  adapter_class = KnapsackPro::Adapters::MinitestAdapter
9
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
9
10
  runner = new(adapter_class)
10
11
 
11
12
  if runner.test_files_to_execute_exist?
12
13
  adapter_class.verify_bind_method_called
13
14
 
15
+ KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
16
+
14
17
  task_name = 'knapsack_pro:minitest_run'
15
18
 
16
19
  if Rake::Task.task_defined?(task_name)
@@ -29,6 +29,10 @@ module KnapsackPro
29
29
 
30
30
  attr_reader :allocator_builder,
31
31
  :allocator
32
+
33
+ def self.child_status
34
+ $?
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -9,7 +9,9 @@ module KnapsackPro
9
9
  ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
10
10
  ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
11
11
 
12
- runner = new(KnapsackPro::Adapters::CucumberAdapter)
12
+ adapter_class = KnapsackPro::Adapters::CucumberAdapter
13
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
14
+ runner = new(adapter_class)
13
15
 
14
16
  accumulator = {
15
17
  status: :next,
@@ -95,16 +97,14 @@ module KnapsackPro
95
97
  # which is defined in lib/knapsack_pro/adapters/cucumber_adapter.rb
96
98
  ENV['KNAPSACK_PRO_BEFORE_QUEUE_HOOK_CALLED'] = 'true'
97
99
 
98
- process_status = $?
99
-
100
- unless process_status.exited?
100
+ unless child_status.exited?
101
101
  raise "Cucumber process execution failed. It's likely that your CI server has exceeded"\
102
102
  " its available memory. Please try changing CI config or retrying the CI build.\n"\
103
103
  "Failed command: #{cmd}\n"\
104
- "Process status: #{process_status.inspect}"
104
+ "Process status: #{child_status.inspect}"
105
105
  end
106
106
 
107
- process_status.exitstatus
107
+ child_status.exitstatus
108
108
  end
109
109
  end
110
110
  end
@@ -9,7 +9,9 @@ module KnapsackPro
9
9
  ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
10
10
  ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
11
11
 
12
- runner = new(KnapsackPro::Adapters::MinitestAdapter)
12
+ adapter_class = KnapsackPro::Adapters::MinitestAdapter
13
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
14
+ runner = new(adapter_class)
13
15
 
14
16
  # Add test_dir to load path to make work:
15
17
  # require 'test_helper'
@@ -11,18 +11,15 @@ module KnapsackPro
11
11
  ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
12
12
  ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
13
13
 
14
- runner = new(KnapsackPro::Adapters::RSpecAdapter)
14
+ adapter_class = KnapsackPro::Adapters::RSpecAdapter
15
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
16
+ runner = new(adapter_class)
15
17
 
16
18
  cli_args = (args || '').split
17
-
18
- if KnapsackPro::Config::Env.rspec_split_by_test_examples? && has_tag_option?(cli_args)
19
- error_message = 'It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it#warning-dont-use-rspec-tag-option'
20
- KnapsackPro.logger.error(error_message)
21
- raise error_message
22
- end
19
+ adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
23
20
 
24
21
  # when format option is not defined by user then use progress formatter to show tests execution progress
25
- cli_args += ['--format', 'progress'] unless has_format_option?(cli_args)
22
+ cli_args += ['--format', 'progress'] unless adapter_class.has_format_option?(cli_args)
26
23
 
27
24
  cli_args += [
28
25
  # shows summary of all tests executed in Queue Mode at the very end
@@ -154,19 +151,6 @@ module KnapsackPro
154
151
  ::RSpec.configuration.reset_filters
155
152
  end
156
153
  end
157
-
158
- def self.has_tag_option?(cli_args)
159
- # use start_with? because user can define tag option in a few ways:
160
- # -t mytag
161
- # -tmytag
162
- # --tag mytag
163
- # --tag=mytag
164
- cli_args.any? { |arg| arg.start_with?('-t') || arg.start_with?('--tag') }
165
- end
166
-
167
- def self.has_format_option?(cli_args)
168
- cli_args.any? { |arg| arg.start_with?('-f') || arg.start_with?('--format') }
169
- end
170
154
  end
171
155
  end
172
156
  end
@@ -6,11 +6,17 @@ module KnapsackPro
6
6
  ENV['KNAPSACK_PRO_RECORDING_ENABLED'] = 'true'
7
7
 
8
8
  adapter_class = KnapsackPro::Adapters::RSpecAdapter
9
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
9
10
  runner = new(adapter_class)
10
11
 
11
12
  if runner.test_files_to_execute_exist?
12
13
  adapter_class.verify_bind_method_called
13
14
 
15
+ cli_args = (args || '').split
16
+ adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
17
+
18
+ KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
19
+
14
20
  require 'rspec/core/rake_task'
15
21
 
16
22
  task_name = 'knapsack_pro:rspec_run'
@@ -5,15 +5,18 @@ module KnapsackPro
5
5
  ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_spinach
6
6
 
7
7
  adapter_class = KnapsackPro::Adapters::SpinachAdapter
8
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
8
9
  runner = new(adapter_class)
9
10
 
10
11
  if runner.test_files_to_execute_exist?
11
12
  adapter_class.verify_bind_method_called
12
13
 
14
+ KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
15
+
13
16
  cmd = %Q[KNAPSACK_PRO_RECORDING_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=#{ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN']} bundle exec spinach #{args} --features_path #{runner.test_dir} -- #{runner.stringify_test_file_paths}]
14
17
 
15
18
  Kernel.system(cmd)
16
- Kernel.exit($?.exitstatus) unless $?.exitstatus.zero?
19
+ Kernel.exit(child_status.exitstatus) unless child_status.exitstatus.zero?
17
20
  end
18
21
  end
19
22
  end
@@ -6,12 +6,13 @@ module KnapsackPro
6
6
  ENV['KNAPSACK_PRO_RECORDING_ENABLED'] = 'true'
7
7
 
8
8
  adapter_class = KnapsackPro::Adapters::TestUnitAdapter
9
+ KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
9
10
  runner = new(adapter_class)
10
11
 
11
12
  if runner.test_files_to_execute_exist?
12
13
  adapter_class.verify_bind_method_called
13
14
 
14
- require 'test/unit'
15
+ KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
15
16
 
16
17
  cli_args =
17
18
  (args || '').split +
@@ -19,13 +20,26 @@ module KnapsackPro
19
20
  File.expand_path(f)
20
21
  end
21
22
 
22
- exit ::Test::Unit::AutoRunner.run(
23
+ exit test_unit_autorunner_run(
23
24
  true,
24
25
  runner.test_dir,
25
26
  cli_args
26
27
  )
27
28
  end
28
29
  end
30
+
31
+ private
32
+
33
+ # https://www.rubydoc.info/github/test-unit/test-unit/Test/Unit/AutoRunner#run-class_method
34
+ def self.test_unit_autorunner_run(force_standalone, default_dir, argv)
35
+ require 'test/unit'
36
+
37
+ ::Test::Unit::AutoRunner.run(
38
+ force_standalone,
39
+ default_dir,
40
+ argv
41
+ )
42
+ end
29
43
  end
30
44
  end
31
45
  end
@@ -6,16 +6,22 @@ module KnapsackPro
6
6
  # to better allocate it in Queue Mode for future CI build runs
7
7
  DEFAULT_TEST_FILE_TIME = 0.0 # seconds
8
8
 
9
- attr_reader :global_time_since_beginning, :global_time, :test_files_with_time
9
+ attr_reader :global_time_since_beginning, :global_time, :test_files_with_time, :prerun_tests_loaded
10
10
  attr_writer :current_test_path
11
11
 
12
12
  def initialize
13
13
  @global_time_since_beginning = 0
14
+ FileUtils.mkdir_p(tracker_dir_path)
14
15
  set_defaults
15
16
  end
16
17
 
17
18
  def reset!
18
19
  set_defaults
20
+
21
+ # Remove report only when the reset! method is called explicitly.
22
+ # The report should be persisted on the disk so that multiple tracker instances can share the report state.
23
+ # Tracker instance can be created by knapsack_pro process and a separate tracker is created by rake task (e.g., RSpec) in Regular Mode.
24
+ File.delete(prerun_tests_report_path) if File.exists?(prerun_tests_report_path)
19
25
  end
20
26
 
21
27
  def start_timer
@@ -54,9 +60,18 @@ module KnapsackPro
54
60
  measured_time: false,
55
61
  }
56
62
  end
63
+
64
+ save_prerun_tests_report(@test_files_with_time)
65
+
66
+ @prerun_tests_loaded = true
57
67
  end
58
68
 
59
69
  def to_a
70
+ # When the test files are not loaded in the memory then load them from the disk.
71
+ # Useful for the Regular Mode when the memory is not shared between tracker instances.
72
+ # Tracker instance can be created by knapsack_pro process and a separate tracker is created by rake task (e.g., RSpec)
73
+ load_prerun_tests unless prerun_tests_loaded
74
+
60
75
  test_files = []
61
76
  @test_files_with_time.each do |path, hash|
62
77
  test_files << {
@@ -73,6 +88,44 @@ module KnapsackPro
73
88
  @global_time = 0
74
89
  @test_files_with_time = {}
75
90
  @current_test_path = nil
91
+ @prerun_tests_loaded = false
92
+ end
93
+
94
+ def tracker_dir_path
95
+ "#{KnapsackPro::Config::Env::TMP_DIR}/tracker"
96
+ end
97
+
98
+ def prerun_tests_report_path
99
+ raise 'Test runner adapter not set. Report a bug to the Knapsack Pro support.' unless KnapsackPro::Config::Env.test_runner_adapter
100
+ report_name = "prerun_tests_#{KnapsackPro::Config::Env.test_runner_adapter}_node_#{KnapsackPro::Config::Env.ci_node_index}.json"
101
+ File.join(tracker_dir_path, report_name)
102
+ end
103
+
104
+ def save_prerun_tests_report(hash)
105
+ report_json = JSON.pretty_generate(hash)
106
+
107
+ File.open(prerun_tests_report_path, 'w+') do |f|
108
+ f.write(report_json)
109
+ end
110
+ end
111
+
112
+ def read_prerun_tests_report
113
+ JSON.parse(File.read(prerun_tests_report_path))
114
+ end
115
+
116
+ def load_prerun_tests
117
+ read_prerun_tests_report.each do |test_file_path, hash|
118
+ # Load only test files that were not measured. For example,
119
+ # track test files assigned to CI node but never executed by test runner (e.g., pending RSpec spec files).
120
+ next if @test_files_with_time.key?(test_file_path)
121
+
122
+ @test_files_with_time[test_file_path] = {
123
+ time_execution: hash.fetch('time_execution'),
124
+ measured_time: hash.fetch('measured_time'),
125
+ }
126
+ end
127
+
128
+ @prerun_tests_loaded = true
76
129
  end
77
130
 
78
131
  def update_global_time(execution_time)
@@ -1,3 +1,3 @@
1
1
  module KnapsackPro
2
- VERSION = '2.16.0'
2
+ VERSION = '2.18.2'
3
3
  end
@@ -12,6 +12,104 @@ describe KnapsackPro::Adapters::RSpecAdapter do
12
12
  it_behaves_like 'adapter'
13
13
  end
14
14
 
15
+ describe '.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!' do
16
+ let(:cli_args) { double }
17
+
18
+ subject { described_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args) }
19
+
20
+ before do
21
+ expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(rspec_split_by_test_examples_enabled)
22
+ end
23
+
24
+ context 'when RSpec split by test examples enabled' do
25
+ let(:rspec_split_by_test_examples_enabled) { true }
26
+
27
+ before do
28
+ expect(described_class).to receive(:has_tag_option?).with(cli_args).and_return(has_tag_option)
29
+ end
30
+
31
+ context 'when RSpec tag option is provided' do
32
+ let(:has_tag_option) { true }
33
+
34
+ it do
35
+ expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
36
+ end
37
+ end
38
+
39
+ context 'when RSpec tag option is not provided' do
40
+ let(:has_tag_option) { false }
41
+
42
+ it 'does nothing' do
43
+ expect(subject).to be_nil
44
+ end
45
+ end
46
+ end
47
+
48
+ context 'when RSpec split by test examples disabled' do
49
+ let(:rspec_split_by_test_examples_enabled) { false }
50
+
51
+ it 'does nothing' do
52
+ expect(subject).to be_nil
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '.has_tag_option?' do
58
+ subject { described_class.has_tag_option?(cli_args) }
59
+
60
+ context 'when tag option is provided as -t' do
61
+ let(:cli_args) { ['-t', 'mytag'] }
62
+
63
+ it { expect(subject).to be true }
64
+ end
65
+
66
+ context 'when tag option is provided as --tag' do
67
+ let(:cli_args) { ['--tag', 'mytag'] }
68
+
69
+ it { expect(subject).to be true }
70
+ end
71
+
72
+ context 'when tag option is provided without delimiter' do
73
+ let(:cli_args) { ['-tmytag'] }
74
+
75
+ it { expect(subject).to be true }
76
+ end
77
+
78
+ context 'when tag option is not provided' do
79
+ let(:cli_args) { ['--fake', 'value'] }
80
+
81
+ it { expect(subject).to be false }
82
+ end
83
+ end
84
+
85
+ describe '.has_format_option?' do
86
+ subject { described_class.has_format_option?(cli_args) }
87
+
88
+ context 'when format option is provided as -f' do
89
+ let(:cli_args) { ['-f', 'documentation'] }
90
+
91
+ it { expect(subject).to be true }
92
+ end
93
+
94
+ context 'when format option is provided as --format' do
95
+ let(:cli_args) { ['--format', 'documentation'] }
96
+
97
+ it { expect(subject).to be true }
98
+ end
99
+
100
+ context 'when format option is provided without delimiter' do
101
+ let(:cli_args) { ['-fd'] }
102
+
103
+ it { expect(subject).to be true }
104
+ end
105
+
106
+ context 'when format option is not provided' do
107
+ let(:cli_args) { ['--fake', 'value'] }
108
+
109
+ it { expect(subject).to be false }
110
+ end
111
+ end
112
+
15
113
  describe '.test_path' do
16
114
  let(:current_example_metadata) do
17
115
  {
@@ -1,4 +1,11 @@
1
- require 'test/unit/testcase'
1
+ # fake class to make tests pass and to avoid require 'test/unit/testcase' to not break RSpec
2
+ # https://www.rubydoc.info/gems/test-unit/3.4.1/Test/Unit/TestSuite
3
+ module Test
4
+ module Unit
5
+ class TestSuite
6
+ end
7
+ end
8
+ end
2
9
 
3
10
  describe KnapsackPro::Adapters::TestUnitAdapter do
4
11
  it do
@@ -75,9 +75,9 @@ describe KnapsackPro::Config::CI::Circle do
75
75
  describe '#project_dir' do
76
76
  subject { described_class.new.project_dir }
77
77
 
78
- context 'when environment exists' do
79
- let(:env) { { 'CIRCLE_PROJECT_REPONAME' => 'knapsack_pro-ruby' } }
80
- it { should eql '/home/ubuntu/knapsack_pro-ruby' }
78
+ context 'when CIRCLE_WORKING_DIRECTORY environment variable exists' do
79
+ let(:env) { { 'CIRCLE_WORKING_DIRECTORY' => '~/knapsack_pro-ruby' } }
80
+ it { should eql '~/knapsack_pro-ruby' }
81
81
  end
82
82
 
83
83
  context "when environment doesn't exist" do
@@ -950,4 +950,29 @@ describe KnapsackPro::Config::Env do
950
950
  it { should be_nil }
951
951
  end
952
952
  end
953
+
954
+ describe '.test_runner_adapter' do
955
+ subject { described_class.test_runner_adapter }
956
+
957
+ context 'when ENV exists' do
958
+ let(:test_runner_adapter) { 'RSpecAdapter' }
959
+ before { stub_const('ENV', { 'KNAPSACK_PRO_TEST_RUNNER_ADAPTER' => test_runner_adapter }) }
960
+ it { should eql 'RSpecAdapter' }
961
+ end
962
+
963
+ context "when ENV doesn't exist" do
964
+ it { should be_nil }
965
+ end
966
+ end
967
+
968
+ describe '.set_test_runner_adapter' do
969
+ let(:adapter_class) { KnapsackPro::Adapters::RSpecAdapter }
970
+
971
+ subject { described_class.set_test_runner_adapter(adapter_class) }
972
+
973
+ it 'sets test runner adapter' do
974
+ subject
975
+ expect(described_class.test_runner_adapter).to eql 'RSpecAdapter'
976
+ end
977
+ end
953
978
  end
@@ -15,20 +15,20 @@ describe KnapsackPro::RepositoryAdapters::GitAdapter do
15
15
 
16
16
  it { should_not be_nil }
17
17
  its(:size) { should eq 40 }
18
- it { should eq circle_sha1 } if ENV['CIRCLE_SHA1']
18
+ it { should eq circle_sha1 } if ENV['CIRCLECI']
19
19
  end
20
20
 
21
21
  describe '#branch' do
22
22
  subject { described_class.new.branch }
23
23
 
24
24
  it { should_not be_nil }
25
- it { should eq circle_branch } if ENV['CIRCLE_BRANCH']
25
+ it { should eq circle_branch } if ENV['CIRCLECI']
26
26
  end
27
27
 
28
28
  describe '#branches' do
29
29
  subject { described_class.new.branches }
30
30
 
31
31
  it { expect(subject.include?('master')).to be true }
32
- it { expect(subject.include?(circle_branch)).to be true } if ENV['CIRCLE_BRANCH']
32
+ it { expect(subject.include?(circle_branch)).to be true } if ENV['CIRCLECI']
33
33
  end
34
34
  end
@@ -18,16 +18,20 @@ describe KnapsackPro::Runners::CucumberRunner do
18
18
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_TEST_SUITE_TOKEN', test_suite_token_cucumber)
19
19
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_RECORDING_ENABLED', 'true')
20
20
 
21
+ expect(KnapsackPro::Config::Env).to receive(:set_test_runner_adapter).with(KnapsackPro::Adapters::CucumberAdapter)
22
+
21
23
  expect(described_class).to receive(:new)
22
24
  .with(KnapsackPro::Adapters::CucumberAdapter).and_return(runner)
23
25
  end
24
26
 
25
27
  context 'when test files were returned by Knapsack Pro API' do
26
28
  let(:test_dir) { 'fake-test-dir' }
27
- let(:stringify_test_file_paths) { 'features/fake1.scenario features/fake2.scenario' }
29
+ let(:test_file_paths) { ['features/fake1.scenario', 'features/fake2.scenario'] }
30
+ let(:stringify_test_file_paths) { test_file_paths.join(' ') }
28
31
  let(:runner) do
29
32
  instance_double(described_class,
30
33
  test_dir: test_dir,
34
+ test_file_paths: test_file_paths,
31
35
  stringify_test_file_paths: stringify_test_file_paths,
32
36
  test_files_to_execute_exist?: true)
33
37
  end
@@ -36,6 +40,10 @@ describe KnapsackPro::Runners::CucumberRunner do
36
40
  before do
37
41
  expect(KnapsackPro::Adapters::CucumberAdapter).to receive(:verify_bind_method_called)
38
42
 
43
+ tracker = instance_double(KnapsackPro::Tracker)
44
+ expect(KnapsackPro).to receive(:tracker).and_return(tracker)
45
+ expect(tracker).to receive(:set_prerun_tests).with(test_file_paths)
46
+
39
47
  expect(Rake::Task).to receive(:[]).with('knapsack_pro:cucumber_run').at_least(1).and_return(task)
40
48
 
41
49
  t = double
@@ -19,6 +19,8 @@ describe KnapsackPro::Runners::Queue::CucumberRunner do
19
19
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_QUEUE_RECORDING_ENABLED', 'true')
20
20
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_QUEUE_ID', queue_id)
21
21
 
22
+ expect(KnapsackPro::Config::Env).to receive(:set_test_runner_adapter).with(KnapsackPro::Adapters::CucumberAdapter)
23
+
22
24
  expect(described_class).to receive(:new).with(KnapsackPro::Adapters::CucumberAdapter).and_return(runner)
23
25
  end
24
26
 
@@ -100,6 +102,7 @@ describe KnapsackPro::Runners::Queue::CucumberRunner do
100
102
 
101
103
  context 'when test files exist' do
102
104
  let(:test_file_paths) { ['features/a.feature', 'features/b.feature'] }
105
+ let(:child_status) { double }
103
106
 
104
107
  before do
105
108
  subset_queue_id = 'fake-subset-queue-id'
@@ -117,8 +120,9 @@ describe KnapsackPro::Runners::Queue::CucumberRunner do
117
120
 
118
121
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_BEFORE_QUEUE_HOOK_CALLED', 'true')
119
122
 
120
- expect($?).to receive(:exited?).and_return(process_exited)
121
- allow($?).to receive(:exitstatus).and_return(exitstatus)
123
+ allow(described_class).to receive(:child_status).and_return(child_status)
124
+ expect(child_status).to receive(:exited?).and_return(process_exited)
125
+ allow(child_status).to receive(:exitstatus).and_return(exitstatus)
122
126
  end
123
127
 
124
128
  context 'when system process finished its work (exited)' do
@@ -19,6 +19,8 @@ describe KnapsackPro::Runners::Queue::MinitestRunner do
19
19
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_QUEUE_RECORDING_ENABLED', 'true')
20
20
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_QUEUE_ID', queue_id)
21
21
 
22
+ expect(KnapsackPro::Config::Env).to receive(:set_test_runner_adapter).with(KnapsackPro::Adapters::MinitestAdapter)
23
+
22
24
  expect(described_class).to receive(:new).with(KnapsackPro::Adapters::MinitestAdapter).and_return(runner)
23
25
 
24
26
  expect($LOAD_PATH).to receive(:unshift).with(test_dir)
@@ -26,6 +26,8 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
26
26
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_QUEUE_RECORDING_ENABLED', 'true')
27
27
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_QUEUE_ID', queue_id)
28
28
 
29
+ expect(KnapsackPro::Config::Env).to receive(:set_test_runner_adapter).with(KnapsackPro::Adapters::RSpecAdapter)
30
+
29
31
  expect(described_class).to receive(:new).with(KnapsackPro::Adapters::RSpecAdapter).and_return(runner)
30
32
  end
31
33
 
@@ -133,31 +135,16 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
133
135
  context 'when RSpec split by test examples feature is enabled' do
134
136
  before do
135
137
  expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(true)
138
+ expect(KnapsackPro::Adapters::RSpecAdapter).to receive(:ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!).and_call_original
136
139
  end
137
140
 
138
- context 'when tag option is provided as --tag' do
141
+ context 'when tag option is provided' do
139
142
  let(:args) { '--tag example-value' }
140
143
 
141
144
  it do
142
145
  expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
143
146
  end
144
147
  end
145
-
146
- context 'when tag option is provided as -t' do
147
- let(:args) { '-t example-value' }
148
-
149
- it do
150
- expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
151
- end
152
- end
153
-
154
- context 'when tag option is provided without delimiter' do
155
- let(:args) { '-texample-value' }
156
-
157
- it do
158
- expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
159
- end
160
- end
161
148
  end
162
149
  end
163
150
 
@@ -18,23 +18,32 @@ describe KnapsackPro::Runners::RSpecRunner do
18
18
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_TEST_SUITE_TOKEN', test_suite_token_rspec)
19
19
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_RECORDING_ENABLED', 'true')
20
20
 
21
+ expect(KnapsackPro::Config::Env).to receive(:set_test_runner_adapter).with(KnapsackPro::Adapters::RSpecAdapter)
22
+
21
23
  expect(described_class).to receive(:new)
22
24
  .with(KnapsackPro::Adapters::RSpecAdapter).and_return(runner)
23
25
  end
24
26
 
25
27
  context 'when test files were returned by Knapsack Pro API' do
26
28
  let(:test_dir) { 'fake-test-dir' }
27
- let(:stringify_test_file_paths) { "spec/a_spec.rb spec/b_spec.rb[1:1]" }
29
+ let(:test_file_paths) { ['spec/a_spec.rb', 'spec/b_spec.rb[1:1]'] }
30
+ let(:stringify_test_file_paths) { test_file_paths.join(' ') }
28
31
  let(:runner) do
29
32
  instance_double(described_class,
30
33
  test_dir: test_dir,
34
+ test_file_paths: test_file_paths,
31
35
  stringify_test_file_paths: stringify_test_file_paths,
32
36
  test_files_to_execute_exist?: true)
33
37
  end
34
38
  let(:task) { double }
35
39
 
36
40
  before do
37
- expect(KnapsackPro::Adapters::RSpecAdapter).to receive(:verify_bind_method_called)
41
+ expect(KnapsackPro::Adapters::RSpecAdapter).to receive(:verify_bind_method_called).ordered
42
+ expect(KnapsackPro::Adapters::RSpecAdapter).to receive(:ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!).with(['--profile', '--color']).ordered
43
+
44
+ tracker = instance_double(KnapsackPro::Tracker)
45
+ expect(KnapsackPro).to receive(:tracker).and_return(tracker)
46
+ expect(tracker).to receive(:set_prerun_tests).with(test_file_paths)
38
47
 
39
48
  expect(Rake::Task).to receive(:[]).with('knapsack_pro:rspec_run').at_least(1).and_return(task)
40
49
 
@@ -44,7 +53,7 @@ describe KnapsackPro::Runners::RSpecRunner do
44
53
  expect(t).to receive(:pattern=).with([])
45
54
  end
46
55
 
47
- context 'when task already exists' do
56
+ context 'when rake task already exists' do
48
57
  before do
49
58
  expect(Rake::Task).to receive(:task_defined?).with('knapsack_pro:rspec_run').and_return(true)
50
59
  expect(task).to receive(:clear)
@@ -57,7 +66,7 @@ describe KnapsackPro::Runners::RSpecRunner do
57
66
  end
58
67
  end
59
68
 
60
- context "when task doesn't exist" do
69
+ context "when rake task doesn't exist" do
61
70
  before do
62
71
  expect(Rake::Task).to receive(:task_defined?).with('knapsack_pro:rspec_run').and_return(false)
63
72
  expect(task).not_to receive(:clear)
@@ -11,24 +11,35 @@ describe KnapsackPro::Runners::SpinachRunner do
11
11
  before do
12
12
  stub_const("ENV", { 'KNAPSACK_PRO_TEST_SUITE_TOKEN_SPINACH' => 'spinach-token' })
13
13
 
14
+ expect(KnapsackPro::Config::Env).to receive(:set_test_runner_adapter).with(KnapsackPro::Adapters::SpinachAdapter)
15
+
14
16
  expect(described_class).to receive(:new)
15
17
  .with(KnapsackPro::Adapters::SpinachAdapter).and_return(runner)
16
18
  end
17
19
 
18
20
  context 'when test files were returned by Knapsack Pro API' do
19
- let(:stringify_test_file_paths) { 'features/a.feature features/b.feature' }
21
+ let(:test_file_paths) { ['features/a.feature', 'features/b.feature'] }
22
+ let(:stringify_test_file_paths) { test_file_paths.join(' ') }
20
23
  let(:test_dir) { 'fake-test-dir' }
21
24
  let(:runner) do
22
25
  instance_double(described_class,
23
26
  test_dir: test_dir,
27
+ test_file_paths: test_file_paths,
24
28
  stringify_test_file_paths: stringify_test_file_paths,
25
29
  test_files_to_execute_exist?: true)
26
30
  end
31
+ let(:child_status) { double }
27
32
 
28
33
  before do
29
34
  expect(KnapsackPro::Adapters::SpinachAdapter).to receive(:verify_bind_method_called)
30
35
 
36
+ tracker = instance_double(KnapsackPro::Tracker)
37
+ expect(KnapsackPro).to receive(:tracker).and_return(tracker)
38
+ expect(tracker).to receive(:set_prerun_tests).with(test_file_paths)
39
+
31
40
  expect(Kernel).to receive(:system).with('KNAPSACK_PRO_RECORDING_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=spinach-token bundle exec spinach --custom-arg --features_path fake-test-dir -- features/a.feature features/b.feature')
41
+
42
+ allow(described_class).to receive(:child_status).and_return(child_status)
32
43
  end
33
44
 
34
45
  after { subject }
@@ -37,7 +48,7 @@ describe KnapsackPro::Runners::SpinachRunner do
37
48
  let(:exitstatus) { 0 }
38
49
 
39
50
  before do
40
- expect($?).to receive(:exitstatus).and_return(exitstatus)
51
+ expect(child_status).to receive(:exitstatus).and_return(exitstatus)
41
52
  end
42
53
 
43
54
  it do
@@ -49,7 +60,7 @@ describe KnapsackPro::Runners::SpinachRunner do
49
60
  let(:exitstatus) { 1 }
50
61
 
51
62
  before do
52
- expect($?).to receive(:exitstatus).twice.and_return(exitstatus)
63
+ expect(child_status).to receive(:exitstatus).twice.and_return(exitstatus)
53
64
  end
54
65
 
55
66
  it do
@@ -1,5 +1,3 @@
1
- require 'test/unit'
2
-
3
1
  describe KnapsackPro::Runners::TestUnitRunner do
4
2
  subject { described_class.new(KnapsackPro::Adapters::TestUnitAdapter) }
5
3
 
@@ -27,7 +25,7 @@ describe KnapsackPro::Runners::TestUnitRunner do
27
25
  .with(KnapsackPro::Adapters::TestUnitAdapter).and_return(runner)
28
26
 
29
27
  auto_runner_exit_code = 0
30
- expect(Test::Unit::AutoRunner).to receive(:run) do |flag, test_dir, cli_args|
28
+ expect(described_class).to receive(:test_unit_autorunner_run) do |flag, test_dir, cli_args|
31
29
  expect(flag).to be true
32
30
  expect(test_dir).to eq 'test-unit_fake'
33
31
  expect(cli_args.size).to eq 4
@@ -1,11 +1,18 @@
1
1
  shared_examples 'default trakcer attributes' do
2
2
  it { expect(tracker.global_time).to eql 0 }
3
3
  it { expect(tracker.test_files_with_time).to eql({}) }
4
+ it { expect(tracker.prerun_tests_loaded).to be false }
4
5
  end
5
6
 
6
7
  describe KnapsackPro::Tracker do
8
+ let(:adapter) { 'RSpecAdapter' }
7
9
  let(:tracker) { described_class.send(:new) }
8
10
 
11
+ before do
12
+ allow(KnapsackPro::Config::Env).to receive(:test_runner_adapter).and_return(adapter)
13
+ allow(KnapsackPro::Config::Env).to receive(:ci_node_index).and_return(0)
14
+ end
15
+
9
16
  it_behaves_like 'default trakcer attributes'
10
17
 
11
18
  describe '#current_test_path' do
@@ -34,6 +41,10 @@ describe KnapsackPro::Tracker do
34
41
  let(:test_paths) { ['a_spec.rb', 'b_spec.rb'] }
35
42
  let(:delta) { 0.02 }
36
43
 
44
+ before do
45
+ tracker.set_prerun_tests(test_paths)
46
+ end
47
+
37
48
  shared_examples '#to_a' do
38
49
  subject { tracker.to_a }
39
50
 
@@ -55,13 +66,14 @@ describe KnapsackPro::Tracker do
55
66
  end
56
67
 
57
68
  it { expect(tracker.global_time).to be_within(delta).of(0.3) }
69
+ it { expect(tracker.prerun_tests_loaded).to be true }
58
70
  it { expect(tracker.test_files_with_time.keys.size).to eql 2 }
59
71
  it { expect(tracker.test_files_with_time['a_spec.rb'][:time_execution]).to be_within(delta).of(0.1) }
60
72
  it { expect(tracker.test_files_with_time['b_spec.rb'][:time_execution]).to be_within(delta).of(0.2) }
61
73
  it_behaves_like '#to_a'
62
74
  end
63
75
 
64
- context "with Timecop - Timecop shouldn't have impact on measured test time" do
76
+ context "with Timecop - Timecop shouldn't have impact on the measured test time" do
65
77
  let(:now) { Time.now }
66
78
 
67
79
  before do
@@ -80,6 +92,7 @@ describe KnapsackPro::Tracker do
80
92
 
81
93
  it { expect(tracker.global_time).to be > 0 }
82
94
  it { expect(tracker.global_time).to be_within(delta).of(0) }
95
+ it { expect(tracker.prerun_tests_loaded).to be true }
83
96
  it { expect(tracker.test_files_with_time.keys.size).to eql 2 }
84
97
  it { expect(tracker.test_files_with_time['a_spec.rb'][:time_execution]).to be_within(delta).of(0) }
85
98
  it { expect(tracker.test_files_with_time['b_spec.rb'][:time_execution]).to be_within(delta).of(0) }
@@ -96,6 +109,7 @@ describe KnapsackPro::Tracker do
96
109
  end
97
110
 
98
111
  it { expect(tracker.global_time).to be > 0 }
112
+ it { expect(tracker.prerun_tests_loaded).to be true }
99
113
  it { expect(tracker.test_files_with_time.keys.size).to eql 2 }
100
114
  it { expect(tracker.test_files_with_time['a_spec.rb'][:time_execution]).to eq 0 }
101
115
  it '2nd spec (b_spec.rb) should have recorded time execution - because start_time was set during first call of stop_timer for the first spec (a_spec.rb)' do
@@ -103,15 +117,62 @@ describe KnapsackPro::Tracker do
103
117
  end
104
118
  it_behaves_like '#to_a'
105
119
  end
120
+
121
+ context 'when a new tracker instance is created' do
122
+ let(:non_pending_test_paths) { ['a_spec.rb', 'b_spec.rb'] }
123
+ let(:test_paths) { non_pending_test_paths + ['pending_spec.rb'] }
124
+
125
+ before do
126
+ # measure tests only for non pending tests
127
+ non_pending_test_paths.each_with_index do |test_path, index|
128
+ tracker.current_test_path = test_path
129
+ tracker.start_timer
130
+ sleep index.to_f / 10 + 0.1
131
+ tracker.stop_timer
132
+ end
133
+ end
134
+
135
+ it '2nd tracker instance loads prerun tests from the disk' do
136
+ expect(tracker.prerun_tests_loaded).to be true
137
+ expect(tracker.to_a.size).to eq 3
138
+ expect(tracker.to_a[0][:path]).to eq 'a_spec.rb'
139
+ expect(tracker.to_a[0][:time_execution]).to be >= 0
140
+ expect(tracker.to_a[1][:path]).to eq 'b_spec.rb'
141
+ expect(tracker.to_a[1][:time_execution]).to be >= 0
142
+ expect(tracker.to_a[2][:path]).to eq 'pending_spec.rb'
143
+ expect(tracker.to_a[2][:time_execution]).to eq 0
144
+
145
+ tracker2 = described_class.send(:new)
146
+ expect(tracker2.prerun_tests_loaded).to be false
147
+ expect(tracker2.to_a.size).to eq 3
148
+ expect(tracker2.to_a[0][:path]).to eq 'a_spec.rb'
149
+ expect(tracker2.to_a[0][:time_execution]).to be >= 0
150
+ expect(tracker2.to_a[1][:path]).to eq 'b_spec.rb'
151
+ expect(tracker2.to_a[1][:time_execution]).to be >= 0
152
+ expect(tracker2.to_a[2][:path]).to eq 'pending_spec.rb'
153
+ expect(tracker2.to_a[2][:time_execution]).to eq 0
154
+ expect(tracker2.prerun_tests_loaded).to be true
155
+ end
156
+ end
106
157
  end
107
158
 
108
159
  describe '#reset!' do
160
+ let(:test_file_path) { 'a_spec.rb' }
161
+
109
162
  before do
110
- tracker.current_test_path = 'a_spec.rb'
163
+ tracker.set_prerun_tests([test_file_path])
164
+ end
165
+
166
+ before do
167
+ expect(tracker.prerun_tests_loaded).to be true
168
+
169
+ tracker.current_test_path = test_file_path
111
170
  tracker.start_timer
112
171
  sleep 0.1
113
172
  tracker.stop_timer
173
+
114
174
  expect(tracker.global_time).not_to eql 0
175
+
115
176
  tracker.reset!
116
177
  end
117
178
 
@@ -120,5 +181,9 @@ describe KnapsackPro::Tracker do
120
181
  it "global time since beginning won't be reset" do
121
182
  expect(tracker.global_time_since_beginning).to be >= 0.1
122
183
  end
184
+
185
+ it 'resets prerun_tests_loaded to false' do
186
+ expect(tracker.prerun_tests_loaded).to be false
187
+ end
123
188
  end
124
189
  end
data/spec/spec_helper.rb CHANGED
@@ -4,9 +4,6 @@ require 'spinach'
4
4
  require 'timecop'
5
5
  Timecop.safe_mode = true
6
6
 
7
- require 'codeclimate-test-reporter'
8
- CodeClimate::TestReporter.start
9
-
10
7
  require 'vcr'
11
8
  require 'webmock/rspec'
12
9
  VCR.configure do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knapsack_pro
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.16.0
4
+ version: 2.18.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ArturT
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-13 00:00:00.000000000 Z
11
+ date: 2021-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -42,36 +42,36 @@ dependencies:
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 2.10.0
48
45
  - - "~>"
49
46
  - !ruby/object:Gem::Version
50
47
  version: '3.0'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 2.10.0
51
51
  type: :development
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: 2.10.0
58
55
  - - "~>"
59
56
  - !ruby/object:Gem::Version
60
57
  version: '3.0'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.10.0
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: rspec-its
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '1.2'
67
+ version: '1.3'
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '1.2'
74
+ version: '1.3'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: cucumber
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -128,20 +128,6 @@ dependencies:
128
128
  - - ">="
129
129
  - !ruby/object:Gem::Version
130
130
  version: 3.0.0
131
- - !ruby/object:Gem::Dependency
132
- name: codeclimate-test-reporter
133
- requirement: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - "~>"
136
- - !ruby/object:Gem::Version
137
- version: '0'
138
- type: :development
139
- prerelease: false
140
- version_requirements: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - "~>"
143
- - !ruby/object:Gem::Version
144
- version: '0'
145
131
  - !ruby/object:Gem::Dependency
146
132
  name: pry
147
133
  requirement: !ruby/object:Gem::Requirement
@@ -160,44 +146,44 @@ dependencies:
160
146
  name: vcr
161
147
  requirement: !ruby/object:Gem::Requirement
162
148
  requirements:
163
- - - "~>"
149
+ - - ">="
164
150
  - !ruby/object:Gem::Version
165
- version: '2.9'
151
+ version: '6.0'
166
152
  type: :development
167
153
  prerelease: false
168
154
  version_requirements: !ruby/object:Gem::Requirement
169
155
  requirements:
170
- - - "~>"
156
+ - - ">="
171
157
  - !ruby/object:Gem::Version
172
- version: '2.9'
158
+ version: '6.0'
173
159
  - !ruby/object:Gem::Dependency
174
160
  name: webmock
175
161
  requirement: !ruby/object:Gem::Requirement
176
162
  requirements:
177
- - - "~>"
163
+ - - ">="
178
164
  - !ruby/object:Gem::Version
179
- version: '1.21'
165
+ version: '3.13'
180
166
  type: :development
181
167
  prerelease: false
182
168
  version_requirements: !ruby/object:Gem::Requirement
183
169
  requirements:
184
- - - "~>"
170
+ - - ">="
185
171
  - !ruby/object:Gem::Version
186
- version: '1.21'
172
+ version: '3.13'
187
173
  - !ruby/object:Gem::Dependency
188
174
  name: timecop
189
175
  requirement: !ruby/object:Gem::Requirement
190
176
  requirements:
191
177
  - - ">="
192
178
  - !ruby/object:Gem::Version
193
- version: 0.1.0
179
+ version: 0.9.4
194
180
  type: :development
195
181
  prerelease: false
196
182
  version_requirements: !ruby/object:Gem::Requirement
197
183
  requirements:
198
184
  - - ">="
199
185
  - !ruby/object:Gem::Version
200
- version: 0.1.0
186
+ version: 0.9.4
201
187
  description: Run tests in parallel across CI server nodes based on tests execution
202
188
  time. Split tests in a dynamic way to ensure parallel jobs are done at a similar
203
189
  time. Thanks to that your CI build time is as fast as possible. It works with many
@@ -423,7 +409,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
423
409
  - !ruby/object:Gem::Version
424
410
  version: '0'
425
411
  requirements: []
426
- rubygems_version: 3.0.6
412
+ rubygems_version: 3.2.15
427
413
  signing_key:
428
414
  specification_version: 4
429
415
  summary: Knapsack Pro splits tests across parallel CI nodes and ensures each parallel