parallel_tests 2.28.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests'
2
3
 
3
4
  module ParallelTests
4
5
  module Test
5
6
  class Runner
6
- NAME = 'Test'
7
-
8
7
  class << self
9
8
  # --- usually overwritten by other runners
10
9
 
11
- def name
12
- NAME
13
- end
14
-
15
10
  def runtime_log
16
11
  'tmp/parallel_runtime_test.log'
17
12
  end
@@ -20,6 +15,10 @@ module ParallelTests
20
15
  /_(test|spec).rb$/
21
16
  end
22
17
 
18
+ def default_test_folder
19
+ "test"
20
+ end
21
+
23
22
  def test_file_name
24
23
  "test"
25
24
  end
@@ -38,7 +37,7 @@ module ParallelTests
38
37
  # --- usually used by other runners
39
38
 
40
39
  # finds all tests and partitions them into groups
41
- def tests_in_groups(tests, num_groups, options={})
40
+ def tests_in_groups(tests, num_groups, options = {})
42
41
  tests = tests_with_size(tests, options)
43
42
  Grouper.in_even_groups_by_size(tests, num_groups, options)
44
43
  end
@@ -52,10 +51,17 @@ module ParallelTests
52
51
  when :filesize
53
52
  sort_by_filesize(tests)
54
53
  when :runtime
55
- sort_by_runtime(tests, runtimes(tests, options), options.merge(allowed_missing: (options[:allowed_missing_percent] || 50) / 100.0))
54
+ sort_by_runtime(
55
+ tests, runtimes(tests, options),
56
+ options.merge(allowed_missing: (options[:allowed_missing_percent] || 50) / 100.0)
57
+ )
56
58
  when nil
57
59
  # use recorded test runtime if we got enough data
58
- runtimes = runtimes(tests, options) rescue []
60
+ runtimes = begin
61
+ runtimes(tests, options)
62
+ rescue StandardError
63
+ []
64
+ end
59
65
  if runtimes.size * 1.5 > tests.size
60
66
  puts "Using recorded test runtime"
61
67
  sort_by_runtime(tests, runtimes)
@@ -73,12 +79,12 @@ module ParallelTests
73
79
  env = (options[:env] || {}).merge(
74
80
  "TEST_ENV_NUMBER" => test_env_number(process_number, options).to_s,
75
81
  "PARALLEL_TEST_GROUPS" => num_processes.to_s,
76
- "PARALLEL_PID_FILE" => ParallelTests.pid_file_path,
82
+ "PARALLEL_PID_FILE" => ParallelTests.pid_file_path
77
83
  )
78
84
  cmd = "nice #{cmd}" if options[:nice]
79
85
  cmd = "#{cmd} 2>&1" if options[:combine_stderr]
80
86
 
81
- puts cmd if options[:verbose] && !options[:serialize_stdout]
87
+ puts cmd if report_process_command?(options) && !options[:serialize_stdout]
82
88
 
83
89
  execute_command_and_capture_output(env, cmd, options)
84
90
  end
@@ -92,11 +98,11 @@ module ParallelTests
92
98
  end
93
99
  ParallelTests.pids.delete(pid) if pid
94
100
  exitstatus = $?.exitstatus
95
- seed = output[/seed (\d+)/,1]
101
+ seed = output[/seed (\d+)/, 1]
96
102
 
97
- output = [cmd, output].join("\n") if options[:verbose] && options[:serialize_stdout]
103
+ output = [cmd, output].join("\n") if report_process_command?(options) && options[:serialize_stdout]
98
104
 
99
- {:stdout => output, :exit_status => exitstatus, :command => cmd, :seed => seed}
105
+ { stdout: output, exit_status: exitstatus, command: cmd, seed: seed }
100
106
  end
101
107
 
102
108
  def find_results(test_output)
@@ -108,7 +114,7 @@ module ParallelTests
108
114
  end.compact
109
115
  end
110
116
 
111
- def test_env_number(process_number, options={})
117
+ def test_env_number(process_number, options = {})
112
118
  if process_number == 0 && !options[:first_is_1]
113
119
  ''
114
120
  else
@@ -118,7 +124,7 @@ module ParallelTests
118
124
 
119
125
  def summarize_results(results)
120
126
  sums = sum_up_results(results)
