knapsack_pro 3.8.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +377 -23
  3. data/.github/dependabot.yml +11 -0
  4. data/.github/pull_request_template.md +22 -0
  5. data/.gitignore +4 -0
  6. data/CHANGELOG.md +325 -1
  7. data/Gemfile +9 -0
  8. data/README.md +3 -10
  9. data/bin/test +15 -0
  10. data/knapsack_pro.gemspec +7 -6
  11. data/lib/knapsack_pro/adapters/base_adapter.rb +17 -2
  12. data/lib/knapsack_pro/adapters/cucumber_adapter.rb +3 -3
  13. data/lib/knapsack_pro/adapters/minitest_adapter.rb +2 -0
  14. data/lib/knapsack_pro/adapters/rspec_adapter.rb +88 -49
  15. data/lib/knapsack_pro/adapters/spinach_adapter.rb +2 -0
  16. data/lib/knapsack_pro/adapters/test_unit_adapter.rb +2 -0
  17. data/lib/knapsack_pro/allocator.rb +2 -0
  18. data/lib/knapsack_pro/allocator_builder.rb +2 -0
  19. data/lib/knapsack_pro/base_allocator_builder.rb +8 -25
  20. data/lib/knapsack_pro/build_distribution_fetcher.rb +2 -0
  21. data/lib/knapsack_pro/client/api/action.rb +2 -0
  22. data/lib/knapsack_pro/client/api/v1/base.rb +2 -0
  23. data/lib/knapsack_pro/client/api/v1/build_distributions.rb +5 -0
  24. data/lib/knapsack_pro/client/api/v1/build_subsets.rb +2 -0
  25. data/lib/knapsack_pro/client/api/v1/queues.rb +6 -1
  26. data/lib/knapsack_pro/client/connection.rb +5 -6
  27. data/lib/knapsack_pro/config/ci/app_veyor.rb +18 -0
  28. data/lib/knapsack_pro/config/ci/base.rb +27 -0
  29. data/lib/knapsack_pro/config/ci/buildkite.rb +18 -0
  30. data/lib/knapsack_pro/config/ci/circle.rb +18 -0
  31. data/lib/knapsack_pro/config/ci/cirrus_ci.rb +18 -0
  32. data/lib/knapsack_pro/config/ci/codefresh.rb +18 -0
  33. data/lib/knapsack_pro/config/ci/codeship.rb +18 -0
  34. data/lib/knapsack_pro/config/ci/github_actions.rb +26 -0
  35. data/lib/knapsack_pro/config/ci/gitlab_ci.rb +20 -1
  36. data/lib/knapsack_pro/config/ci/heroku.rb +18 -0
  37. data/lib/knapsack_pro/config/ci/semaphore.rb +16 -0
  38. data/lib/knapsack_pro/config/ci/semaphore2.rb +19 -0
  39. data/lib/knapsack_pro/config/ci/travis.rb +18 -0
  40. data/lib/knapsack_pro/config/env.rb +46 -22
  41. data/lib/knapsack_pro/config/env_generator.rb +2 -0
  42. data/lib/knapsack_pro/config/temp_files.rb +8 -4
  43. data/lib/knapsack_pro/crypto/branch_encryptor.rb +2 -0
  44. data/lib/knapsack_pro/crypto/decryptor.rb +2 -0
  45. data/lib/knapsack_pro/crypto/digestor.rb +2 -0
  46. data/lib/knapsack_pro/crypto/encryptor.rb +2 -0
  47. data/lib/knapsack_pro/extensions/rspec_extension.rb +137 -0
  48. data/lib/knapsack_pro/formatters/rspec_json_formatter.rb +2 -0
  49. data/lib/knapsack_pro/formatters/time_tracker.rb +152 -0
  50. data/lib/knapsack_pro/formatters/time_tracker_fetcher.rb +20 -0
  51. data/lib/knapsack_pro/hooks/queue.rb +2 -0
  52. data/lib/knapsack_pro/logger_wrapper.rb +2 -0
  53. data/lib/knapsack_pro/mask_string.rb +9 -0
  54. data/lib/knapsack_pro/presenter.rb +6 -3
  55. data/lib/knapsack_pro/pure/queue/rspec_pure.rb +92 -0
  56. data/lib/knapsack_pro/queue_allocator.rb +2 -0
  57. data/lib/knapsack_pro/queue_allocator_builder.rb +2 -0
  58. data/lib/knapsack_pro/railtie.rb +2 -0
  59. data/lib/knapsack_pro/report.rb +15 -9
  60. data/lib/knapsack_pro/repository_adapter_initiator.rb +2 -0
  61. data/lib/knapsack_pro/repository_adapters/base_adapter.rb +2 -0
  62. data/lib/knapsack_pro/repository_adapters/env_adapter.rb +2 -0
  63. data/lib/knapsack_pro/repository_adapters/git_adapter.rb +50 -0
  64. data/lib/knapsack_pro/runners/base_runner.rb +2 -0
  65. data/lib/knapsack_pro/runners/cucumber_runner.rb +2 -0
  66. data/lib/knapsack_pro/runners/minitest_runner.rb +2 -0
  67. data/lib/knapsack_pro/runners/queue/base_runner.rb +29 -0
  68. data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +9 -6
  69. data/lib/knapsack_pro/runners/queue/minitest_runner.rb +13 -6
  70. data/lib/knapsack_pro/runners/queue/rspec_runner.rb +128 -135
  71. data/lib/knapsack_pro/runners/rspec_runner.rb +22 -3
  72. data/lib/knapsack_pro/runners/spinach_runner.rb +2 -0
  73. data/lib/knapsack_pro/runners/test_unit_runner.rb +2 -0
  74. data/lib/knapsack_pro/slow_test_file_determiner.rb +2 -0
  75. data/lib/knapsack_pro/slow_test_file_finder.rb +2 -0
  76. data/lib/knapsack_pro/task_loader.rb +2 -0
  77. data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +2 -0
  78. data/lib/knapsack_pro/test_case_mergers/base_merger.rb +2 -0
  79. data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +2 -0
  80. data/lib/knapsack_pro/test_file_cleaner.rb +2 -0
  81. data/lib/knapsack_pro/test_file_finder.rb +2 -0
  82. data/lib/knapsack_pro/test_file_pattern.rb +2 -0
  83. data/lib/knapsack_pro/test_file_presenter.rb +2 -0
  84. data/lib/knapsack_pro/test_files_with_test_cases_composer.rb +2 -0
  85. data/lib/knapsack_pro/test_flat_distributor.rb +2 -0
  86. data/lib/knapsack_pro/tracker.rb +3 -3
  87. data/lib/knapsack_pro/urls.rb +4 -0
  88. data/lib/knapsack_pro/utils.rb +2 -0
  89. data/lib/knapsack_pro/version.rb +3 -1
  90. data/lib/knapsack_pro.rb +5 -3
  91. data/lib/tasks/cucumber.rake +2 -0
  92. data/lib/tasks/encrypted_branch_names.rake +2 -0
  93. data/lib/tasks/encrypted_test_file_names.rake +2 -0
  94. data/lib/tasks/minitest.rake +2 -0
  95. data/lib/tasks/queue/cucumber.rake +13 -0
  96. data/lib/tasks/queue/minitest.rake +13 -0
  97. data/lib/tasks/queue/rspec.rake +13 -0
  98. data/lib/tasks/rspec.rake +5 -0
  99. data/lib/tasks/salt.rake +2 -0
  100. data/lib/tasks/spinach.rake +2 -0
  101. data/lib/tasks/test_unit.rake +2 -0
  102. data/spec/integration/api/build_distributions_subset_spec.rb +1 -0
  103. data/spec/integration/runners/queue/rspec_runner.rb +80 -0
  104. data/spec/integration/runners/queue/rspec_runner_spec.rb +2232 -0
  105. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +30 -11
  106. data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +2 -5
  107. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +146 -174
  108. data/spec/knapsack_pro/base_allocator_builder_spec.rb +22 -48
  109. data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +19 -27
  110. data/spec/knapsack_pro/client/api/v1/queues_spec.rb +23 -43
  111. data/spec/knapsack_pro/client/connection_spec.rb +59 -7
  112. data/spec/knapsack_pro/config/ci/app_veyor_spec.rb +22 -8
  113. data/spec/knapsack_pro/config/ci/base_spec.rb +1 -0
  114. data/spec/knapsack_pro/config/ci/buildkite_spec.rb +51 -16
  115. data/spec/knapsack_pro/config/ci/circle_spec.rb +48 -13
  116. data/spec/knapsack_pro/config/ci/cirrus_ci_spec.rb +12 -12
  117. data/spec/knapsack_pro/config/ci/codefresh_spec.rb +21 -6
  118. data/spec/knapsack_pro/config/ci/codeship_spec.rb +20 -6
  119. data/spec/knapsack_pro/config/ci/github_actions_spec.rb +37 -10
  120. data/spec/knapsack_pro/config/ci/gitlab_ci_spec.rb +48 -13
  121. data/spec/knapsack_pro/config/ci/heroku_spec.rb +12 -12
  122. data/spec/knapsack_pro/config/ci/semaphore2_spec.rb +11 -11
  123. data/spec/knapsack_pro/config/ci/semaphore_spec.rb +12 -12
  124. data/spec/knapsack_pro/config/ci/travis_spec.rb +8 -8
  125. data/spec/knapsack_pro/config/env_spec.rb +204 -124
  126. data/spec/knapsack_pro/formatters/time_tracker_specs.rb +424 -0
  127. data/spec/knapsack_pro/hooks/queue_spec.rb +2 -2
  128. data/spec/knapsack_pro/presenter_spec.rb +1 -1
  129. data/spec/knapsack_pro/pure/queue/rspec_pure_spec.rb +224 -0
  130. data/spec/knapsack_pro/repository_adapters/git_adapter_spec.rb +72 -0
  131. data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +18 -16
  132. data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +17 -14
  133. data/spec/knapsack_pro/runners/rspec_runner_spec.rb +40 -23
  134. data/spec/knapsack_pro/test_case_detectors/rspec_test_example_detector_spec.rb +1 -0
  135. data/spec/knapsack_pro/tracker_spec.rb +0 -4
  136. data/spec/knapsack_pro_spec.rb +3 -3
  137. data/spec/spec_helper.rb +0 -1
  138. metadata +26 -23
  139. data/lib/knapsack_pro/config/ci/snap_ci.rb +0 -35
  140. data/lib/knapsack_pro/config/ci/solano_ci.rb +0 -32
  141. data/lib/knapsack_pro/extensions/time.rb +0 -7
  142. data/lib/knapsack_pro/formatters/rspec_queue_profile_formatter_extension.rb +0 -56
  143. data/lib/knapsack_pro/formatters/rspec_queue_summary_formatter.rb +0 -112
  144. data/spec/knapsack_pro/config/ci/snap_ci_spec.rb +0 -104
  145. data/spec/knapsack_pro/config/ci/solano_ci_spec.rb +0 -73
  146. data/spec/knapsack_pro/extensions/time_spec.rb +0 -5
  147. data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +0 -342
