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
@@ -0,0 +1,424 @@
1
+ # Named _specs.rb on purpose because it hangs if run as part of `bundle exec rspec`.
2
+ # Use `bundle exec ruby spec/knapsack_pro/formatters/time_tracker_specs.rb` instead.
3
+
4
+ require 'rspec/core'
5
+ require 'knapsack_pro'
6
+ require 'stringio'
7
+ require 'tempfile'
8
+ require_relative '../../../lib/knapsack_pro/formatters/time_tracker'
9
+
10
+ class TestTimeTracker
11
+ def test_single_example
12
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
13
+ false
14
+ end
15
+
16
+ spec = <<~SPEC
17
+ describe "KnapsackPro::Formatters::TimeTracker" do
18
+ it do
19
+ sleep 0.1
20
+ expect(1).to eq 1
21
+ end
22
+ end
23
+ SPEC
24
+
25
+ run_specs(spec) do |spec_paths, times|
26
+ raise unless times.size == 1
27
+ raise unless times[0]["path"] == spec_paths.first
28
+ raise unless times[0]["time_execution"] > 0.10
29
+ raise unless times[0]["time_execution"] < 0.15
30
+ end
31
+ end
32
+
33
+ def test_two_files
34
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
35
+ false
36
+ end
37
+
38
+ spec_1 = <<~SPEC
39
+ describe "KnapsackPro::Formatters::TimeTracker 1" do
40
+ it do
41
+ sleep 0.1
42
+ expect(1).to eq 1
43
+ end
44
+ end
45
+ SPEC
46
+
47
+ spec_2 = <<~SPEC
48
+ describe "KnapsackPro::Formatters::TimeTracker 2" do
49
+ it do
50
+ sleep 0.2
51
+ expect(1).to eq 1
52
+ end
53
+ end
54
+ SPEC
55
+
56
+ run_specs([spec_1, spec_2]) do |spec_paths, times|
57
+ raise unless times.size == 2
58
+ raise unless times.first["path"] == spec_paths.first
59
+ raise unless times.first["time_execution"] > 0.10
60
+ raise unless times.first["time_execution"] < 0.15
61
+ raise unless times.last["path"] == spec_paths.last
62
+ raise unless times.last["time_execution"] > 0.20
63
+ raise unless times.last["time_execution"] < 0.25
64
+ end
65
+ end
66
+
67
+ def test_failing_example
68
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
69
+ false
70
+ end
71
+
72
+ spec = <<~SPEC
73
+ describe "KnapsackPro::Formatters::TimeTracker" do
74
+ it do
75
+ sleep 0.1
76
+ expect(1).to eq 2
77
+ end
78
+ end
79
+ SPEC
80
+
81
+ run_specs(spec) do |spec_paths, times|
82
+ raise unless times.size == 1
83
+ raise unless times[0]["path"] == spec_paths.first
84
+ raise unless times[0]["time_execution"] > 0.10
85
+ raise unless times[0]["time_execution"] < 0.15
86
+ end
87
+ end
88
+
89
+ def test_pending_example
90
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
91
+ false
92
+ end
93
+
94
+ spec = <<~SPEC
95
+ describe "KnapsackPro::Formatters::TimeTracker" do
96
+ xit do
97
+ sleep 0.1
98
+ expect(1).to eq 2
99
+ end
100
+ end
101
+ SPEC
102
+
103
+ run_specs(spec) do |spec_paths, times|
104
+ raise unless times.size == 1
105
+ raise unless times[0]["path"] == spec_paths.first
106
+ raise unless times[0]["time_execution"] == 0.0
107
+ end
108
+ end
109
+
110
+ def test_multiple_top_level_groups
111
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
112
+ false
113
+ end
114
+
115
+ spec = <<~SPEC
116
+ describe "KnapsackPro::Formatters::TimeTracker 1" do
117
+ it do
118
+ sleep 0.1
119
+ expect(1).to eq 1
120
+ end
121
+ end
122
+
123
+ describe "KnapsackPro::Formatters::TimeTracker 2" do
124
+ it do
125
+ sleep 0.2
126
+ expect(1).to eq 1
127
+ end
128
+ end
129
+ SPEC
130
+
131
+ run_specs(spec) do |spec_paths, times|
132
+ raise unless times.size == 1
133
+ raise unless times[0]["path"] == spec_paths.first
134
+ raise unless times[0]["time_execution"] > 0.30
135
+ raise unless times[0]["time_execution"] < 0.35
136
+ end
137
+ end
138
+
139
+ def test_rspec_split_by_test_example
140
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
141
+ true
142
+ end
143
+
144
+ spec = <<~SPEC
145
+ describe "KnapsackPro::Formatters::TimeTracker 1" do
146
+ it do
147
+ expect(1).to eq 1
148
+ end
149
+
150
+ it do
151
+ sleep 0.1
152
+ expect(1).to eq 1
153
+ end
154
+ end
155
+
156
+ describe "KnapsackPro::Formatters::TimeTracker 2" do
157
+ it do
158
+ sleep 0.2
159
+ expect(1).to eq 1
160
+ end
161
+
162
+ it do
163
+ sleep 0.3
164
+ expect(1).to eq 1
165
+ end
166
+ end
167
+ SPEC
168
+
169
+ run_specs(spec) do |spec_paths, times|
170
+ raise unless times.size == 4
171
+ spec_path = spec_paths.first
172
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:1]" }["time_execution"] < 0.05
173
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] > 0.10
174
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] < 0.15
175
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:1]" }["time_execution"] > 0.20
176
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:1]" }["time_execution"] < 0.25
177
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:2]" }["time_execution"] > 0.30
178
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:2]" }["time_execution"] < 0.35
179
+ end
180
+ end
181
+
182
+ def test_hooks
183
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
184
+ false
185
+ end
186
+
187
+ spec = <<~SPEC
188
+ describe "KnapsackPro::Formatters::TimeTracker" do
189
+ before(:all) do
190
+ sleep 0.1
191
+ end
192
+
193
+ before(:each) do
194
+ sleep 0.1
195
+ end
196
+
197
+ after(:each) do
198
+ sleep 0.1
199
+ end
200
+
201
+ it do
202
+ expect(1).to eq 1
203
+ end
204
+
205
+ it do
206
+ expect(1).to eq 1
207
+ end
208
+
209
+ after(:all) do
210
+ sleep 0.1
211
+ end
212
+ end
213
+ SPEC
214
+
215
+ run_specs(spec) do |spec_paths, times|
216
+ raise unless times.size == 1
217
+ raise unless times[0]["path"] == spec_paths.first
218
+ raise unless times[0]["time_execution"] > 0.60
219
+ raise unless times[0]["time_execution"] < 0.65
220
+ end
221
+ end
222
+
223
+ def test_hooks_with_rspec_split_by_test_example
224
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
225
+ true
226
+ end
227
+
228
+ spec = <<~SPEC
229
+ describe "KnapsackPro::Formatters::TimeTracker" do
230
+ before(:all) do
231
+ sleep 0.1
232
+ end
233
+
234
+ before(:each) do
235
+ sleep 0.1
236
+ end
237
+
238
+ after(:each) do
239
+ sleep 0.1
240
+ end
241
+
242
+ it do
243
+ expect(1).to eq 1
244
+ end
245
+
246
+ it do
247
+ expect(1).to eq 1
248
+ end
249
+
250
+ after(:all) do
251
+ sleep 0.1
252
+ end
253
+ end
254
+ SPEC
255
+
256
+ run_specs(spec) do |spec_paths, times|
257
+ raise unless times.size == 2
258
+ spec_path = spec_paths.first
259
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:1]" }["time_execution"] > 0.40
260
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:1]" }["time_execution"] < 0.45
261
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] > 0.40
262
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] < 0.45
263
+ end
264
+ end
265
+
266
+ def test_unknown_path
267
+ KnapsackPro::Formatters::TimeTracker.class_eval do
268
+ alias_method :original_file_path_for, :file_path_for
269
+
270
+ define_method(:file_path_for) do |_example|
271
+ ""
272
+ end
273
+ end
274
+
275
+ spec = <<~SPEC
276
+ describe "KnapsackPro::Formatters::TimeTracker" do
277
+ it do
278
+ expect(1).to eq 1
279
+ end
280
+ end
281
+ SPEC
282
+
283
+ run_specs(spec) do |spec_paths, times|
284
+ raise unless times.size == 1
285
+ raise unless times[0]["path"] == spec_paths.first
286
+ raise unless times[0]["time_execution"] == 0.0
287
+ end
288
+
289
+ ensure
290
+ KnapsackPro::Formatters::TimeTracker.class_eval do
291
+ undef :file_path_for
292
+ alias_method :file_path_for, :original_file_path_for
293
+ end
294
+ end
295
+
296
+ def test_empty_group
297
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
298
+ false
299
+ end
300
+
301
+ spec = <<~SPEC
302
+ describe "KnapsackPro::Formatters::TimeTracker" do
303
+ end
304
+ SPEC
305
+
306
+ run_specs(spec) do |spec_paths, times|
307
+ raise unless times.size == 1
308
+ raise unless times[0]["path"] == spec_paths.first
309
+ raise unless times[0]["time_execution"] == 0.0
310
+ end
311
+ end
312
+
313
+ def test_duration
314
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
315
+ false
316
+ end
317
+
318
+ spec = <<~SPEC
319
+ describe "KnapsackPro::Formatters::TimeTracker" do
320
+ it do
321
+ expect(1).to eq 1
322
+ end
323
+ end
324
+ SPEC
325
+
326
+ run_specs(spec) do |_, _, time_tracker|
327
+ raise unless time_tracker.duration > 0.0
328
+ end
329
+ end
330
+
331
+ def test_unexecuted_test_files
332
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
333
+ false
334
+ end
335
+
336
+ spec = <<~SPEC
337
+ describe "KnapsackPro::Formatters::TimeTracker" do
338
+ xit do
339
+ end
340
+ end
341
+ SPEC
342
+
343
+ run_specs(spec) do |spec_paths, _, time_tracker|
344
+ unexecuted_test_files = ["foo_spec.rb", "bar_spec.rb"]
345
+ # Need to filter because RSpec keeps accumulating state.
346
+ files = time_tracker
347
+ .unexecuted_test_files(spec_paths + unexecuted_test_files)
348
+ .filter { |file| spec_paths.include?(file) || unexecuted_test_files.include?(file) }
349
+
350
+ raise unless files.size == 3
351
+ end
352
+ end
353
+
354
+ def test_subset
355
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
356
+ false
357
+ end
358
+
359
+ spec = <<~SPEC
360
+ describe "KnapsackPro::Formatters::TimeTracker" do
361
+ it "works" do
362
+ sleep 0.1
363
+ expect(1).to eq 1
364
+ end
365
+ end
366
+ SPEC
367
+
368
+ run_specs(spec) do |spec_paths, times, time_tracker|
369
+ # Need to filter because RSpec keeps accumulating state.
370
+ files = time_tracker
371
+ .batch
372
+ .filter { |file| spec_paths.include?(file["path"]) }
373
+
374
+ raise unless files.size == 1
375
+ raise unless files[0]["path"] == spec_paths.first
376
+ raise unless files[0]["time_execution"] > 0.10
377
+ raise unless files[0]["time_execution"] < 0.15
378
+ end
379
+ end
380
+
381
+ private
382
+
383
+ def run_specs(specs)
384
+ files = Array(specs).map.with_index do |spec, i|
385
+ file = Tempfile.new(["time_tracker_#{i}", "_spec.rb"], "./spec/knapsack_pro/formatters/")
386
+ file.write(spec)
387
+ file.rewind
388
+ file
389
+ end
390
+
391
+ paths = files.map(&:path).map { _1.sub("./", "") }
392
+
393
+ options = ::RSpec::Core::ConfigurationOptions.new([
394
+ "--format", KnapsackPro::Formatters::TimeTracker.to_s,
395
+ *paths,
396
+ ])
397
+ runner = ::RSpec::Core::Runner.new(options)
398
+ runner.run(StringIO.new, StringIO.new)
399
+
400
+ time_tracker = runner.configuration.formatters.find { |f| f.class.to_s == KnapsackPro::Formatters::TimeTracker.to_s }
401
+ # Need to filter because RSpec keeps accumulating state.
402
+ times = time_tracker
403
+ .queue(paths)
404
+ .sort_by { |time| time["path"] }
405
+ .filter do |time|
406
+ paths.any? { |path| time["path"].start_with?(path) }
407
+ end
408
+ yield(paths, times, time_tracker)
409
+
410
+ ensure
411
+ # Need to reset because RSpec keeps reusing the same instance.
412
+ time_tracker.instance_variable_set(:@queue, {}) if time_tracker
413
+ time_tracker.instance_variable_set(:@started, time_tracker.send(:now)) if time_tracker
414
+ end
415
+ end
416
+
417
+ TestTimeTracker
418
+ .instance_methods
419
+ .filter { |method| method.to_s.start_with?("test_") }
420
+ .shuffle
421
+ .each do |method|
422
+ puts method
423
+ TestTimeTracker.new.public_send(method)
424
+ end
@@ -93,8 +93,8 @@ describe KnapsackPro::Hooks::Queue do
93
93
  let(:subset_queue_id) { double }