121
- sums.sort.map{|word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
127
+ sums.sort.map { |word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
122
128
  end
123
129
 
124
130
  # remove old seed and add new seed
@@ -138,19 +144,18 @@ module ParallelTests
138
144
  end
139
145
 
140
146
  def sum_up_results(results)
141
- results = results.join(' ').gsub(/s\b/,'') # combine and singularize results
147
+ results = results.join(' ').gsub(/s\b/, '') # combine and singularize results
142
148
  counts = results.scan(/(\d+) (\w+)/)
143
- counts.inject(Hash.new(0)) do |sum, (number, word)|
149
+ counts.each_with_object(Hash.new(0)) do |(number, word), sum|
144
150
  sum[word] += number.to_i
145
- sum
146
151
  end
147
152
  end
148
153
 
149
154
  # read output of the process and print it in chunks
150
- def capture_output(out, env, options={})
151
- result = ""
152
- loop do
153
- begin
155
+ def capture_output(out, env, options = {})
156
+ result = +""
157
+ begin
158
+ loop do
154
159
  read = out.readpartial(1000000) # read whatever chunk we can get
155
160
  if Encoding.default_internal
156
161
  read = read.force_encoding(Encoding.default_internal)
@@ -163,11 +168,13 @@ module ParallelTests
163
168
  $stdout.flush
164
169
  end
165
170
  end
166
- end rescue EOFError
171
+ rescue EOFError
172
+ nil
173
+ end
167
174
  result
168
175
  end
169
176
 
170
- def sort_by_runtime(tests, runtimes, options={})
177
+ def sort_by_runtime(tests, runtimes, options = {})
171
178
  allowed_missing = options[:allowed_missing] || 1.0
172
179
  allowed_missing = tests.size * allowed_missing
173
180
 
@@ -177,20 +184,14 @@ module ParallelTests
177
184
  allowed_missing -= 1 unless time = runtimes[test]
178
185
  if allowed_missing < 0
179
186
  log = options[:runtime_log] || runtime_log
180
- raise "Runtime log file '#{log}' does not contain sufficient data to sort #{tests.size} test files, please update it."
187
+ raise "Runtime log file '#{log}' does not contain sufficient data to sort #{tests.size} test files, please update or remove it."
181
188
  end
182
189
  [test, time]
183
190
  end
184
191
 
185
- if options[:verbose]
186
- puts "Runtime found for #{tests.count(&:last)} of #{tests.size} tests"
187
- end
192
+ puts "Runtime found for #{tests.count(&:last)} of #{tests.size} tests" if options[:verbose]
188
193
 
189
- # fill gaps with unknown-runtime if given, average otherwise
190
- known, unknown = tests.partition(&:last)
191
- average = (known.any? ? known.map!(&:last).inject(:+) / known.size : 1)
192
- unknown_runtime = options[:unknown_runtime] || average
193
- unknown.each { |set| set[1] = unknown_runtime }
194
+ set_unknown_runtime tests, options
194
195
  end
195
196
 
196
197
  def runtimes(tests, options)
@@ -198,7 +199,7 @@ module ParallelTests
198
199
  lines = File.read(log).split("\n")
199
200
  lines.each_with_object({}) do |line, times|
200
201
  test, _, time = line.rpartition(':')
201
- next unless test and time
202
+ next unless test && time
202
203
  times[test] = time.to_f if tests.include?(test)
203
204
  end
204
205
  end
@@ -225,7 +226,7 @@ module ParallelTests
225
226
  end.uniq
226
227
  end
227
228
 
228
- def files_in_folder(folder, options={})
229
+ def files_in_folder(folder, options = {})
229
230
  pattern = if options[:symlinks] == false # not nil or true
230
231
  "**/*"
231
232
  else
@@ -233,7 +234,23 @@ module ParallelTests
233
234
  # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
234
235
  "**{,/*/**}/*"
235
236
  end
236
- Dir[File.join(folder, pattern)].uniq
237
+ Dir[File.join(folder, pattern)].uniq.sort
238
+ end
239
+
240
+ private
241
+
242
+ # fill gaps with unknown-runtime if given, average otherwise
243
+ # NOTE: an optimization could be doing runtime by average runtime per file size, but would need file checks
244
+ def set_unknown_runtime(tests, options)
245
+ known, unknown = tests.partition(&:last)
246
+ return if unknown.empty?
247
+ unknown_runtime = options[:unknown_runtime] ||
248
+ (known.empty? ? 1 : known.map!(&:last).sum / known.size) # average
249
+ unknown.each { |set| set[1] = unknown_runtime }
250
+ end
251
+
252
+ def report_process_command?(options)
253
+ options[:verbose] || options[:verbose_process_command]
237
254
  end
238
255
  end
239
256
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests'
2
3
  require 'parallel_tests/test/runner'
3
4
 
@@ -22,7 +23,7 @@ module ParallelTests
22
23
  separator = "\n"
23
24
  groups = logfile.read.split(separator).map { |line| line.split(":") }.group_by(&:first)
24
25
  lines = groups.map do |file, times|
25
- time = "%.2f" % times.map(&:last).map(&:to_f).inject(:+)
26
+ time = "%.2f" % times.map(&:last).map(&:to_f).sum
26
27
  "#{file}:#{time}"
27
28
  end
28
29
  logfile.rewind
@@ -34,7 +35,7 @@ module ParallelTests
34
35
  private
35
36
 
36
37
  def with_locked_log
37
- File.open(logfile, File::RDWR|File::CREAT) do |logfile|
38
+ File.open(logfile, File::RDWR | File::CREAT) do |logfile|
38
39
  logfile.flock(File::LOCK_EX)
39
40
  yield logfile
40
41
  end
@@ -59,7 +60,7 @@ module ParallelTests
59
60
  end
60
61
 
61
62
  def message(test, delta)
62
- return unless method = test.public_instance_methods(true).detect { |method| method =~ /^test_/ }
63
+ return unless method = test.public_instance_methods(true).detect { |m| m =~ /^test_/ }
63
64
  filename = test.instance_method(method).source_location.first.sub("#{Dir.pwd}/", "")
64
65
  "#{filename}:#{delta}"
65
66
  end
@@ -74,22 +75,26 @@ end
74
75
 
75
76
  if defined?(Minitest::Runnable) # Minitest 5
76
77
  class << Minitest::Runnable
77
- prepend(Module.new do
78
- def run(*)
79
- ParallelTests::Test::RuntimeLogger.log_test_run(self) do
80
- super
78
+ prepend(
79
+ Module.new do
80
+ def run(*)
81
+ ParallelTests::Test::RuntimeLogger.log_test_run(self) do
82
+ super
83
+ end
81
84
  end
82
85
  end
83
- end)
86
+ )
84
87
  end
85
88
 
86
89
  class << Minitest
87
- prepend(Module.new do
88
- def run(*args)
89
- result = super
90
- ParallelTests::Test::RuntimeLogger.unique_log
91
- result
90
+ prepend(
91
+ Module.new do
92
+ def run(*args)
93
+ result = super
94
+ ParallelTests::Test::RuntimeLogger.unique_log
95
+ result
96
+ end
92
97
  end
93
- end)
98
+ )
94
99
  end
95
100
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ParallelTests
2
- VERSION = Version = '2.28.0'
3
+ VERSION = '3.7.0'
3
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.28.0
4
+ version: 3.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-07 00:00:00.000000000 Z
11
+ date: 2021-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parallel
@@ -42,6 +42,7 @@ files:
42
42
  - lib/parallel_tests.rb
43
43
  - lib/parallel_tests/cli.rb
44
44
  - lib/parallel_tests/cucumber/failures_logger.rb
45
+ - lib/parallel_tests/cucumber/features_with_steps.rb
45
46
  - lib/parallel_tests/cucumber/runner.rb
46
47
  - lib/parallel_tests/cucumber/scenario_line_logger.rb
47
48
  - lib/parallel_tests/cucumber/scenarios.rb
@@ -62,10 +63,14 @@ files:
62
63
  - lib/parallel_tests/test/runner.rb
63
64
  - lib/parallel_tests/test/runtime_logger.rb
64
65
  - lib/parallel_tests/version.rb
65
- homepage: http://github.com/grosser/parallel_tests
66
+ homepage: https://github.com/grosser/parallel_tests
66
67
  licenses:
67
68
  - MIT
68
- metadata: {}
69
+ metadata:
70
+ bug_tracker_uri: https://github.com/grosser/parallel_tests/issues
71
+ documentation_uri: https://github.com/grosser/parallel_tests/blob/v3.7.0/Readme.md
72
+ source_code_uri: https://github.com/grosser/parallel_tests/tree/v3.7.0
73
+ wiki_uri: https://github.com/grosser/parallel_tests/wiki
69
74
  post_install_message:
70
75
  rdoc_options: []
71
76
  require_paths:
@@ -74,15 +79,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
74
79
  requirements:
75
80
  - - ">="
76
81
  - !ruby/object:Gem::Version
77
- version: 2.2.0
82
+ version: 2.5.0
78
83
  required_rubygems_version: !ruby/object:Gem::Requirement
79
84
  requirements:
80
85
  - - ">="
81
86
  - !ruby/object:Gem::Version
82
87
  version: '0'
83
88
  requirements: []
84
- rubyforge_project:
85
- rubygems_version: 2.7.6
89
+ rubygems_version: 3.1.3
86
90
  signing_key:
87
91
  specification_version: 4
88
92
  summary: Run Test::Unit / RSpec / Cucumber / Spinach in parallel