knapsack_pro 5.6.0 → 6.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.
@@ -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
@@ -282,15 +282,17 @@ describe KnapsackPro::Client::Connection do
282
282
  request_hash: request_hash)
283
283
  end
284
284
  let(:test_suite_token) { '3fa64859337f6e56409d49f865d13fd7' }
285
-
286
285
  let(:connection) { described_class.new(action) }
287
-
288
- before do
289
- stub_const('ENV', {
286
+ let(:headers) do
287
+ {
290
288
  'KNAPSACK_PRO_ENDPOINT' => 'http://api.knapsackpro.test:3000',
291
289
  'KNAPSACK_PRO_TEST_SUITE_TOKEN' => test_suite_token,
292
290
  'GITHUB_ACTIONS' => 'true',
293
- })
291
+ }
292
+ end
293
+
294
+ before do
295
+ stub_const('ENV', headers)
294
296
  end
295
297
 
296
298
  describe '#call' do
@@ -311,7 +313,7 @@ describe KnapsackPro::Client::Connection do
311
313
  expect(http).to receive(:read_timeout=).with(15)
312
314
  end
313
315
 
314
- context 'when http method is POST' do
316
+ context 'when http method is POST on GitHub Actions' do
315
317
  let(:http_method) { :post }
316
318
 
317
319
  before do
@@ -334,7 +336,30 @@ describe KnapsackPro::Client::Connection do
334
336
  end
335
337
  end
336
338
 
337
- context 'when http method is GET' do
339
+ context 'when http method is POST and CI is undetected' do
340
+ let(:http_method) { :post }
341
+
342
+ let(:headers) do
343
+ {
344
+ 'KNAPSACK_PRO_ENDPOINT' => 'http://api.knapsackpro.test:3000',
345
+ 'KNAPSACK_PRO_TEST_SUITE_TOKEN' => test_suite_token,
346
+ }
347
+ end
348
+
349
+ before do
350
+ expect(http).to receive(:post).with(
351
+ anything,
352
+ anything,
353
+ hash_not_including('KNAPSACK-PRO-CI-PROVIDER')
354
+ ).and_return(http_response)
355
+ end
356
+
357
+ it_behaves_like 'when request got response from API' do
358
+ let(:expected_http_method) { 'POST' }
359
+ end
360
+ end
361
+
362
+ context 'when http method is GET on GitHub Actions' do
338
363
  let(:http_method) { :get }
339
364
 
340
365
  before do
@@ -358,6 +383,28 @@ describe KnapsackPro::Client::Connection do
358
383
  end
359
384
  end
360
385
 
386
+ context 'when http method is GET and CI is undetected' do
387
+ let(:http_method) { :get }
388
+
389
+ let(:headers) do
390
+ {
391
+ 'KNAPSACK_PRO_ENDPOINT' => 'http://api.knapsackpro.test:3000',
392
+ 'KNAPSACK_PRO_TEST_SUITE_TOKEN' => test_suite_token,
393
+ }
394
+ end
395
+
396
+ before do
397
+ expect(http).to receive(:get).with(
398
+ anything,
399
+ hash_not_including('KNAPSACK-PRO-CI-PROVIDER')
400
+ ).and_return(http_response)
401
+ end
402
+
403
+ it_behaves_like 'when request got response from API' do
404
+ let(:expected_http_method) { 'GET' }
405
+ end
406
+ end
407
+
361
408
  context 'when retry request for http method POST' do
362
409
  let(:http_method) { :post }
363
410
 