@@ -1,184 +1,177 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  module Runners
3
5
  module Queue
4
6
  class RSpecRunner < BaseRunner
5
- @@used_seed = nil
6
-
7
- def self.run(args)
7
+ def self.run(args, stream_error = $stderr, stream_out = $stdout)
8
8
  require 'rspec/core'
9
- require_relative '../../formatters/rspec_queue_summary_formatter'
10
- require_relative '../../formatters/rspec_queue_profile_formatter_extension'
9
+ require_relative '../../extensions/rspec_extension'
10
+ require_relative '../../formatters/time_tracker'
11
+ require_relative '../../formatters/time_tracker_fetcher'
12
+
13
+ KnapsackPro::Extensions::RSpecExtension.setup!
11
14
 
12
15
  ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_rspec
13
- ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
14
- ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
15
16
 
16
- KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
17
- runner = new(adapter_class)
18
-
19
- cli_args = (args || '').split
20
- adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
21
-
22
- # when format option is not defined by user then use progress formatter to show tests execution progress
23
- cli_args += ['--format', 'progress'] unless adapter_class.has_format_option?(cli_args)
24
-
25
- cli_args += [
26
- # shows summary of all tests executed in Queue Mode at the very end
27
- '--format', KnapsackPro::Formatters::RSpecQueueSummaryFormatter.to_s,
28
- '--default-path', runner.test_dir,
29
- ]
30
-
31
- accumulator = {
32
- status: :next,
33
- runner: runner,
34
- can_initialize_queue: true,
35
- args: cli_args,
36
- exitstatus: 0,
37
- all_test_file_paths: [],
38
- }
39
- while accumulator[:status] == :next
40
- accumulator = run_tests(accumulator)
41
- end
17
+ rspec_pure = KnapsackPro::Pure::Queue::RSpecPure.new
42
18
 
