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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa2ae33f8d962bbd8d5f1639e050f2a43e9ad28bf655d637d43ca2c23bc1d7d3
4
- data.tar.gz: 70e747f428235b65bba04ed2f748221170a6971e8982e0ab0fbbdb60177d9a94
3
+ metadata.gz: 315a4ff2f5885be68529cc0799affd8163fcad51558e4813a254b99bd057992a
4
+ data.tar.gz: 5709757ca30a22401be4dc170a926b16f5206d494d5b80c583bf03088d8570a6
5
5
  SHA512:
6
- metadata.gz: ed58c0b63af2f3301e7233c142da4165a9c2c9a4f31987d465a803d3b4fdab590137d2ef71bbf6b80b9abc95f513cea605091b5caa7fcd0598620790977cc29b
7
- data.tar.gz: 607af355f302481ca595f577428ea49565e711a790c284d7fb595f0f15106070a99e85acf4ebd5cf0e073e672ee37f9d851eb2b464c372dca550bf5c878c8050
6
+ metadata.gz: c43ee2474109d5e509fb57534857164ec0596cb018f61b44942ab261bdcb55345ac3025239fd3dde12c7d31f30841aa39aa480fb07bcab1519272788dad9c4b7
7
+ data.tar.gz: 9511b45fb2b0d59a309517ccc70ca10ebbb2b8a3cf0cfb2f2198e38a776838a402b531f64aea2ca94ce37489288dde8171b5fd36c45038014d781cba0fd5462d
data/.circleci/config.yml CHANGED
@@ -41,3 +41,5 @@ jobs:
41
41
  - run: rubocop -A --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment lib/
42
42
 
43
43
  - run: bundle exec rspec spec
44
+
45
+ - run: bundle exec ruby spec/knapsack_pro/formatters/time_tracker_specs.rb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ### 6.0.0
4
+
5
+ * __(breaking change)__ Dropped support for Turnip < 2.0.0
6
+ * Use an RSpec Formatter to track tests' execution times more accurately
7
+ * Removed `Time.raw_now`
8
+
9
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/229
10
+
11
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v5.7.0...UNRELEASED
12
+
13
+ ### 5.7.0
14
+
15
+ * Performance improvement: don't run `rake knapsack_pro:rspec_test_example_detector` when no slow test files are detected for RSpec.
16
+
17
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/225
18
+
19
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v5.6.0...v5.7.0
20
+
3
21
  ### 5.6.0
4
22
 
5
23
  * Use `frozen_string_literal: true` to reduce memory usage
data/README.md CHANGED
@@ -74,7 +74,7 @@ bundle update knapsack_pro
74
74
  RSpec:
75
75
 
76
76
  ```bash
77
- bundle exec rspec spec
77
+ bin/test
78
78
  ```
79
79
 
