knapsack_pro 3.8.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -42,6 +42,18 @@ describe KnapsackPro::Adapters::BaseAdapter do
42
42
  end
43
43
  end
44
44
 
45
+ describe '.split_by_test_cases_enabled?' do
46
+ subject { described_class.split_by_test_cases_enabled? }
47
+
48
+ it { expect(subject).to be false }
49
+ end
50
+
51
+ describe '.test_file_cases_for' do
52
+ subject { described_class.test_file_cases_for([]) }
53
+
54
+ it { expect { subject }.to raise_error NotImplementedError }
55
+ end
56
+
45
57
  describe '.slow_test_file?' do
46
58
  let(:adapter_class) { double }
47
59
  let(:slow_test_files) do
@@ -98,6 +110,7 @@ describe KnapsackPro::Adapters::BaseAdapter do
98
110
 
99
111
  before do
100
112
  expect(::Kernel).to receive(:at_exit).and_yield
113
+ allow(File).to receive(:exist?)
101
114
  expect(File).to receive(:exist?).with('.knapsack_pro/KnapsackPro-Adapters-BaseAdapter-bind_method_called_for_node_0.txt').and_return(adapter_bind_method_called_file_exists)
102
115
  end
103
116
 
@@ -146,7 +159,7 @@ describe KnapsackPro::Adapters::BaseAdapter do
146
159
  it do
147
160
  logger = instance_double(Logger)
148
161
  expect(KnapsackPro).to receive(:logger).and_return(logger)
149
- expect(logger).to receive(:debug).with('Test suite time execution recording enabled.')
162
+ expect(logger).to receive(:debug).with('Regular Mode enabled.')
150
163
  end
151
164
  it { expect(subject).to receive(:bind_time_tracker) }
152
165
  it { expect(subject).to receive(:bind_save_report) }
@@ -155,24 +168,22 @@ describe KnapsackPro::Adapters::BaseAdapter do
155
168
  context 'when queue recording enabled' do
156
169
  let(:queue_recording_enabled?) { true }
157
170
 
158
- before do
159
- allow(subject).to receive(:bind_before_queue_hook)
160
- allow(subject).to receive(:bind_time_tracker)
161
- end
162
-
163
- it do
171
+ it 'calls queue hooks in proper order before binding time tracker' do
164
172
  logger = instance_double(Logger)
165
173
  expect(KnapsackPro).to receive(:logger).and_return(logger)
166
- expect(logger).to receive(:debug).with('Test suite time execution queue recording enabled.')
174
+ expect(logger).to receive(:debug).with('Queue Mode enabled.')
175
+
176
+ expect(subject).to receive(:bind_before_queue_hook).ordered
177
+ expect(subject).to receive(:bind_after_queue_hook).ordered
178
+ expect(subject).to receive(:bind_time_tracker).ordered
167
179
  end
168
- it { expect(subject).to receive(:bind_before_queue_hook) }
169
- it { expect(subject).to receive(:bind_time_tracker) }
170
180
  end
171
181
 
172
182
  context 'when recording disabled' do
173
- it { expect(subject).not_to receive(:bind_time_tracker) }
174
183
  it { expect(subject).not_to receive(:bind_save_report) }
175
184
  it { expect(subject).not_to receive(:bind_before_queue_hook) }
185
+ it { expect(subject).not_to receive(:bind_after_queue_hook) }
186
+ it { expect(subject).not_to receive(:bind_time_tracker) }
176
187
  end
177
188
  end
178
189
 
@@ -199,4 +210,12 @@ describe KnapsackPro::Adapters::BaseAdapter do
199
210
  }.to raise_error(NotImplementedError)
200
211
  end
201
212
  end
213
+
214
+ describe '#bind_after_queue_hook' do
215
+ it do
216
+ expect {
217
+ subject.bind_after_queue_hook
218
+ }.to raise_error(NotImplementedError)
219
+ end
220
+ end
202
221
  end
@@ -203,16 +203,13 @@ describe KnapsackPro::Adapters::CucumberAdapter do
203
203
  end
204
204
  end
205
205
 
206
- describe '#bind_queue_mode' do
206
+ describe '#bind_after_queue_hook' do
207
207
  it do