43
- Kernel.exit(accumulator[:exitstatus])
19
+ queue_runner = new(KnapsackPro::Adapters::RSpecAdapter, rspec_pure, args, stream_error, stream_out)
20
+ queue_runner.run
44
21
  end
45
22
 
46
- def self.run_tests(accumulator)
47
- runner = accumulator.fetch(:runner)
48
- can_initialize_queue = accumulator.fetch(:can_initialize_queue)
49
- args = accumulator.fetch(:args)
50
- exitstatus = accumulator.fetch(:exitstatus)
51
- all_test_file_paths = accumulator.fetch(:all_test_file_paths)
23
+ def initialize(adapter_class, rspec_pure, args, stream_error, stream_out)
24
+ super(adapter_class)
25
+ @adapter_class = adapter_class
26
+ @rspec_pure = rspec_pure
27
+ has_format_option = @adapter_class.has_format_option?((args || '').split)
28
+ @cli_args = rspec_pure.prepare_cli_args(args, has_format_option, test_dir)
29
+ @stream_error = stream_error
30
+ @stream_out = stream_out
31
+ @node_test_file_paths = []
32
+ @rspec_runner = nil # RSpec::Core::Runner is lazy initialized
33
+ end
52
34
 
53
- test_file_paths = runner.test_file_paths(
54
- can_initialize_queue: can_initialize_queue,
55
- executed_test_files: all_test_file_paths
56
- )
35
+ # Based on:
36
+ # https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/runner.rb#L85
37
+ #
38
+ # @return [Fixnum] exit status code.
39
+ # 0 if all specs passed,
40
+ # or the configured failure exit code (1 by default) if specs failed.
41
+ def run
42
+ pre_run_setup
43
+
44
+ if @rspec_runner.knapsack__wants_to_quit?
45
+ exit_code = @rspec_runner.knapsack__exit_early
46
+ Kernel.exit(exit_code)
47
+ end
48
+
49
+ begin
50
+ exit_code = @rspec_runner.knapsack__run_specs(self)
51
+ rescue KnapsackPro::Runners::Queue::BaseRunner::TerminationError
52
+ exit_code = @rspec_pure.error_exit_code(@rspec_runner.knapsack__error_exit_code)
53
+ Kernel.exit(exit_code)
54
+ rescue Exception => exception
55
+ KnapsackPro.logger.error("An unexpected exception happened. RSpec cannot handle it. The exception: #{exception.inspect}")
57
56
 