80
80
  Scripted tests can be found in the [Rails App With Knapsack Pro repository](https://github.com/KnapsackPro/rails-app-with-knapsack_pro/blob/master/bin/knapsack_pro_all.rb).
data/bin/test ADDED
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ bundle exec ruby spec/knapsack_pro/formatters/time_tracker_specs.rb
4
+ export FORMATTERS_EXIT_CODE=$?
5
+
6
+ bundle exec rspec spec
7
+ export RSPEC_EXIT_CODE=$?
8
+
9
+ if [ "$FORMATTERS_EXIT_CODE" -ne "0" ]; then
10
+ exit $FORMATTERS_EXIT_CODE
11
+ fi
12
+
13
+ if [ "$RSPEC_EXIT_CODE" -ne "0" ]; then
14
+ exit $RSPEC_EXIT_CODE
15
+ fi
data/knapsack_pro.gemspec CHANGED
@@ -4,10 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'knapsack_pro/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "knapsack_pro"
7
+ spec.name = 'knapsack_pro'
8
8
  spec.version = KnapsackPro::VERSION
9
9
  spec.authors = ['ArturT']
10
- spec.email = ['arturtrzop@gmail.com']
10
+ spec.email = ['support@knapsackpro.com']
11
11
  spec.summary = %q{Knapsack Pro splits tests across parallel CI nodes and ensures each parallel job finish work at a similar time.}
12
12
  spec.description = %q{Run tests in parallel across CI server nodes based on tests execution time. Split tests in a dynamic way to ensure parallel jobs are done at a similar time. Thanks to that your CI build time is as fast as possible. It works with many CI providers.}
13
13
  spec.homepage = 'https://knapsackpro.com'
@@ -15,15 +15,15 @@ Gem::Specification.new do |spec|
15
15
  spec.metadata = {
16
16
  'bug_tracker_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby/issues',
17
17
  'changelog_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby/blob/master/CHANGELOG.md',
18
- 'documentation_uri' => 'https://docs.knapsackpro.com/integration/',
18
+ 'documentation_uri' => 'https://docs.knapsackpro.com/knapsack_pro-ruby/guide/',
19
19
  'homepage_uri' => 'https://knapsackpro.com',
20
20
  'source_code_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby'
21
21
  }
22
22
 
23
23
  spec.files = `git ls-files -z`.split("\x0")
24
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
24
+ spec.executables = ['knapsack_pro']
25
25
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
26
- spec.require_paths = ["lib"]
26
+ spec.require_paths = ['lib']
27
27
 
28
28
  spec.add_dependency 'rake', '>= 0'
29
29
 
@@ -11,6 +11,14 @@ module KnapsackPro
11
11
  "#{KnapsackPro::Config::TempFiles::TEMP_DIRECTORY_PATH}/#{adapter_name}-bind_method_called_for_node_#{KnapsackPro::Config::Env.ci_node_index}.txt"
12
12
  end
13
13
 
14
+ def self.split_by_test_cases_enabled?
15
+ false
16
+ end
17
+
18
+ def self.test_file_cases_for(slow_test_files)
19
+ raise NotImplementedError
20
+ end
21
+
14
22
  def self.slow_test_file?(adapter_class, test_file_path)
15
23
  @slow_test_file_paths ||=
16
24
  begin
@@ -1,10 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../formatters/time_tracker_fetcher'
4
+
3
5
  module KnapsackPro
4
6
  module Adapters
5
7
  class RSpecAdapter < BaseAdapter
6
8
  TEST_DIR_PATTERN = 'spec/**{,/*/**}/*_spec.rb'
7
9
 
10
+ def self.split_by_test_cases_enabled?
11
+ return false unless KnapsackPro::Config::Env.rspec_split_by_test_examples?
12
+
13
+ require 'rspec/core/version'
14
+ unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
15
+ raise "RSpec >= 3.3.0 is required to split test files by test examples. Learn more: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
16
+ end
17
+
18
+ true
19
+ end
20
+
21
+ def self.test_file_cases_for(slow_test_files)
22
+ KnapsackPro.logger.info("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 #{slow_test_files.size} slow test files.")
23
+
24
+ # generate the RSpec JSON report in a separate process to not pollute the RSpec state
25
+ cmd = [
26
+ 'RACK_ENV=test',
27
+ 'RAILS_ENV=test',
28
+ KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
29
+ 'rake knapsack_pro:rspec_test_example_detector',
30
+ ].join(' ')
31
+ unless Kernel.system(cmd)
32
+ raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
33
+ end
34
+
35
+ # read the JSON report
36
+ KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.test_file_example_paths
37
+ end
38
+
8
39
  def self.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
9
40
  if KnapsackPro::Config::Env.rspec_split_by_test_examples? && has_tag_option?(cli_args)
10
41
  error_message = "It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: #{KnapsackPro::Urls::RSPEC__SPLIT_BY_TEST_EXAMPLES__TAG}"
@@ -25,63 +56,61 @@ module KnapsackPro
25
56
  parsed_options(cli_args)&.[](:order)
26
57
  end
27
58
 
28
- def self.test_path(example)
29
- example_group = example.metadata[:example_group]
30
-
31
- if defined?(::Turnip) && Gem::Version.new(::Turnip::VERSION) < Gem::Version.new('2.0.0')
32
- unless example_group[:turnip]
33
- until example_group[:parent_example_group].nil?
34
- example_group = example_group[:parent_example_group]
35
- end
59
+ def self.file_path_for(example)
60
+ [
61
+ -> { parse_file_path(example.id) },
62
+ -> { example.metadata[:file_path] },
63
+ -> { example.metadata[:example_group][:file_path] },
64
+ -> { top_level_group(example)[:file_path] },
65
+ ]
66
+ .each do |path|
67
+ p = path.call
68
+ return p if p.include?('_spec.rb')
36
69
  end
37
- else
38
- until example_group[:parent_example_group].nil?
39
- example_group = example_group[:parent_example_group]
40
- end
41
- end
42
70
 
43
- example_group[:file_path]
71
+ return ''
44
72
  end
45
73
 
46
- def bind_time_tracker
47
- ::RSpec.configure do |config|
48
- config.prepend_before(:context) do
49
- KnapsackPro.tracker.start_timer
50
- end
74
+ def self.parse_file_path(id)
75
+ # https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example.rb#L122
76
+ id.match(/\A(.*?)(?:\[([\d\s:,]+)\])?\z/).captures.first
77
+ end
51
78
 
52
- config.around(:each) do |example|
53
- current_test_path = KnapsackPro::Adapters::RSpecAdapter.test_path(example)
54
-
55
- # Stop timer to update time for a previously run test example.
56
- # This way we count time spent in runtime for the previous test example after around(:each) is already done.
57
- # Only do that if we're in the same test file. Otherwise, `before(:all)` execution time in the current file
58
- # will be applied to the previously ran test file.
59
- if KnapsackPro.tracker.current_test_path&.start_with?(KnapsackPro::TestFileCleaner.clean(current_test_path))
60
- KnapsackPro.tracker.stop_timer
61
- end
79
+ # private
80
+ def self.top_level_group(example)
81
+ group = example.metadata[:example_group]
82
+ until group[:parent_example_group].nil?
83
+ group = group[:parent_example_group]
84
+ end
85
+ group
86
+ end
62
87
 
63
- KnapsackPro.tracker.current_test_path =
64
- if KnapsackPro::Config::Env.rspec_split_by_test_examples? && KnapsackPro::Adapters::RSpecAdapter.slow_test_file?(RSpecAdapter, current_test_path)
65
- example.id
66
- else
67
- current_test_path
68
- end
88
+ def bind_time_tracker
89
+ ensure_no_focus!
90
+ log_batch_duration
91
+ end
69
92
 
93
+ def ensure_no_focus!
94
+ ::RSpec.configure do |config|
95
+ config.around(:each) do |example|
70
96
  if example.metadata[:focus] && KnapsackPro::Adapters::RSpecAdapter.rspec_configuration.filter.rules[:focus]
71
- raise "We detected a test file path #{current_test_path} with a test using the metadata `:focus` tag. RSpec might not run some tests in the Queue Mode (causing random tests skipping problem). Please remove the `:focus` tag from your codebase. See more: #{KnapsackPro::Urls::RSPEC__SKIPS_TESTS}"
97
+ file_path = KnapsackPro::Adapters::RSpecAdapter.file_path_for(example)
98
+ file_path = KnapsackPro::TestFileCleaner.clean(file_path)
99
+
100
+ raise "Knapsack Pro found an example tagged with focus in #{file_path}, please remove it. See more: #{KnapsackPro::Urls::RSPEC__SKIPS_TESTS}"
72
101
  end
73
102
 
74
103
  example.run
75
104
  end
105
+ end
106
+ end
76
107
 
77
- config.append_after(:context) do
78
- # after(:context) hook is run one time only, after all of the examples in a group
79
- # stop timer to count time for the very last executed test example
80
- KnapsackPro.tracker.stop_timer
81
- end
82
-
108
+ def log_batch_duration
109
+ ::RSpec.configure do |config|
83
110
  config.after(:suite) do
84
- KnapsackPro.logger.debug(KnapsackPro::Presenter.global_time)
111
+ time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
112
+ formatted = KnapsackPro::Presenter.global_time(time_tracker.batch_duration)
113
+ KnapsackPro.logger.debug(formatted)
85
114
  end
86
115
  end
87
116
  end
@@ -89,7 +118,8 @@ module KnapsackPro
89
118
  def bind_save_report
90
119
  ::RSpec.configure do |config|
91
120
  config.after(:suite) do
92
- KnapsackPro::Report.save
121
+ time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
122
+ KnapsackPro::Report.save(time_tracker.batch)
93
123
  end
94
124
  end
95
125
  end
@@ -35,35 +35,16 @@ module KnapsackPro
35
35
  def fast_and_slow_test_files_to_run
36
36
  test_files_to_run = all_test_files_to_run
37
37
 
38
- if adapter_class == KnapsackPro::Adapters::RSpecAdapter && KnapsackPro::Config::Env.rspec_split_by_test_examples?
39
- require 'rspec/core/version'
40
- unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
41
- raise "RSpec >= 3.3.0 is required to split test files by test examples. Learn more: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
42
- end
43
-
38
+ if adapter_class.split_by_test_cases_enabled?
44
39
  slow_test_files = get_slow_test_files
40
+ return test_files_to_run if slow_test_files.empty?
45
41
 
46
- KnapsackPro.logger.info("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 #{slow_test_files.size} slow test files.")
47
-
48
- # generate RSpec JSON report in separate process to not pollute RSpec state
49
- cmd = [
50
- 'RACK_ENV=test',
51
- 'RAILS_ENV=test',
52
- KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
53
- 'rake knapsack_pro:rspec_test_example_detector',
54
- ].join(' ')
55
- unless Kernel.system(cmd)
56
- raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
57
- end
42
+ test_file_cases = adapter_class.test_file_cases_for(slow_test_files)
58
43
 
59
- # read JSON report
60
- detector = KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new
61
- test_file_example_paths = detector.test_file_example_paths
62
-
63
- KnapsackPro::TestFilesWithTestCasesComposer.call(test_files_to_run, slow_test_files, test_file_example_paths)
64
- else
65
- test_files_to_run
44
+ return KnapsackPro::TestFilesWithTestCasesComposer.call(test_files_to_run, slow_test_files, test_file_cases)
66
45
  end
46
+
47
+ test_files_to_run
67
48
  end
68
49
 
69
50
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './time_tracker_fetcher'
4
+
3
5
  RSpec::Support.require_rspec_core('formatters/base_formatter')
4
6
  RSpec::Support.require_rspec_core('formatters/base_text_formatter')
5
7
 
@@ -76,11 +78,12 @@ module KnapsackPro
76
78
  registered_output.puts(most_recent_summary)
77
79
  end
78
80
 
79
- def self.print_exit_summary
81
+ def self.print_exit_summary(all_test_file_paths)
80
82
  registered_output.puts('Knapsack Pro Queue exited/aborted!')
81
83
  registered_output.puts('')
82
84
 
83
- unexecuted_test_files = KnapsackPro.tracker.unexecuted_test_files
85
+ time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
86
+ unexecuted_test_files = time_tracker&.unexecuted_test_files(all_test_file_paths) || []
84
87
  unless unexecuted_test_files.empty?
85
88
  registered_output.puts('Unexecuted tests on this CI node:')
86
89
  registered_output.puts(unexecuted_test_files)
@@ -119,7 +122,7 @@ module KnapsackPro
119
122
 
120
123
  def dump_summary(summary)
121
124
  colorizer = ::RSpec::Core::Formatters::ConsoleCodes
122
- duration = KnapsackPro.tracker.global_time_since_beginning
125
+ duration = KnapsackPro::Formatters::TimeTrackerFetcher.call.duration
123
126
  formatted_duration = ::RSpec::Core::Formatters::Helpers.format_duration(duration)
124
127
 
125
128
  formatted = "\nFinished in #{formatted_duration}\n" \
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KnapsackPro
4
+ module Formatters
5
+ class TimeTracker
6
+ ::RSpec::Core::Formatters.register self,
7
+ :example_group_started,
8
+ :example_started,
9
+ :example_finished,
10
+ :example_group_finished,
11
+ :stop
12
+
13
+ # Called at the beginning of each batch,
14
+ # but only the first instance of this class is used,
15
+ # so don't rely on the initializer to reset values.
16
+ def initialize(_output)
17
+ @time_each = nil
18
+ @time_all = nil
19
+ @before_all = 0.0
20
+ @group = {}
21
+ @batch = {}
22
+ @queue = {}
23
+ @suite_started = now
24
+ @batch_started = now
25
+ end
26
+
27
+ def example_group_started(notification)
28
+ return unless top_level_group?(notification.group)
29
+ @time_all = now
30
+ end
31
+
32
+ def example_started(_notification)
33
+ @before_all = now - @time_all if @before_all == 0.0
34
+ @time_each = now
35
+ end
36
+
37
+ def example_finished(notification)
38
+ record_example(@group, notification.example, @time_each)
39
+ @time_all = now
40
+ end
41
+
42
+ def example_group_finished(notification)
43
+ return unless top_level_group?(notification.group)
44
+
45
+ add_hooks_time(@group, @before_all, now - @time_all)
46
+ @batch = merge(@batch, @group)
47
+ @before_all = 0.0
48
+ @group = {}
49
+ end
50
+
51
+ # Called at the end of each batch
52
+ def stop(_notification)
53
+ @queue = merge(@queue, @batch)
54
+ @batch = {}
55
+ @batch_started = now
56
+ end
57
+
58
+ def queue(scheduled_paths)
59
+ recorded_paths = @queue.values.map do |example|
60
+ KnapsackPro::Adapters::RSpecAdapter.parse_file_path(example[:path])
61
+ end
62
+
63
+ missing = (scheduled_paths - recorded_paths).each_with_object({}) do |path, object|
64
+ object[path] = { path: path, time_execution: 0.0 }
65
+ end
66
+
67
+ merge(@queue, missing).values.map do |example|
68
+ example.transform_keys(&:to_s)
69
+ end
70
+ end
71
+
72
+ def batch
73
+ @batch.values.map do |example|
74
+ example.transform_keys(&:to_s)
75
+ end
76
+ end
77
+
78
+ def duration
79
+ now - @suite_started
80
+ end
81
+
82
+ def batch_duration
83
+ now - @batch_started
84
+ end
85
+
86
+ def unexecuted_test_files(scheduled_paths)
87
+ pending_paths = (@queue.values + @batch.values)
88
+ .filter { |example| example[:time_execution] == 0.0 }
89
+ .map { |example| example[:path] }
90
+
91
+ not_run_paths = scheduled_paths -
92
+ (@queue.values + @batch.values)
93
+ .map { |example| example[:path] }
94
+
95
+ pending_paths + not_run_paths
96
+ end
97
+
98
+ private
99
+
100
+ def top_level_group?(group)
101
+ group.metadata[:parent_example_group].nil?
102
+ end
103
+
104
+ def add_hooks_time(group, before_all, after_all)
105
+ group.each do |_, example|
106
+ next if example[:time_execution] == 0.0
107
+ example[:time_execution] += before_all + after_all
108
+ end
109
+ end
110
+
111
+ def record_example(accumulator, example, started_at)
112
+ path = path_for(example)
113
+ time_execution = time_execution_for(example, started_at)
114
+
115
+ if accumulator.key?(path)
116
+ accumulator[path][:time_execution] += time_execution
117
+ else
118
+ accumulator[path] = { path: path, time_execution: time_execution }
119
+ end
120
+ end
121
+
122
+ def path_for(example)
123
+ file = file_path_for(example)
124
+ return "UNKNOWN_PATH" if file == ""
125
+ path = rspec_split_by_test_example?(file) ? example.id : file
126
+ KnapsackPro::TestFileCleaner.clean(path)
127
+ end
128
+
129
+ def rspec_split_by_test_example?(file)
130
+ return false unless KnapsackPro::Config::Env.rspec_split_by_test_examples?
131
+ return false unless KnapsackPro::Adapters::RSpecAdapter.slow_test_file?(KnapsackPro::Adapters::RSpecAdapter, file)
132
+ true
133
+ end
134
+
135
+ def file_path_for(example)
136
+ KnapsackPro::Adapters::RSpecAdapter.file_path_for(example)
137
+ end
138
+
139
+ def time_execution_for(example, started_at)
140
+ if example.execution_result.status.to_s == "pending"
141
+ 0.0
142
+ else
143
+ (now - started_at).to_f
144
+ end
145
+ end
146
+
147
+ def merge(h1, h2)
148
+ h1.merge(h2) do |key, v1, v2|
149
+ {
150
+ path: key,
151
+ time_execution: v1[:time_execution] + v2[:time_execution]
152
+ }
153
+ end
154
+ end
155
+
156
+ def now
157
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,12 @@
1
+ module KnapsackPro
2
+ module Formatters
3
+ class TimeTrackerFetcher
4
+ def self.call
5
+ ::RSpec
6
+ .configuration
7
+ .formatters
8
+ .find { |f| f.class.to_s == "KnapsackPro::Formatters::TimeTracker" }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -3,8 +3,9 @@
3
3
  module KnapsackPro
4
4
  class Presenter
5
5
  class << self
6
- def global_time
7
- global_time = pretty_seconds(KnapsackPro.tracker.global_time)
6
+ def global_time(time = nil)
7
+ time = KnapsackPro.tracker.global_time if time.nil?
8
+ global_time = pretty_seconds(time)
8
9
  "Global time execution for tests: #{global_time}"
9
10
  end
10
11
 
@@ -2,15 +2,17 @@
2
2
 
3
3
  module KnapsackPro
4
4
  class Report
5
- def self.save
6
- test_files = KnapsackPro.tracker.to_a
5
+ def self.save(tests = nil)
6
+ if tests.nil?
7
+ tests = KnapsackPro.tracker.to_a
8
+ end
7
9
 
8
- if test_files.empty?
10
+ if tests.empty?
9
11
  KnapsackPro.logger.warn("No test files were executed on this CI node.")
10
12
  KnapsackPro.logger.debug("When you use knapsack_pro Regular Mode, the reason for no tests executing might be a very narrow tests list. Most likely, you run only tests with a specified tag, and there were fewer test files with the tag than parallel CI nodes.")
11
13
  end
12
14
 
13
- create_build_subset(test_files)
15
+ create_build_subset(tests)
14
16
  end
15
17
 
16
18
  def self.save_subset_queue_to_file
@@ -30,11 +32,13 @@ module KnapsackPro
30
32
  end
31
33
  end
32
34
 
33
- def self.save_node_queue_to_api
34
- test_files = []
35
- Dir.glob("#{queue_path}/*.json").each do |file|
36
- report = JSON.parse(File.read(file))
37
- test_files += report
35
+ def self.save_node_queue_to_api(test_files = nil)
36
+ if test_files.nil?
37
+ test_files = []
38
+ Dir.glob("#{queue_path}/*.json").each do |file|
39
+ report = JSON.parse(File.read(file))
40
+ test_files += report
41
+ end
38
42
  end
39
43
 
40
44
  if test_files.empty?