208
- expect(subject).to receive(:bind_before_queue_hook)
209
- expect(subject).to receive(:bind_time_tracker)
210
-
211
208
  expect(::Kernel).to receive(:at_exit).and_yield
212
209
  expect(KnapsackPro::Hooks::Queue).to receive(:call_after_subset_queue)
213
210
  expect(KnapsackPro::Report).to receive(:save_subset_queue_to_file)
214
211
 
215
- subject.bind_queue_mode
212
+ subject.bind_after_queue_hook
216
213
  end
217
214
  end
218
215
  end
@@ -1,3 +1,5 @@
1
+ require_relative '../../../lib/knapsack_pro/formatters/time_tracker'
2
+
1
3
  describe KnapsackPro::Adapters::RSpecAdapter do
2
4
  it 'backwards compatibility with knapsack gem old rspec adapter name' do
3
5
  expect(KnapsackPro::Adapters::RspecAdapter.new).to be_kind_of(described_class)
@@ -8,10 +10,85 @@ describe KnapsackPro::Adapters::RSpecAdapter do
8
10
  end
9
11
 
10
12
  context do
11
- before { expect(::RSpec).to receive(:configure) }
13
+ before { expect(::RSpec).to receive(:configure).at_least(:once) }
12
14
  it_behaves_like 'adapter'
13
15
  end
14
16
 
17
+ describe '.split_by_test_cases_enabled?' do
18
+ subject { described_class.split_by_test_cases_enabled? }
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 the RSpec split by test examples is enabled' do
25
+ let(:rspec_split_by_test_examples_enabled) { true }
26
+
27
+ it { expect(subject).to be true }
28
+
29
+ context 'when the RSpec version is < 3.3.0' do
30
+ before do
31
+ stub_const('RSpec::Core::Version::STRING', '3.2.0')
32
+ end
33
+
34
+ it do
35
+ expect { subject }.to raise_error RuntimeError, 'RSpec >= 3.3.0 is required to split test files by test examples. Learn more: https://knapsackpro.com/perma/ruby/split-by-test-examples'
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'when the RSpec split by test examples is disabled' do
41
+ let(:rspec_split_by_test_examples_enabled) { false }
42
+
43
+ it { expect(subject).to be false }
44
+ end
45
+ end
46
+
47
+ describe '.test_file_cases_for' do
48
+ let(:slow_test_files) do
49
+ [
50
+ '1_spec.rb',
51
+ '2_spec.rb',
52
+ '3_spec.rb',
53
+ '4_spec.rb',
54
+ '5_spec.rb',
55
+ ]
56
+ end
57
+
58
+ subject { described_class.test_file_cases_for(slow_test_files) }
59
+
60
+ before do
61
+ logger = instance_double(Logger)
62
+ expect(KnapsackPro).to receive(:logger).and_return(logger)
63
+ expect(logger).to receive(:info).with("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing 5 slow test files.")
64
+
65
+ cmd = 'RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector'
66
+ expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
67
+ end
68
+
69
+ context 'when the rake task to detect RSpec test examples succeeded' do
70
+ let(:cmd_result) { true }
71
+
72
+ it 'returns test example paths for slow test files' do
73
+ rspec_test_example_detector = instance_double(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector)
74
+ expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
75
+
76
+ test_file_example_paths = double
77
+ expect(rspec_test_example_detector).to receive(:test_file_example_paths).and_return(test_file_example_paths)
78
+
79
+ expect(subject).to eq test_file_example_paths
80
+ end
81
+ end
82
+
83
+ context 'when the rake task to detect RSpec test examples failed' do
84
+ let(:cmd_result) { false }
85
+
86
+ it do
87
+ expect { subject }.to raise_error(RuntimeError, 'Could not generate JSON report for RSpec. Rake task failed when running RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector')
88
+ end
89
+ end
90
+ end
91
+
15
92
  describe '.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!' do
16
93
  let(:cli_args) { double }
17
94
 
@@ -162,59 +239,79 @@ describe KnapsackPro::Adapters::RSpecAdapter do
162
239
  end
163
240
  end
164
241
 