58
- if test_file_paths.empty?
59
- unless all_test_file_paths.empty?
60
- KnapsackPro::Adapters::RSpecAdapter.verify_bind_method_called
57
+ message = @rspec_pure.exit_summary(unexecuted_test_files)
58
+ KnapsackPro.logger.warn(message) if message
61
59
 
62
- KnapsackPro::Formatters::RSpecQueueSummaryFormatter.print_summary
63
- KnapsackPro::Formatters::RSpecQueueProfileFormatterExtension.print_summary
60
+ exit_code = @rspec_pure.error_exit_code(@rspec_runner.knapsack__error_exit_code)
61
+ Kernel.exit(exit_code)
62
+ end
64
63
 
65
- args += ['--seed', @@used_seed] if @@used_seed
64
+ post_run_tasks(exit_code)
65
+ end
66
66
 
67
- log_rspec_command(args, all_test_file_paths, :end_of_queue)
68
- end
67
+ def with_batch
68
+ can_initialize_queue = true
69
69
 
70
- KnapsackPro::Hooks::Queue.call_after_queue
70
+ loop do
71
+ handle_signal!
72
+ test_file_paths = pull_tests_from_queue(can_initialize_queue: can_initialize_queue)
73
+ can_initialize_queue = false
71
74
 
72
- KnapsackPro::Report.save_node_queue_to_api
75
+ break if test_file_paths.empty?
73
76
 
74
- return {
75
- status: :completed,
76
- exitstatus: exitstatus,
77
- }
78
- else
79
77
  subset_queue_id = KnapsackPro::Config::EnvGenerator.set_subset_queue_id
80
78
  ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID'] = subset_queue_id
81
79
 
82
- KnapsackPro.tracker.reset!
83
- KnapsackPro.tracker.set_prerun_tests(test_file_paths)
84
-
85
80
  KnapsackPro::Hooks::Queue.call_before_subset_queue
86
81
 
87
- all_test_file_paths += test_file_paths
88
- cli_args = args + test_file_paths
89
-
90
- options = ::RSpec::Core::ConfigurationOptions.new(cli_args)
91
- rspec_runner = ::RSpec::Core::Runner.new(options)
92
-
93
- exit_code = rspec_runner.run($stderr, $stdout)
94
- exitstatus = exit_code if exit_code != 0
95
-
96
- printable_args = args_with_seed_option_added_when_viable(args, rspec_runner)
97
- log_rspec_command(printable_args, test_file_paths, :subset_queue)
98
-
99
- rspec_clear_examples
82
+ yield test_file_paths
100
83
 
101
84
  KnapsackPro::Hooks::Queue.call_after_subset_queue
102
85
 
103
- KnapsackPro::Report.save_subset_queue_to_file
86
+ if @rspec_runner.knapsack__wants_to_quit?
87
+ KnapsackPro.logger.warn('RSpec wants to quit.')
88
+ set_terminate_process
89
+ end
90
+ if @rspec_runner.knapsack__rspec_is_quitting?
91
+ KnapsackPro.logger.warn('RSpec is quitting.')
92
+ set_terminate_process
93
+ end
104
94
 