94
94
 
95
95
  before do
96
- expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
97
- expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).twice.and_return(subset_queue_id)
96
+ expect(KnapsackPro::Config::Env).to receive(:queue_id).at_least(:once).and_return(queue_id)
97
+ expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).at_least(:once).and_return(subset_queue_id)
98
98
 
99
99
  $expected_called_blocks = []
100
100
 
@@ -8,7 +8,7 @@ describe KnapsackPro::Presenter do
8
8
  expect(KnapsackPro).to receive(:tracker).and_return(tracker)
9
9
  end
10
10
 
11
- it { should eql "Global time execution for tests: 01h 02m 03s" }
11
+ it { should eql "Global test execution duration: 01h 02m 03s" }
12
12
  end
13
13
 
14
14
  describe '.pretty_seconds' do
@@ -0,0 +1,224 @@
1
+ require(KnapsackPro.root + '/lib/knapsack_pro/formatters/time_tracker')
2
+ require(KnapsackPro.root + '/lib/knapsack_pro/extensions/rspec_extension')
3
+
4
+ describe KnapsackPro::Pure::Queue::RSpecPure do
5
+ let(:rspec_pure) { described_class.new }
6
+
7
+ describe '#add_knapsack_pro_formatters_to' do
8
+ subject { rspec_pure.add_knapsack_pro_formatters_to(spec_opts) }
9
+
10
+ context 'when no spec_opts' do
11
+ let(:spec_opts) { nil }
12
+
13
+ it 'returns no spec_opts' do
14
+ expect(subject).to be nil
15
+ end
16
+ end
17
+
18
+ context 'when spec_opts have Knapsack Pro formatters' do
19
+ let(:spec_opts) { '--color --format d --format KnapsackPro::Formatters::TimeTracker' }
20
+
21
+ it 'returns spec_opts' do
22
+ expect(subject).to eq spec_opts
23
+ end
24
+ end
25
+
26
+ context 'when spec_opts have no Knapsack Pro formatters' do
27
+ let(:spec_opts) { '--color --format d' }
28
+
29
+ it 'returns spec_opts with added Knapsack Pro formatters' do
30
+ expect(subject).to eq '--color --format d --format KnapsackPro::Formatters::TimeTracker'
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#error_exit_code' do
36
+ subject { rspec_pure.error_exit_code(rspec_error_exit_code) }
37
+
38
+ context 'when RSpec has no defined error exit code' do
39
+ let(:rspec_error_exit_code) { nil }
40
+
41
+ it 'returns 1 as the default exit code' do
42
+ expect(subject).to eq 1
43
+ end
44
+ end
45
+
46
+ context 'when RSpec has a defined error exit code' do
47
+ let(:rspec_error_exit_code) { 2 }
48
+
49
+ it 'returns the custom exit code' do
50
+ expect(subject).to eq rspec_error_exit_code
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#args_with_seed_option_added_when_viable' do
56
+ let(:order_option) { KnapsackPro::Adapters::RSpecAdapter.order_option(args) }
57
+
58
+ subject { rspec_pure.args_with_seed_option_added_when_viable(order_option, seed, args) }
59
+
60
+ context 'when the order option is not random' do
61
+ let(:args) { ['--order', 'defined'] }
62
+ let(:seed) { KnapsackPro::Extensions::RSpecExtension::Seed.new(value: nil, used?: false) }
63
+
64
+ it 'does not add the seed option to args' do
65
+ expect(subject).to eq ['--order', 'defined']
66
+ end
67
+ end
68
+
69
+ ['random', 'rand'].each do |random_option_value|
70
+ context "when the order option is `#{random_option_value}`" do
71
+ let(:args) { ['--order', random_option_value] }
72
+
73
+ context 'when the seed is not used' do
74
+ let(:seed) { KnapsackPro::Extensions::RSpecExtension::Seed.new(value: '123', used?: false) }
75
+
76
+ it 'does not add the seed option to args' do
77
+ expect(subject).to eq ['--order', random_option_value]
78
+ end
79
+ end
80
+
81
+ context 'when the seed is used' do
82
+ let(:seed) { KnapsackPro::Extensions::RSpecExtension::Seed.new(value: '123', used?: true) }
83
+
84
+ it 'adds the seed option to args' do
85
+ expect(subject).to eq ['--order', random_option_value, '--seed', '123']
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ context 'when the order option is `rand:123`' do
92
+ let(:args) { ['--order', 'rand:123'] }
93
+ let(:seed) { KnapsackPro::Extensions::RSpecExtension::Seed.new(value: '123', used?: true) }
94
+
95
+ it 'does not add the seed option to args' do
96
+ expect(subject).to eq ['--order', 'rand:123']
97
+ end
98
+ end
99
+
100
+ context 'when the order option is not set in args AND seed is used' do
101
+ let(:args) { ['--format', 'documentation'] }
102
+ let(:seed) { KnapsackPro::Extensions::RSpecExtension::Seed.new(value: '123', used?: true) }
103
+
104
+ it 'adds the seed option to args' do
105
+ expect(subject).to eq ['--format', 'documentation', '--seed', '123']
106
+ end
107
+ end
108
+
109
+ context 'when the order option is not set in args AND seed is not used' do
110
+ let(:args) { ['--format', 'documentation'] }
111
+ let(:seed) { KnapsackPro::Extensions::RSpecExtension::Seed.new(value: '123', used?: false) }
112
+
113
+ it 'does not add the seed option to args' do
114
+ expect(subject).to eq ['--format', 'documentation']
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '#prepare_cli_args' do
120
+ subject { rspec_pure.prepare_cli_args(args, has_format_option, test_dir) }
121
+
122
+ context 'when no args' do
123
+ let(:args) { nil }
124
+ let(:has_format_option) { false }
125
+ let(:test_dir) { 'spec' }
126
+
127
+ it 'adds the default progress formatter and the default path and the time tracker formatter' do
128
+ expect(subject).to eq [
129
+ '--format', 'progress',
130
+ '--default-path', 'spec',
131
+ '--format', 'KnapsackPro::Formatters::TimeTracker',
132
+ ]
133
+ end
134
+ end
135
+
136
+ context 'when args are present and a custom test directory is set' do
137
+ let(:args) { '--color --profile' }
138
+ let(:has_format_option) { false }
139
+ let(:test_dir) { 'custom_spec_dir' }
140
+
141
+ it do
142
+ expect(subject).to eq [
143
+ '--color',
144
+ '--profile',
145
+ '--format', 'progress',
146
+ '--default-path', 'custom_spec_dir',
147
+ '--format', 'KnapsackPro::Formatters::TimeTracker',
148
+ ]
149
+ end
150
+ end
151
+
152
+ context 'when args are present and has format option' do
153
+ let(:args) { '--color --profile --format d' }
154
+ let(:has_format_option) { true }
155
+ let(:test_dir) { 'spec' }
156
+
157
+ it 'uses the format option from args instead of the default formatter' do
158
+ expect(subject).to eq [
159
+ '--color',
160
+ '--profile',
161
+ '--format', 'd',
162
+ '--default-path', 'spec',
163
+ '--format', 'KnapsackPro::Formatters::TimeTracker',
164
+ ]
165
+ end
166
+ end
167
+ end
168
+
169
+ describe '#rspec_command' do
170
+ let(:args) { ['--format', 'documentation'] }
171
+ let(:test_file_paths) { ['a_spec.rb', 'b_spec.rb'] }
172
+
173
+ subject { rspec_pure.rspec_command(args, test_file_paths, scope) }
174
+
175
+ context 'when there are no test file paths' do
176
+ let(:scope) { :queue_finished }
177
+ let(:test_file_paths) { [] }
178
+
179
+ it 'returns no messages' do
180
+ expect(subject).to eq []
181
+ end
182
+ end
183
+
184
+ context 'when a subset of queue (a batch of tests fetched from the Queue API)' do
185
+ let(:scope) { :batch_finished }
186
+
187
+ it 'returns messages with the RSpec command' do
188
+ expect(subject).to eq([
189
+ 'To retry the last batch of tests fetched from the Queue API, please run the following command on your machine:',
190
+ 'bundle exec rspec --format documentation "a_spec.rb" "b_spec.rb"',
191
+ ])
192
+ end
193
+ end
194
+
195
+ context 'when all tests fetched from the Queue API' do
196
+ let(:scope) { :queue_finished }
197
+
198
+ it 'returns messages with the RSpec command' do
199
+ expect(subject).to eq([
200
+ 'To retry all the tests assigned to this CI node, please run the following command on your machine:',
201
+ 'bundle exec rspec --format documentation "a_spec.rb" "b_spec.rb"',
202
+ ])
203
+ end
204
+ end
205
+
206
+ describe '#exit_summary' do
207
+ subject { rspec_pure.exit_summary(unexecuted_test_files) }
208
+
209
+ context 'when there are no unexecuted test files' do
210
+ let(:unexecuted_test_files) { [] }
211
+
212
+ it { expect(subject).to be_nil }
213
+ end
214
+
215
+ context 'when there are unexecuted test files' do
216
+ let(:unexecuted_test_files) { ['b_spec.rb', 'c_spec.rb'] }
217
+
218
+ it 'returns a warning message' do
219
+ expect(subject).to eq 'Unexecuted tests on this CI node (including pending tests): b_spec.rb c_spec.rb'
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end