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
@@ -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