105
- return {
106
- status: :next,
107
- runner: runner,
108
- can_initialize_queue: false,
109
- args: args,
110
- exitstatus: exitstatus,
111
- all_test_file_paths: all_test_file_paths,
112
- }
95
+ log_rspec_batch_command(test_file_paths)
113
96
  end
114
97
  end
115
98
 
99
+ def handle_signal!
100
+ self.class.handle_signal!
101
+ end
102
+
103
+ def log_fail_fast_limit_met
104
+ KnapsackPro.logger.warn('Test execution has been canceled because the RSpec --fail-fast option is enabled. It will cause other CI nodes to run tests longer because they need to consume more tests from the Knapsack Pro Queue API.')
105
+ end
106
+
116
107
  private
117
108
 
118
- def self.adapter_class
119
- KnapsackPro::Adapters::RSpecAdapter
109
+ def pre_run_setup
110
+ ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
111
+ ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
112
+
113
+ KnapsackPro::Config::Env.set_test_runner_adapter(@adapter_class)
114
+
115
+ ENV['SPEC_OPTS'] = @rspec_pure.add_knapsack_pro_formatters_to(ENV['SPEC_OPTS'])
116
+ @adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(@cli_args)
117
+
118
+ rspec_configuration_options = ::RSpec::Core::ConfigurationOptions.new(@cli_args)
119
+ @rspec_runner = ::RSpec::Core::Runner.new(rspec_configuration_options)
120
+ @rspec_runner.knapsack__setup(@stream_error, @stream_out)
121
+
122
+ ensure_no_deprecated_run_all_when_everything_filtered_option!
120
123
  end
121
124
 
122
- def self.log_rspec_command(cli_args, test_file_paths, type)
123
- case type
124
- when :subset_queue
125
- KnapsackPro.logger.info("To retry the last batch of tests fetched from the API Queue, please run the following command on your machine:")
126
- when :end_of_queue
127
- KnapsackPro.logger.info("To retry all the tests assigned to this CI node, please run the following command on your machine:")
128
- end
125
+ def post_run_tasks(exit_code)
126
+ @adapter_class.verify_bind_method_called
129
127
 
130
- stringified_cli_args = cli_args.join(' ').sub(" --format #{KnapsackPro::Formatters::RSpecQueueSummaryFormatter}", '')
128
+ log_rspec_queue_command
131
129
 
132
- KnapsackPro.logger.info(
133
- "bundle exec rspec #{stringified_cli_args} " +
134
- KnapsackPro::TestFilePresenter.stringify_paths(test_file_paths)
135
- )
130
+ time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
131
+ KnapsackPro::Report.save_node_queue_to_api(time_tracker&.queue(@node_test_file_paths))
132
+
133
+ Kernel.exit(exit_code)
136
134
  end
137
135
 
138
- # Clear rspec examples without the shared examples:
139
- # https://github.com/rspec/rspec-core/pull/2379
140
- #
141
- # Keep formatters and report to accumulate info about failed/pending tests
142
- def self.rspec_clear_examples
143
- if ::RSpec::ExampleGroups.respond_to?(:remove_all_constants)
144
- ::RSpec::ExampleGroups.remove_all_constants
145
- else
146
- ::RSpec::ExampleGroups.constants.each do |constant|
147
- ::RSpec::ExampleGroups.__send__(:remove_const, constant)
148
- end
149
- end
150
- ::RSpec.world.example_groups.clear
151
- ::RSpec.configuration.start_time = ::RSpec::Core::Time.now
152
-
153
- if KnapsackPro::Config::Env.rspec_split_by_test_examples?
154
- # Reset example group counts to ensure scoped example ids in metadata
155
- # have correct index (not increased by each subsequent run).
156
- # Solves this problem: https://github.com/rspec/rspec-core/issues/2721
157
- ::RSpec.world.instance_variable_set(:@example_group_counts_by_spec_file, Hash.new(0))
158
- end
136
+ def ensure_no_deprecated_run_all_when_everything_filtered_option!
137
+ return unless @rspec_runner.knapsack__deprecated_run_all_when_everything_filtered_enabled?
159
138
 
160
- # skip reset filters for old RSpec versions
161
- if ::RSpec.configuration.respond_to?(:reset_filters)
162
- ::RSpec.configuration.reset_filters
163
- end
139
+ error_message = "The run_all_when_everything_filtered option is deprecated. See: #{KnapsackPro::Urls::RSPEC__DEPRECATED_RUN_ALL_WHEN_EVERYTHING_FILTERED}"
140
+ KnapsackPro.logger.error(error_message)
141
+ raise error_message
164
142
  end
165
143
 