@@ -0,0 +1,453 @@
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_relative '../../../lib/knapsack_pro/formatters/time_tracker'
8
+
9
+ class TestTimeTracker
10
+ def test_single_example
11
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
12
+ false
13
+ end
14
+
15
+ spec = <<~SPEC
16
+ describe "KnapsackPro::Formatters::TimeTracker" do
17
+ it do
18
+ sleep 0.1
19
+ expect(1).to eq 1
20
+ end
21
+ end
22
+ SPEC
23
+
24
+ run_specs(spec) do |spec_paths, times|
25
+ raise unless times.size == 1
26
+ raise unless times[0]["path"] == spec_paths.first
27
+ raise unless times[0]["time_execution"] > 0.10
28
+ raise unless times[0]["time_execution"] < 0.15
29
+ end
30
+ end
31
+
32
+ def test_two_files
33
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
34
+ false
35
+ end
36
+
37
+ spec_1 = <<~SPEC
38
+ describe "KnapsackPro::Formatters::TimeTracker 1" do
39
+ it do
40
+ sleep 0.1
41
+ expect(1).to eq 1
42
+ end
43
+ end
44
+ SPEC
45
+
46
+ spec_2 = <<~SPEC
47
+ describe "KnapsackPro::Formatters::TimeTracker 2" do
48
+ it do
49
+ sleep 0.2
50
+ expect(1).to eq 1
51
+ end
52
+ end
53
+ SPEC
54
+
55
+ run_specs([spec_1, spec_2]) do |spec_paths, times|
56
+ raise unless times.size == 2
57
+ raise unless times.first["path"] == spec_paths.first
58
+ raise unless times.first["time_execution"] > 0.10
59
+ raise unless times.first["time_execution"] < 0.15
60
+ raise unless times.last["path"] == spec_paths.last
61
+ raise unless times.last["time_execution"] > 0.20
62
+ raise unless times.last["time_execution"] < 0.25
63
+ end
64
+ end
65
+
66
+ def test_failing_example
67
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
68
+ false
69
+ end
70
+
71
+ spec = <<~SPEC
72
+ describe "KnapsackPro::Formatters::TimeTracker" do
73
+ it do
74
+ sleep 0.1
75
+ expect(1).to eq 2
76
+ end
77
+ end
78
+ SPEC
79
+
80
+ run_specs(spec) do |spec_paths, times|
81
+ raise unless times.size == 1
82
+ raise unless times[0]["path"] == spec_paths.first
83
+ raise unless times[0]["time_execution"] > 0.10
84
+ raise unless times[0]["time_execution"] < 0.15
85
+ end
86
+ end
87
+
88
+ def test_pending_example
89
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
90
+ false
91
+ end
92
+
93
+ spec = <<~SPEC
94
+ describe "KnapsackPro::Formatters::TimeTracker" do
95
+ xit do
96
+ sleep 0.1
97
+ expect(1).to eq 2
98
+ end
99
+ end
100
+ SPEC
101
+
102
+ run_specs(spec) do |spec_paths, times|
103
+ raise unless times.size == 1
104
+ raise unless times[0]["path"] == spec_paths.first
105
+ raise unless times[0]["time_execution"] == 0.0
106
+ end
107
+ end
108
+
109
+ def test_multiple_top_level_groups
110
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
111
+ false
112
+ end
113
+
114
+ spec = <<~SPEC
115
+ describe "KnapsackPro::Formatters::TimeTracker 1" do
116
+ it do
117
+ sleep 0.1
118
+ expect(1).to eq 1
119
+ end
120
+ end
121
+
122
+ describe "KnapsackPro::Formatters::TimeTracker 2" do
123
+ it do
124
+ sleep 0.2
125
+ expect(1).to eq 1
126
+ end
127
+ end
128
+ SPEC
129
+
130
+ run_specs(spec) do |spec_paths, times|
131
+ raise unless times.size == 1
132
+ raise unless times[0]["path"] == spec_paths.first
133
+ raise unless times[0]["time_execution"] > 0.30
134
+ raise unless times[0]["time_execution"] < 0.35
135
+ end
136
+ end
137
+
138
+ def test_rspec_split_by_test_example
139
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
140
+ true
141
+ end
142
+
143
+ spec = <<~SPEC
144
+ describe "KnapsackPro::Formatters::TimeTracker 1" do
145
+ it do
146
+ expect(1).to eq 1
147
+ end
148
+
149
+ it do
150
+ sleep 0.1
151
+ expect(1).to eq 1
152
+ end
153
+ end
154
+
155
+ describe "KnapsackPro::Formatters::TimeTracker 2" do
156
+ it do
157
+ sleep 0.2
158
+ expect(1).to eq 1
159
+ end
160
+
161
+ it do
162
+ sleep 0.3
163
+ expect(1).to eq 1
164
+ end
165
+ end
166
+ SPEC
167
+
168
+ run_specs(spec) do |spec_paths, times|
169
+ raise unless times.size == 4
170
+ spec_path = spec_paths.first
171
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:1]" }["time_execution"] < 0.05
172
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] > 0.10
173
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] < 0.15
174
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:1]" }["time_execution"] > 0.20
175
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:1]" }["time_execution"] < 0.25
176
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:2]" }["time_execution"] > 0.30
177
+ raise unless times.find { |time| time["path"] == "#{spec_path}[2:2]" }["time_execution"] < 0.35
178
+ end
179
+ end
180
+
181
+ def test_hooks
182
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
183
+ false
184
+ end
185
+
186
+ spec = <<~SPEC
187
+ describe "KnapsackPro::Formatters::TimeTracker" do
188
+ before(:all) do
189
+ sleep 0.1
190
+ end
191
+
192
+ before(:each) do
193
+ sleep 0.1
194
+ end
195
+
196
+ after(:each) do
197
+ sleep 0.1
198
+ end
199
+
200
+ it do
201
+ expect(1).to eq 1
202
+ end
203
+
204
+ it do
205
+ expect(1).to eq 1
206
+ end
207
+
208
+ after(:all) do
209
+ sleep 0.1
210
+ end
211
+ end
212
+ SPEC
213
+
214
+ run_specs(spec) do |spec_paths, times|
215
+ raise unless times.size == 1
216
+ raise unless times[0]["path"] == spec_paths.first
217
+ raise unless times[0]["time_execution"] > 0.60
218
+ raise unless times[0]["time_execution"] < 0.65
219
+ end
220
+ end
221
+
222
+ def test_hooks_with_rspec_split_by_test_example
223
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
224
+ true
225
+ end
226
+
227
+ spec = <<~SPEC
228
+ describe "KnapsackPro::Formatters::TimeTracker" do
229
+ before(:all) do
230
+ sleep 0.1
231
+ end
232
+
233
+ before(:each) do
234
+ sleep 0.1
235
+ end
236
+
237
+ after(:each) do
238
+ sleep 0.1
239
+ end
240
+
241
+ it do
242
+ expect(1).to eq 1
243
+ end
244
+
245
+ it do
246
+ expect(1).to eq 1
247
+ end
248
+
249
+ after(:all) do
250
+ sleep 0.1
251
+ end
252
+ end
253
+ SPEC
254
+
255
+ run_specs(spec) do |spec_paths, times|
256
+ raise unless times.size == 2
257
+ spec_path = spec_paths.first
258
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:1]" }["time_execution"] > 0.40
259
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:1]" }["time_execution"] < 0.45
260
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] > 0.40
261
+ raise unless times.find { |time| time["path"] == "#{spec_path}[1:2]" }["time_execution"] < 0.45
262
+ end
263
+ end
264
+
265
+ def test_unknown_path
266
+ KnapsackPro::Formatters::TimeTracker.class_eval do
267
+ alias_method :original_file_path_for, :file_path_for
268
+
269
+ define_method(:file_path_for) do |_example|
270
+ ""
271
+ end
272
+ end
273
+
274
+ spec = <<~SPEC
275
+ describe "KnapsackPro::Formatters::TimeTracker" do
276
+ it do
277
+ expect(1).to eq 1
278
+ end
279
+ end
280
+ SPEC
281
+
282
+ run_specs(spec) do |spec_paths, times|
283
+ raise unless times.size == 2
284
+ raise unless times[0]["path"] == "UNKNOWN_PATH"
285
+ raise unless times[1]["path"] == spec_paths.first
286
+ end
287
+
288
+ ensure
289
+ KnapsackPro::Formatters::TimeTracker.class_eval do
290
+ undef :file_path_for
291
+ alias_method :file_path_for, :original_file_path_for
292
+ end
293
+ end
294
+
295
+ def test_empty_group
296
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
297
+ false
298
+ end
299
+
300
+ spec = <<~SPEC
301
+ describe "KnapsackPro::Formatters::TimeTracker" do
302
+ end
303
+ SPEC
304
+
305
+ run_specs(spec) do |spec_paths, times|
306
+ raise unless times.size == 1
307
+ raise unless times[0]["path"] == spec_paths.first
308
+ raise unless times[0]["time_execution"] == 0.0
309
+ end
310
+ end
311
+
312
+ def test_duration
313
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
314
+ false
315
+ end
316
+
317
+ spec = <<~SPEC
318
+ describe "KnapsackPro::Formatters::TimeTracker" do
319
+ it do
320
+ expect(1).to eq 1
321
+ end
322
+ end
323
+ SPEC
324
+
325
+ run_specs(spec) do |_, _, time_tracker|
326
+ raise unless time_tracker.duration > 0.0
327
+ end
328
+ end
329
+
330
+ def test_batch_duration
331
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
332
+ false
333
+ end
334
+
335
+ spec = <<~SPEC
336
+ describe "KnapsackPro::Formatters::TimeTracker" do
337
+ it do
338
+ expect(1).to eq 1
339
+ end
340
+ end
341
+ SPEC
342
+
343
+ run_specs(spec) do |_, _, time_tracker|
344
+ raise unless time_tracker.batch_duration > 0.0
345
+ end
346
+ end
347
+
348
+ def test_unexecuted_test_files
349
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
350
+ false
351
+ end
352
+
353
+ spec = <<~SPEC
354
+ describe "KnapsackPro::Formatters::TimeTracker" do
355
+ xit do
356
+ end
357
+ end
358
+ SPEC
359
+
360
+ run_specs(spec) do |spec_paths, _, time_tracker|
361
+ unexecuted_test_files = ["foo_spec.rb", "bar_spec.rb"]
362
+ # Need to filter because RSpec keeps accumulating state.
363
+ files = time_tracker
364
+ .unexecuted_test_files(spec_paths + unexecuted_test_files)
365
+ .filter { |file| spec_paths.include?(file) || unexecuted_test_files.include?(file) }
366
+
367
+ raise unless files.size == 3
368
+ end
369
+ end
370
+
371
+ def test_subset
372
+ KnapsackPro::Formatters::TimeTracker.define_method(:rspec_split_by_test_example?) do |_file|
373
+ false
374
+ end
375
+ KnapsackPro::Formatters::TimeTracker.class_eval do
376
+ alias_method :original_stop, :stop
377
+
378
+ # In Regular Mode, #subset is called before #stop.
379
+ # This test makes #stop a noop to simulate that behavior.
380
+ define_method(:stop) do |_|
381
+ end
382
+ end
383
+
384
+ spec = <<~SPEC
385
+ describe "KnapsackPro::Formatters::TimeTracker" do
386
+ it "works" do
387
+ sleep 0.1
388
+ expect(1).to eq 1
389
+ end
390
+ end
391
+ SPEC
392
+
393
+ run_specs(spec) do |spec_paths, times, time_tracker|
394
+ # Need to filter because RSpec keeps accumulating state.
395
+ files = time_tracker
396
+ .batch
397
+ .filter { |file| spec_paths.include?(file["path"]) }
398
+
399
+ raise unless files.size == 1
400
+ raise unless files[0]["path"] == spec_paths.first
401
+ raise unless files[0]["time_execution"] > 0.10
402
+ raise unless files[0]["time_execution"] < 0.15
403
+ end
404
+
405
+ ensure
406
+ KnapsackPro::Formatters::TimeTracker.class_eval do
407
+ undef :stop
408
+ alias_method :stop, :original_stop
409
+ end
410
+ end
411
+
412
+ private
413
+
414
+ def run_specs(specs)
415
+ paths = Array(specs).map.with_index do |spec, i|
416
+ path = "spec/knapsack_pro/formatters/#{i}_#{SecureRandom.uuid}_spec.rb"
417
+ File.open(path, 'w') { |file| file.write(spec) }
418
+ path
419
+ end
420
+
421
+ options = ::RSpec::Core::ConfigurationOptions.new([
422
+ "--format", KnapsackPro::Formatters::TimeTracker.to_s,
423
+ *paths,
424
+ ])
425
+ runner = ::RSpec::Core::Runner.new(options)
426
+ runner.run(StringIO.new, StringIO.new)
427
+
428
+ time_tracker = runner.configuration.formatters.find { |f| f.class.to_s == KnapsackPro::Formatters::TimeTracker.to_s }
429
+ # Need to filter because RSpec keeps accumulating state.
430
+ times = time_tracker
431
+ .queue(paths)
432
+ .sort_by { |time| time["path"] }
433
+ .filter do |time|
434
+ paths.any? { |path| time["path"].start_with?(path) || time["path"] == "UNKNOWN_PATH" }
435
+ end
436
+ yield(paths, times, time_tracker)
437
+
438
+ ensure
439
+ paths.each { |path| File.delete(path) }
440
+ # Need to reset because RSpec keeps reusing the same instance.
441
+ time_tracker.instance_variable_set(:@queue, {}) if time_tracker
442
+ time_tracker.instance_variable_set(:@started, time_tracker.send(:now)) if time_tracker
443
+ end
444
+ end
445
+
446
+ TestTimeTracker
447
+ .instance_methods
448
+ .filter { |method| method.to_s.start_with?("test_") }
449
+ .shuffle
450
+ .each do |method|
451
+ puts method
452
+ TestTimeTracker.new.public_send(method)
453
+ end