165
- describe '.test_path' do
166
- let(:example_group) do
167
- {
168
- file_path: '1_shared_example.rb',
169
- parent_example_group: {
170
- file_path: '2_shared_example.rb',
171
- parent_example_group: {
172
- file_path: 'a_spec.rb'
173
- }
174
- }
175
- }
242
+ describe '.file_path_for' do
243
+ let(:current_example) { ::RSpec.describe.example }
244
+
245
+ subject { described_class.file_path_for(current_example) }
246
+
247
+ context "when id ends in _spec.rb" do
248
+ it "returns the first part of the id" do
249
+ allow(current_example).to receive(:id).and_return("./foo_spec.rb[1:1]")
250
+
251
+ expect(subject).to eq('./foo_spec.rb')
252
+ end
176
253
  end
177
- let(:current_example) do
178
- OpenStruct.new(metadata: {
179
- example_group: example_group
180
- })
254
+
255
+ context "when id does not end in _spec.rb" do
256
+ it "returns the file_path" do
257
+ allow(current_example).to receive(:id).and_return("./foo.rb")
258
+ allow(current_example).to receive(:metadata).and_return(file_path: "./foo_spec.rb")
259
+
260
+ expect(subject).to eq('./foo_spec.rb')
261
+ end
181
262
  end
182
263
 
183
- subject { described_class.test_path(current_example) }
264
+ context "when id and file_path do not end in _spec.rb" do
265
+ it "returns the example_group's file_path" do
266
+ allow(current_example).to receive(:id).and_return("./foo.rb")
267
+ allow(current_example).to receive(:metadata).and_return(
268
+ file_path: "./foo.rb", example_group: { file_path: "./foo_spec.rb" }
269
+ )
184
270
 
185
- it { should eql 'a_spec.rb' }
271
+ expect(subject).to eq('./foo_spec.rb')
272
+ end
273
+ end
186
274
 