166
- def self.args_with_seed_option_added_when_viable(args, rspec_runner)
167
- order_option = adapter_class.order_option(args)
144
+ def pull_tests_from_queue(can_initialize_queue: false)
145
+ test_file_paths = test_file_paths(
146
+ can_initialize_queue: can_initialize_queue,
147
+ executed_test_files: @node_test_file_paths
148
+ )
149
+ @node_test_file_paths += test_file_paths
150
+ test_file_paths
151
+ end
168
152
 
169
- if order_option
170
- # Don't add the seed option for order other than random, e.g. `defined`
171
- return args unless order_option.include?('rand')
172
- # Don't add the seed option if the seed is already set in args, e.g. `rand:12345`
173
- return args if order_option.to_s.split(':')[1]
174
- end
153
+ def log_rspec_batch_command(test_file_paths)
154
+ order_option = @adapter_class.order_option(@cli_args)
155
+ printable_args = @rspec_pure.args_with_seed_option_added_when_viable(order_option, @rspec_runner.knapsack__seed, @cli_args)
156
+ messages = @rspec_pure.rspec_command(printable_args, test_file_paths, :batch_finished)
157
+ log_info_messages(messages)
158
+ end
175
159
 
176
- # Don't add the seed option if the seed was not used (i.e. a different order is being used, e.g. `defined`)
177
- return args unless rspec_runner.configuration.seed_used?
160
+ def log_rspec_queue_command
161
+ order_option = @adapter_class.order_option(@cli_args)
162
+ printable_args = @rspec_pure.args_with_seed_option_added_when_viable(order_option, @rspec_runner.knapsack__seed, @cli_args)
163
+ messages = @rspec_pure.rspec_command(printable_args, @node_test_file_paths, :queue_finished)
164
+ log_info_messages(messages)
165
+ end
178
166
 
179
- @@used_seed = rspec_runner.configuration.seed.to_s
167
+ def log_info_messages(messages)
168
+ messages.each do |message|
169
+ KnapsackPro.logger.info(message)
170
+ end
171
+ end
180
172
 
181
- args + ['--seed', @@used_seed]
173
+ def unexecuted_test_files
174
+ KnapsackPro::Formatters::TimeTrackerFetcher.unexecuted_test_files(@node_test_file_paths)
182
175
  end
183
176
  end
184
177
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  module Runners
3
5
  class RSpecRunner < BaseRunner
@@ -17,8 +19,6 @@ module KnapsackPro
17
19
  cli_args = (args || '').split
18
20
  adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
19
21
 
20
- KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
21
-
22
22
  require 'rspec/core/rake_task'
23
23
 
24
24
  task_name = 'knapsack_pro:rspec_run'
@@ -31,11 +31,30 @@ module KnapsackPro
31
31
  # because pattern does not accept test example path like spec/a_spec.rb[1:2]
32
32
  # instead we pass test files and test example paths to t.rspec_opts
33
33
  t.pattern = []
34
- t.rspec_opts = "#{args} --default-path #{runner.test_dir} #{runner.stringify_test_file_paths}"
34
+ t.rspec_opts = "#{args} #{self.formatters} --default-path #{runner.test_dir} #{runner.stringify_test_file_paths}"
35
+ t.verbose = KnapsackPro::Config::Env.log_level < ::Logger::WARN
35
36
  end
36
37
  Rake::Task[task_name].invoke
37
38
  end
38
39
  end
40
+
41
+ # Use RSpec::Core::ConfigurationOptions to respect external configurations like .rspec
42
+ def self.formatters
43
+ require_relative '../formatters/time_tracker'
44
+
45
+ formatters = ::RSpec::Core::ConfigurationOptions
46
+ .new([])
47
+ .options
48
+ .fetch(:formatters, [])
49
+ .map do |formatter, output|
50
+ arg = "--format #{formatter}"
51
+ arg += " --out #{output}" if output
52
+ arg
53
+ end
54
+ formatters = ['--format progress'] if formatters.empty?
55
+ formatters += ["--format #{KnapsackPro::Formatters::TimeTracker}"]
56
+ formatters.join(' ')
57
+ end
39
58
  end
40
59
  end
41
60
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  module Runners
3
5
  class SpinachRunner < BaseRunner
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  module Runners
3
5
  class TestUnitRunner < BaseRunner
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class SlowTestFileDeterminer
3
5
  TIME_THRESHOLD_PER_CI_NODE = 0.7 # 70%
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class SlowTestFileFinder
3
5
  # Get recorded test files from API.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rake'
2
4
 
3
5
  module KnapsackPro
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  module TestCaseDetectors
3
5
  class RSpecTestExampleDetector
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  module TestCaseMergers
3
5
  class BaseMerger
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  module TestCaseMergers
3
5
  class RSpecMerger < BaseMerger
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class TestFileCleaner
3
5
  def self.clean(test_file_path)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class TestFileFinder
3
5
  def self.call(test_file_pattern, test_file_list_enabled: true)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class TestFilePattern
3
5
  def self.call(adapter_class)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class TestFilePresenter
3
5
  def self.stringify_paths(test_file_paths)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class TestFilesWithTestCasesComposer
3
5
  # Args:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class TestFlatDistributor
3
5
  DIR_TYPES = [
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class Tracker
3
5
  include Singleton
@@ -6,11 +8,10 @@ module KnapsackPro
6
8
  # to better allocate it in Queue Mode for future CI build runs
7
9
  DEFAULT_TEST_FILE_TIME = 0.0 # seconds
8
10
 
9
- attr_reader :global_time_since_beginning, :global_time, :test_files_with_time, :prerun_tests_loaded
11
+ attr_reader :global_time, :test_files_with_time, :prerun_tests_loaded
10
12
  attr_writer :current_test_path
11
13
 
12
14
  def initialize
13
- @global_time_since_beginning = 0
14
15
  KnapsackPro::Config::TempFiles.ensure_temp_directory_exists!
15
16
  FileUtils.mkdir_p(tracker_dir_path)
16
17
  set_defaults
@@ -133,7 +134,6 @@ module KnapsackPro
133
134
 
134
135
  def update_global_time(execution_time)
135
136
  @global_time += execution_time
136
- @global_time_since_beginning += execution_time
137
137
  end
138
138
 
139
139
  def update_test_file_time(execution_time)
@@ -18,6 +18,8 @@ module KnapsackPro
18
18
 
19
19
  INSTALLATION_GUIDE = "#{HOST}/perma/ruby/installation-guide"
20
20
 
21
+ KNAPSACK_PRO_CI_NODE_BUILD_ID= "#{HOST}/perma/ruby/knapsack-pro-ci-node-build-id"
22
+
21
23
  QUEUE_MODE__CONNECTION_ERROR_WITH_FALLBACK_ENABLED_FALSE = "#{HOST}/perma/ruby/queue-mode-connection-error-with-fallback-enabled-false"
22
24
 
23
25
  QUEUE_MODE__CONNECTION_ERROR_WITH_FALLBACK_ENABLED_TRUE_AND_POSITIVE_RETRY_COUNT = "#{HOST}/perma/ruby/queue-mode-connection-error-with-fallback-enabled-true-and-positive-retry-count"
@@ -26,6 +28,8 @@ module KnapsackPro
26
28
 
27
29
  REGULAR_MODE__CONNECTION_ERROR_WITH_FALLBACK_ENABLED_TRUE_AND_POSITIVE_RETRY_COUNT = "#{HOST}/perma/ruby/regular-mode-connection-error-with-fallback-enabled-true-and-positive-retry-count"
28
30
 
31
+ RSPEC__DEPRECATED_RUN_ALL_WHEN_EVERYTHING_FILTERED = "#{HOST}/perma/ruby/rspec-deprecated-run-all-when-everything-filtered"
32
+
29
33
  RSPEC__SKIPS_TESTS = "#{HOST}/perma/ruby/rspec-skips-tests"
30
34
 
31
35
  RSPEC__SPLIT_BY_TEST_EXAMPLES__TAG = "#{HOST}/perma/ruby/rspec-split-by-test-examples-tag"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
4
  class Utils
3
5
  def self.unsymbolize(obj)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KnapsackPro
2
- VERSION = '3.8.0'
4
+ VERSION = '7.0.0'
3
5
  end
data/lib/knapsack_pro.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'singleton'
3
5
  require 'net/http'
@@ -6,9 +8,9 @@ require 'uri'
6
8
  require 'rake/testtask'
7
9
  require 'digest'
8
10
  require 'securerandom'
11
+ require 'timeout'
9
12
  require_relative 'knapsack_pro/urls'
10
13
  require_relative 'knapsack_pro/version'
11
- require_relative 'knapsack_pro/extensions/time'
12
14
  require_relative 'knapsack_pro/hooks/queue'
13
15
  require_relative 'knapsack_pro/utils'
14
16
  require_relative 'knapsack_pro/config/ci/base'
@@ -21,8 +23,6 @@ require_relative 'knapsack_pro/config/ci/semaphore'
21
23
  require_relative 'knapsack_pro/config/ci/semaphore2'
22
24
  require_relative 'knapsack_pro/config/ci/buildkite'
23
25
  require_relative 'knapsack_pro/config/ci/travis'
24
- require_relative 'knapsack_pro/config/ci/snap_ci'
25
- require_relative 'knapsack_pro/config/ci/solano_ci'
26
26
  require_relative 'knapsack_pro/config/ci/codeship'
27
27
  require_relative 'knapsack_pro/config/ci/github_actions'
28
28
  require_relative 'knapsack_pro/config/ci/heroku'
@@ -57,6 +57,7 @@ require_relative 'knapsack_pro/adapters/test_unit_adapter'
57
57
  require_relative 'knapsack_pro/adapters/spinach_adapter'
58
58
  require_relative 'knapsack_pro/allocator'
59
59
  require_relative 'knapsack_pro/queue_allocator'
60
+ require_relative 'knapsack_pro/mask_string'
60
61
  require_relative 'knapsack_pro/test_case_mergers/base_merger'
61
62
  require_relative 'knapsack_pro/test_case_mergers/rspec_merger'
62
63
  require_relative 'knapsack_pro/build_distribution_fetcher'
@@ -81,6 +82,7 @@ require_relative 'knapsack_pro/crypto/encryptor'
81
82
  require_relative 'knapsack_pro/crypto/branch_encryptor'
82
83
  require_relative 'knapsack_pro/crypto/decryptor'
83
84
  require_relative 'knapsack_pro/crypto/digestor'
85
+ require_relative 'knapsack_pro/pure/queue/rspec_pure'
84
86
 
85
87
  require 'knapsack_pro/railtie' if defined?(Rails::Railtie)
86
88
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'knapsack_pro'
2
4
 
3
5
  namespace :knapsack_pro do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'knapsack_pro'
2
4
 
3
5
  namespace :knapsack_pro do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'knapsack_pro'
2
4
 
3
5
  namespace :knapsack_pro do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'knapsack_pro'
2
4
 
3
5
  namespace :knapsack_pro do
@@ -1,8 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'knapsack_pro'
2
4
 
3
5
  namespace :knapsack_pro do
4
6
  namespace :queue do
5
7
  task :cucumber, [:cucumber_args] do |_, args|
8
+ Kernel.system("RAILS_ENV=test RACK_ENV=test #{$PROGRAM_NAME} 'knapsack_pro:queue:cucumber_go[#{args[:cucumber_args]}]'")
9
+ exitstatus = $?.exitstatus
10
+ if exitstatus.nil?
11
+ puts 'Something went wrong. Most likely, the process has been killed. Knapsack Pro has been terminated.'
12
+ Kernel.exit(1)
13
+ else
14
+ Kernel.exit(exitstatus)
15
+ end
16
+ end
17
+
18
+ task :cucumber_go, [:cucumber_args] do |_, args|
6
19
  KnapsackPro::Runners::Queue::CucumberRunner.run(args[:cucumber_args])
7
20
  end
8
21
  end
@@ -1,8 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'knapsack_pro'
2
4
 
3
5
  namespace :knapsack_pro do
4
6
  namespace :queue do
5
7
  task :minitest, [:minitest_args] do |_, args|
8
+ Kernel.system("RAILS_ENV=test RACK_ENV=test #{$PROGRAM_NAME} 'knapsack_pro:queue:minitest_go[#{args[:minitest_args]}]'")
9
+ exitstatus = $?.exitstatus
10
+ if exitstatus.nil?
11
+ puts 'Something went wrong. Most likely, the process has been killed. Knapsack Pro has been terminated.'
12
+ Kernel.exit(1)
13
+ else
14
+ Kernel.exit(exitstatus)
15
+ end
16
+ end
17
+
18
+ task :minitest_go, [:minitest_args] do |_, args|
6
19
  KnapsackPro::Runners::Queue::MinitestRunner.run(args[:minitest_args])
7
20
  end
8
21
  end
@@ -1,8 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'knapsack_pro'
2
4
 
3
5
  namespace :knapsack_pro do
4
6
  namespace :queue do
5
7
  task :rspec, [:rspec_args] do |_, args|
8
+ Kernel.system("RAILS_ENV=test RACK_ENV=test #{$PROGRAM_NAME} 'knapsack_pro:queue:rspec_go[#{args[:rspec_args]}]'")
9
+ exitstatus = $?.exitstatus
10
+ if exitstatus.nil?
11
+ puts 'Something went wrong. Most likely, the process has been killed. Knapsack Pro has been terminated.'
12
+ Kernel.exit(1)
13
+ else
14
+ Kernel.exit(exitstatus)
15
+ end
16
+ end
17
+
18
+ task :rspec_go, [:rspec_args] do |_, args|
6
19
  KnapsackPro::Runners::Queue::RSpecRunner.run(args[:rspec_args])
7
20
  end
8
21
  end