187
- context 'with turnip features' do
188
- describe 'when the turnip version is less than 2' do
189
- let(:example_group) do
190
- {
191
- file_path: "./spec/features/logging_in.feature",
192
- turnip: true,
275
+ context "when id, file_path, and example_group's file_path do not end in _spec.rb" do
276
+ it "returns the top_level_group's file_path" do
277
+ allow(current_example).to receive(:id).and_return("./foo.rb")
278
+ allow(current_example).to receive(:metadata).and_return(
279
+ file_path: "./foo.rb",
280
+ example_group: {
281
+ file_path: "./foo.rb",
193
282
  parent_example_group: {
194
- file_path: "gems/turnip-1.2.4/lib/turnip/rspec.rb"
283
+ file_path: "./foo_spec.rb",
195
284
  }
196
285
  }
197
- end
198
-
199
- before { stub_const("Turnip::VERSION", '1.2.4') }
286
+ )
200
287
 
201
- it { should eql './spec/features/logging_in.feature' }
288
+ expect(subject).to eq('./foo_spec.rb')
202
289
  end
290
+ end
203
291
 
204
- describe 'when turnip is version 2 or greater' do
205
- let(:example_group) do
206
- {
207
- file_path: "gems/turnip-2.0.0/lib/turnip/rspec.rb",
208
- turnip: true,
292
+ context "when id, file_path, example_group's, and top_level_group's file_path do not end in _spec.rb" do
293
+ it "returns empty string" do
294
+ allow(current_example).to receive(:id).and_return("./foo.rb")
295
+ allow(current_example).to receive(:metadata).and_return(
296
+ file_path: "./foo.rb",
297
+ example_group: {
298
+ file_path: "./foo.rb",
209
299
  parent_example_group: {
210
- file_path: "./spec/features/logging_in.feature",
300
+ file_path: "./foo.rb",
211
301
  }
212
302
  }
213
- end
303
+ )
214
304
 
215
- before { stub_const("Turnip::VERSION", '2.0.0') }
305
+ expect(subject).to eq('')
306
+ end
307
+ end
308
+
309
+ context "when id does not end in .feature (nor _spec.rb)" do
310
+ it "returns the file_path" do
311
+ allow(current_example).to receive(:id).and_return("./foo.rb")
312
+ allow(current_example).to receive(:metadata).and_return(file_path: "./foo.feature")
216
313
 
217
- it { should eql './spec/features/logging_in.feature' }
314
+ expect(subject).to eq("./foo.feature")
218
315
  end
219
316
  end
220
317
  end
@@ -223,155 +320,37 @@ describe KnapsackPro::Adapters::RSpecAdapter do
223
320
  let(:config) { double }
224
321
 
225
322
  describe '#bind_time_tracker' do
226
- let(:tracker) { instance_double(KnapsackPro::Tracker) }
227
- let(:logger) { instance_double(Logger) }
228
- let(:global_time) { 'Global time: 01m 05s' }
229
- let(:test_path) { 'spec/a_spec.rb' }
230
323
  let(:current_example) { double(metadata: {}) }
231
324
 
232
325
  context "when the example's metadata has :focus tag AND RSpec inclusion rule includes :focus" do
233
326
  let(:current_example) { double(metadata: { focus: true }) }
327
+ let(:test_path) { 'spec/a_spec.rb' }
234
328
 
235
329
  it do
236
- expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(false)
237
-
238
- expect(config).to receive(:prepend_before).with(:context).and_yield
239
-
240
- allow(KnapsackPro).to receive(:tracker).and_return(tracker)
241
- expect(tracker).to receive(:start_timer).ordered
242
-
243
330
  expect(config).to receive(:around).with(:each).and_yield(current_example)
244
331
  expect(::RSpec).to receive(:configure).and_yield(config)
245
332
 
246
- expect(tracker).to receive(:current_test_path).ordered.and_return(test_path)
247
- expect(tracker).to receive(:stop_timer).ordered
248
-
249
- expect(described_class).to receive(:test_path).with(current_example).and_return(test_path)
250
-
251
- expect(tracker).to receive(:current_test_path=).with(test_path).ordered
333
+ expect(described_class).to receive(:file_path_for).with(current_example).and_return(test_path)
252
334
 
253
335
  expect(described_class).to receive_message_chain(:rspec_configuration, :filter, :rules, :[]).with(:focus).and_return(true)
254
336
 
255
337
  expect {
256
338
  subject.bind_time_tracker
257
- }.to raise_error /We detected a test file path spec\/a_spec\.rb with a test using the metadata `:focus` tag/
339
+ }.to raise_error /Knapsack Pro found an example tagged with focus in spec\/a_spec\.rb/i
258
340
  end
259
341
  end
260
342
 
261
- context 'when rspec split by test examples is disabled' do
262
- before do
263
- expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(false)
264
- end
265
-
343
+ context 'with no focus' do
266
344
  it 'records time for current test path' do
267
- expect(config).to receive(:prepend_before).with(:context).and_yield
268
-
269
- allow(KnapsackPro).to receive(:tracker).and_return(tracker)
270
- expect(tracker).to receive(:start_timer).ordered
271
-
272
345
  expect(config).to receive(:around).with(:each).and_yield(current_example)
273
- expect(config).to receive(:append_after).with(:context).and_yield
274
- expect(config).to receive(:after).with(:suite).and_yield
275
- expect(::RSpec).to receive(:configure).and_yield(config)
276
-
277
- expect(tracker).to receive(:current_test_path).ordered.and_return(test_path)
278
- expect(tracker).to receive(:stop_timer).ordered
279
-
280
- expect(described_class).to receive(:test_path).with(current_example).and_return(test_path)
281
-
282
- expect(tracker).to receive(:current_test_path=).with(test_path).ordered
346
+ expect(config).to receive(:append_after).with(:suite)
347
+ expect(::RSpec).to receive(:configure).at_least(1).and_yield(config)
283
348
 
284
349
  expect(current_example).to receive(:run)
285
350
 
286
- expect(tracker).to receive(:stop_timer).ordered
287
-
288
- expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
289
- expect(KnapsackPro).to receive(:logger).and_return(logger)
290
- expect(logger).to receive(:debug).with(global_time)
291
-
292
351
  subject.bind_time_tracker
293
352
  end
294
353
  end
295
-
296
- context 'when rspec split by test examples is enabled' do
297
- let(:test_example_path) { 'spec/a_spec.rb[1:1]' }
298
-
299
- before do
300
- expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(true)
301
- end
302
-
303
- context 'when current test_path is a slow test file' do
304
- before do
305
- expect(described_class).to receive(:slow_test_file?).with(described_class, test_path).and_return(true)
306
- end
307
-
308
- it 'records time for example.id' do
309
- expect(current_example).to receive(:id).and_return(test_example_path)
310
-
311
- expect(config).to receive(:prepend_before).with(:context).and_yield
312
-
313
- allow(KnapsackPro).to receive(:tracker).and_return(tracker)
314
- expect(tracker).to receive(:start_timer).ordered
315
-
316
- expect(config).to receive(:around).with(:each).and_yield(current_example)
317
- expect(config).to receive(:append_after).with(:context).and_yield
318
- expect(config).to receive(:after).with(:suite).and_yield
319
- expect(::RSpec).to receive(:configure).and_yield(config)
320
-
321
- expect(tracker).to receive(:current_test_path).ordered.and_return(test_path)
322
- expect(tracker).to receive(:stop_timer).ordered
323
-
324
- expect(described_class).to receive(:test_path).with(current_example).and_return(test_path)
325
-
326
- expect(tracker).to receive(:current_test_path=).with(test_example_path).ordered
327
-
328
- expect(current_example).to receive(:run)
329
-
330
- expect(tracker).to receive(:stop_timer).ordered
331
-
332
- expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
333
- expect(KnapsackPro).to receive(:logger).and_return(logger)
334
- expect(logger).to receive(:debug).with(global_time)
335
-
336
- subject.bind_time_tracker
337
- end
338
- end
339
-
340
- context 'when current test_path is not a slow test file' do
341
- before do
342
- expect(described_class).to receive(:slow_test_file?).with(described_class, test_path).and_return(false)
343
- end
344
-
345
- it 'records time for current test path' do
346
- expect(config).to receive(:prepend_before).with(:context).and_yield
347
-
348
- allow(KnapsackPro).to receive(:tracker).and_return(tracker)
349
- expect(tracker).to receive(:start_timer).ordered
350
-
351
- expect(config).to receive(:around).with(:each).and_yield(current_example)
352
- expect(config).to receive(:append_after).with(:context).and_yield
353
- expect(config).to receive(:after).with(:suite).and_yield
354
- expect(::RSpec).to receive(:configure).and_yield(config)
355
-
356
- expect(described_class).to receive(:test_path).with(current_example).and_return(test_path)
357
-
358
- expect(tracker).to receive(:current_test_path).ordered.and_return(test_path)
359
- expect(tracker).to receive(:stop_timer).ordered
360
-
361
- expect(tracker).to receive(:current_test_path=).with(test_path).ordered
362
-
363
- expect(current_example).to receive(:run)
364
-
365
- expect(tracker).to receive(:stop_timer).ordered
366
-
367
- expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
368
- expect(KnapsackPro).to receive(:logger).and_return(logger)
369
- expect(logger).to receive(:debug).with(global_time)
370
-
371
- subject.bind_time_tracker
372
- end
373
- end
374
- end
375
354
  end
376
355
 
377
356
  describe '#bind_save_report' do
@@ -379,21 +358,14 @@ describe KnapsackPro::Adapters::RSpecAdapter do
379
358
  expect(config).to receive(:after).with(:suite).and_yield
380
359
  expect(::RSpec).to receive(:configure).and_yield(config)
381
360
 
382
- expect(KnapsackPro::Report).to receive(:save)
361
+ time_tracker = instance_double(KnapsackPro::Formatters::TimeTracker)
362
+ times = [{ path: "foo_spec.rb", time_execution: 1.0 }]
363
+ expect(time_tracker).to receive(:batch).and_return(times)
364
+ expect(KnapsackPro::Formatters::TimeTrackerFetcher).to receive(:call).and_return(time_tracker)
365
+ expect(KnapsackPro::Report).to receive(:save).with(times)
383
366
 
384
367
  subject.bind_save_report
385
368
  end
386
369
  end
387
-
388
- describe '#bind_before_queue_hook' do
389
- it do
390
- expect(config).to receive(:before).with(:suite).and_yield
391
- expect(::RSpec).to receive(:configure).and_yield(config)
392
-
393
- expect(KnapsackPro::Hooks::Queue).to receive(:call_before_queue)
394
-
395
- subject.bind_before_queue_hook
396
- end
397
- end
398
370
  end
399
371
  end
@@ -117,8 +117,8 @@ describe KnapsackPro::BaseAllocatorBuilder do
117
117
  describe '#fast_and_slow_test_files_to_run' do
118
118
  subject { allocator_builder.fast_and_slow_test_files_to_run }
119
119
 
120
- context 'when looking for test files on disk by default' do
121
- it do
120
+ context 'when split by test cases disabled' do
121
+ it 'returns test files to run based on test files on the disk' do
122
122
  test_file_pattern = double
123
123
  expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return(test_file_pattern)
124
124
 
@@ -129,70 +129,44 @@ describe KnapsackPro::BaseAllocatorBuilder do
129
129
  end
130
130
  end
131
131
 
132
- context 'when RSpec adapter AND rspec split by test examples is enabled' do
133
- let(:adapter_class) { KnapsackPro::Adapters::RSpecAdapter }
132
+ context 'when split by test cases enabled' do
134
133
  let(:test_files_to_run) { double }
135
- let(:cmd) { 'RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector' }
136
134
 
137
- before do
138
- expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(true)
135
+ before do
136
+ expect(adapter_class).to receive(:split_by_test_cases_enabled?).and_return(true)
139
137
 
140
138
  test_file_pattern = double
141
139
  expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return(test_file_pattern)
142
140
 
143
141
  expect(KnapsackPro::TestFileFinder).to receive(:call).with(test_file_pattern).and_return(test_files_to_run)
144
- end
145
142
 
146
- context 'when RSpec version < 3.3.0' do
147
- before do
148
- stub_const('RSpec::Core::Version::STRING', '3.2.0')
149
- end
150
-
151
- it do
152
- expect { subject }.to raise_error RuntimeError, 'RSpec >= 3.3.0 is required to split test files by test examples. Learn more: https://knapsackpro.com/perma/ruby/split-by-test-examples'
153
- end
143
+ expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
154
144
  end
155
145
 
156
- context 'when rake task to detect RSpec test examples works' do
157
- let(:slow_test_files) { double(size: 5) }
158
- let(:cmd_result) { true }
159
- let(:test_file_example_paths) { double }
160
- let(:logger) { instance_double(Logger) }
161
- let(:test_files_with_test_cases) { double }
162
-
163
- before do
164
- expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
165
-
166
- expect(KnapsackPro).to receive(:logger).and_return(logger)
167
-
168
- expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
169
-
170
- rspec_test_example_detector = instance_double(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector)
171
- expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
172
- expect(rspec_test_example_detector).to receive(:test_file_example_paths).and_return(test_file_example_paths)
173
-
174
- expect(KnapsackPro::TestFilesWithTestCasesComposer).to receive(:call).with(test_files_to_run, slow_test_files, test_file_example_paths).and_return(test_files_with_test_cases)
146
+ context 'when slow test files are detected' do
147
+ let(:slow_test_files) do
148
+ [
149
+ '1_spec.rb',
150
+ '2_spec.rb',
151
+ ]
175
152
  end
176
153
 
177
- it do
178
- expect(logger).to receive(:info).with("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual 'it's. Thanks to that a single slow test file can be split across parallel CI nodes). Analyzing 5 slow test files.")
154
+ it 'returns test files with test cases' do
155
+ test_file_cases = double
156
+ expect(adapter_class).to receive(:test_file_cases_for).with(slow_test_files).and_return(test_file_cases)
157
+
158
+ test_files_with_test_cases = double
159
+ expect(KnapsackPro::TestFilesWithTestCasesComposer).to receive(:call).with(test_files_to_run, slow_test_files, test_file_cases).and_return(test_files_with_test_cases)
179
160
 
180
161
  expect(subject).to eq test_files_with_test_cases
181
162
  end
182
163
  end
183
164
 
184
- context 'when rake task to detect RSpec test examples failed' do
185
- let(:slow_test_files) { double(size: 5) }
186
- let(:cmd_result) { false }
187
-
188
- before do
189
- expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
190
-
191
- expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
192
- end
165
+ context 'when slow test files are not detected' do
166
+ let(:slow_test_files) { [] }
193
167
 
194
- it do
195
- expect { subject }.to raise_error(RuntimeError, 'Could not generate JSON report for RSpec. Rake task failed when running RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector')
168
+ it 'returns test files without test cases' do
169
+ expect(subject).to eq test_files_to_run
196
170
  end
197
171
  end
198
172
  end
@@ -6,7 +6,9 @@ describe KnapsackPro::Client::API::V1::BuildDistributions do
6
6
  let(:node_total) { double }
7
7
  let(:node_index) { double }
8
8
  let(:ci_build_id) { double }
9
+ let(:masked_user_seat) { double }
9
10
  let(:test_files) { double }
11
+ let(:cache_read_attempt) { [false, true].sample }
10
12
 
11
13
  subject do
12
14
  described_class.subset(
@@ -22,6 +24,7 @@ describe KnapsackPro::Client::API::V1::BuildDistributions do
22
24
  before do
23
25
  expect(KnapsackPro::Config::Env).to receive(:fixed_test_suite_split).and_return(fixed_test_suite_split)
24
26
  expect(KnapsackPro::Config::Env).to receive(:ci_node_build_id).and_return(ci_build_id)
27
+ expect(KnapsackPro::Config::Env).to receive(:masked_user_seat).and_return(masked_user_seat)
25
28
  end
26
29
 
27
30
  context 'when cache_read_attempt=true' do
@@ -29,19 +32,9 @@ describe KnapsackPro::Client::API::V1::BuildDistributions do
29
32
 
30
33
  it 'does not send test_files among other params' do
31
34
  action = double
32
- expect(KnapsackPro::Client::API::Action).to receive(:new).with({
33
- endpoint_path: '/v1/build_distributions/subset',
34
- http_method: :post,
35
- request_hash: {
36
- fixed_test_suite_split: fixed_test_suite_split,
37
- cache_read_attempt: cache_read_attempt,
38
- commit_hash: commit_hash,
39
- branch: branch,
40
- node_total: node_total,
41
- node_index: node_index,
42
- ci_build_id: ci_build_id,
43
- }
44
- }).and_return(action)
35
+ expect(KnapsackPro::Client::API::Action).to receive(:new).with(
36
+ hash_including(request_hash: hash_excluding(:test_files))
37
+ ).and_return(action)
45
38
  expect(subject).to eq action
46
39
  end
47
40
  end
@@ -51,23 +44,22 @@ describe KnapsackPro::Client::API::V1::BuildDistributions do
51
44
 
52
45
  it 'sends test_files among other params' do
53
46
  action = double
54
- expect(KnapsackPro::Client::API::Action).to receive(:new).with({
55
- endpoint_path: '/v1/build_distributions/subset',
56
- http_method: :post,
57
- request_hash: {
58
- fixed_test_suite_split: fixed_test_suite_split,
59
- cache_read_attempt: cache_read_attempt,
60
- commit_hash: commit_hash,
61
- branch: branch,
62
- node_total: node_total,
63
- node_index: node_index,
64
- ci_build_id: ci_build_id,
65
- test_files: test_files
66
- }
67
- }).and_return(action)
47
+ expect(KnapsackPro::Client::API::Action).to receive(:new).with(
48
+ hash_including(request_hash: hash_including(test_files: test_files))
49
+ ).and_return(action)
68
50
  expect(subject).to eq action
69
51
  end
70
52
  end
53
+
54
+ it "sends authors" do
55
+ action = double
56
+
57
+ expect(KnapsackPro::Client::API::Action).to receive(:new).with(
58
+ hash_including(request_hash: hash_including(:build_author, :commit_authors))
59
+ ).and_return(action)
60
+
61
+ expect(subject).to eq action
62
+ end
71
63
  end
72
64
 
73
65
  describe '.last' do