parallel_tests 3.2.0 → 3.5.2

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.
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests/rspec/logger_base'
2
3
  require 'parallel_tests/rspec/runner'
3
4
 
4
5
  class ParallelTests::RSpec::FailuresLogger < ParallelTests::RSpec::LoggerBase
5
6
  if RSPEC_2
6
- def dump_failures(*args)
7
- end
7
+ def dump_failures(*args); end
8
8
  else
9
9
  RSpec::Core::Formatters.register self, :dump_summary
10
10
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ParallelTests
2
3
  module RSpec
3
4
  end
@@ -13,26 +14,27 @@ class ParallelTests::RSpec::LoggerBase < RSpec::Core::Formatters::BaseTextFormat
13
14
 
14
15
  @output ||= args[0]
15
16
 
16
- if String === @output # a path ?
17
+ case @output
18
+ when String # a path ?
17
19
  FileUtils.mkdir_p(File.dirname(@output))
18
- File.open(@output, 'w'){} # overwrite previous results
20
+ File.open(@output, 'w') {} # overwrite previous results
19
21
  @output = File.open(@output, 'a')
20
- elsif File === @output # close and restart in append mode
22
+ when File # close and restart in append mode
21
23
  @output.close
22
24
  @output = File.open(@output.path, 'a')
23
25
  end
24
26
  end
25
27
 
26
- #stolen from Rspec
27
- def close(*args)
28
- @output.close if (IO === @output) & (@output != $stdout)
28
+ # stolen from Rspec
29
+ def close(*)
30
+ @output.close if (IO === @output) & (@output != $stdout)
29
31
  end
30
32
 
31
33
  protected
32
34
 
33
35
  # do not let multiple processes get in each others way
34
36
  def lock_output
35
- if File === @output
37
+ if @output.is_a?(File)
36
38
  begin
37
39
  @output.flock File::LOCK_EX
38
40
  yield
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "parallel_tests/test/runner"
2
3
 
3
4
  module ParallelTests
@@ -12,10 +13,9 @@ module ParallelTests
12
13
  end
13
14
 
14
15
  def determine_executable
15
- case
16
- when File.exist?("bin/rspec")
16
+ if File.exist?("bin/rspec")
17
17
  ParallelTests.with_ruby_binary("bin/rspec")
18
- when ParallelTests.bundler_enabled?
18
+ elsif ParallelTests.bundler_enabled?
19
19
  "bundle exec rspec"
20
20
  else
21
21
  "rspec"
@@ -23,7 +23,7 @@ module ParallelTests
23
23
  end
24
24
 
25
25
  def runtime_log
26
- 'tmp/parallel_runtime_rspec.log'
26
+ "tmp/parallel_runtime_rspec.log"
27
27
  end
28
28
 
29
29
  def test_file_name
@@ -48,6 +48,22 @@ module ParallelTests
48
48
  "#{clean} --seed #{seed}"
49
49
  end
50
50
 
51
+ # Summarize results from threads and colorize results based on failure and pending counts.
52
+ #
53
+ def summarize_results(results)
54
+ text = super
55
+ return text unless $stdout.tty?
56
+ sums = sum_up_results(results)
57
+ color =
58
+ if sums['failure'] > 0
59
+ 31 # red
60
+ elsif sums['pending'] > 0
61
+ 33 # yellow
62
+ else
63
+ 32 # green
64
+ end
65
+ "\e[#{color}m#{text}\e[0m"
66
+ end
51
67
 
52
68
  private
53
69
 
@@ -61,7 +77,7 @@ module ParallelTests
61
77
  end
62
78
 
63
79
  def spec_opts
64
- options_file = ['.rspec_parallel', 'spec/parallel_spec.opts', 'spec/spec.opts'].detect{|f| File.file?(f) }
80
+ options_file = ['.rspec_parallel', 'spec/parallel_spec.opts', 'spec/spec.opts'].detect { |f| File.file?(f) }
65
81
  return unless options_file
66
82
  "-O #{options_file}"
67
83
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests'
2
3
  require 'parallel_tests/rspec/logger_base'
3
4
 
@@ -8,9 +9,7 @@ class ParallelTests::RSpec::RuntimeLogger < ParallelTests::RSpec::LoggerBase
8
9
  @group_nesting = 0
9
10
  end
10
11
 
11
- unless RSPEC_2
12
- RSpec::Core::Formatters.register self, :example_group_started, :example_group_finished, :start_dump
13
- end
12
+ RSpec::Core::Formatters.register self, :example_group_started, :example_group_finished, :start_dump unless RSPEC_2
14
13
 
15
14
  def example_group_started(example_group)
16
15
  @time = ParallelTests.now if @group_nesting == 0
@@ -27,16 +26,19 @@ class ParallelTests::RSpec::RuntimeLogger < ParallelTests::RSpec::LoggerBase
27
26
  super if defined?(super)
28
27
  end
29
28
 
30
- def dump_summary(*args);end
31
- def dump_failures(*args);end
32
- def dump_failure(*args);end
33
- def dump_pending(*args);end
29
+ def dump_summary(*); end
30
+
31
+ def dump_failures(*); end
32
+
33
+ def dump_failure(*); end
34
+
35
+ def dump_pending(*); end
34
36
 
35
- def start_dump(*args)
36
- return unless ENV['TEST_ENV_NUMBER'] #only record when running in parallel
37
+ def start_dump(*)
38
+ return unless ENV['TEST_ENV_NUMBER'] # only record when running in parallel
37
39
  lock_output do
38
40
  @example_times.each do |file, time|
39
- relative_path = file.sub(/^#{Regexp.escape Dir.pwd}\//,'').sub(/^\.\//, "")
41
+ relative_path = file.sub(%r{^#{Regexp.escape Dir.pwd}/}, '').sub(%r{^\./}, "")
40
42
  @output.puts "#{relative_path}:#{time > 0 ? time : 0}"
41
43
  end
42
44
  end
@@ -1,9 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests/rspec/failures_logger'
2
3
 
3
4
  class ParallelTests::RSpec::SummaryLogger < ParallelTests::RSpec::LoggerBase
4
- unless RSPEC_2
5
- RSpec::Core::Formatters.register self, :dump_failures
6
- end
5
+ RSpec::Core::Formatters.register self, :dump_failures unless RSPEC_2
7
6
 
8
7
  def dump_failures(*args)
9
8
  lock_output { super }
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "parallel_tests/gherkin/runner"
2
3
 
3
4
  module ParallelTests
@@ -9,10 +10,9 @@ module ParallelTests
9
10
  end
10
11
 
11
12
  def runtime_logging
12
- #Not Yet Supported
13
+ # Not Yet Supported
13
14
  ""
14
15
  end
15
-
16
16
  end
17
17
  end
18
18
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rake'
2
3
  require 'shellwords'
3
4
 
@@ -27,12 +28,13 @@ module ParallelTests
27
28
  end
28
29
  end
29
30
 
30
- def run_in_parallel(cmd, options={})
31
+ def run_in_parallel(cmd, options = {})
31
32
  load_lib
32
33
  count = " -n #{options[:count]}" unless options[:count].to_s.empty?
33
34
  # Using the relative path to find the binary allow to run a specific version of it
34
- executable = File.expand_path("../../../bin/parallel_test", __FILE__)
35
- command = "#{ParallelTests.with_ruby_binary(Shellwords.escape(executable))} --exec '#{cmd}'#{count}#{' --non-parallel' if options[:non_parallel]}"
35
+ executable = File.expand_path('../../bin/parallel_test', __dir__)
36
+ non_parallel = (options[:non_parallel] ? ' --non-parallel' : '')
37
+ command = "#{ParallelTests.with_ruby_binary(Shellwords.escape(executable))} --exec '#{cmd}'#{count}#{non_parallel}"
36
38
  abort unless system(command)
37
39
  end
38
40
 
@@ -50,12 +52,12 @@ module ParallelTests
50
52
  # - simple system "set -o pipefail" returns nil even though set -o pipefail exists with 0
51
53
  def suppress_output(command, ignore_regex)
52
54
  activate_pipefail = "set -o pipefail"
53
- remove_ignored_lines = %Q{(grep -v "#{ignore_regex}" || test 1)}
55
+ remove_ignored_lines = %{(grep -v "#{ignore_regex}" || test 1)}
54
56
 
55
57
  if File.executable?('/bin/bash') && system('/bin/bash', '-c', "#{activate_pipefail} 2>/dev/null && test 1")
56
58
  # We need to shell escape single quotes (' becomes '"'"') because
57
59
  # run_in_parallel wraps command in single quotes
58
- %Q{/bin/bash -c '"'"'#{activate_pipefail} && (#{command}) | #{remove_ignored_lines}'"'"'}
60
+ %{/bin/bash -c '"'"'#{activate_pipefail} && (#{command}) | #{remove_ignored_lines}'"'"'}
59
61
  else
60
62
  command
61
63
  end
@@ -83,7 +85,7 @@ module ParallelTests
83
85
  # parallel:spec[2,models,options]
84
86
  # parallel:spec[,models,options]
85
87
  count = args.shift if args.first.to_s =~ /^\d*$/
86
- num_processes = count.to_i unless count.to_s.empty?
88
+ num_processes = (count.to_s.empty? ? nil : Integer(count))
87
89
  pattern = args.shift
88
90
  options = args.shift
89
91
  pass_through = args.shift
@@ -96,30 +98,38 @@ end
96
98
 
97
99
  namespace :parallel do
98
100
  desc "Setup test databases via db:setup --> parallel:setup[num_cpus]"
99
- task :setup, :count do |_,args|
101
+ task :setup, :count do |_, args|
100
102
  command = "#{ParallelTests::Tasks.rake_bin} db:setup RAILS_ENV=#{ParallelTests::Tasks.rails_env}"
101
103
  ParallelTests::Tasks.run_in_parallel(ParallelTests::Tasks.suppress_schema_load_output(command), args)
102
104
  end
103
105
 
104
106
  desc "Create test databases via db:create --> parallel:create[num_cpus]"
105
- task :create, :count do |_,args|
107
+ task :create, :count do |_, args|
106
108
  ParallelTests::Tasks.run_in_parallel(
107
- "#{ParallelTests::Tasks.rake_bin} db:create RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args)
109
+ "#{ParallelTests::Tasks.rake_bin} db:create RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
110
+ )
108
111
  end
109
112
 
110
113
  desc "Drop test databases via db:drop --> parallel:drop[num_cpus]"
111
- task :drop, :count do |_,args|
114
+ task :drop, :count do |_, args|
112
115
  ParallelTests::Tasks.run_in_parallel(
113
116
  "#{ParallelTests::Tasks.rake_bin} db:drop RAILS_ENV=#{ParallelTests::Tasks.rails_env} " \
114
- "DISABLE_DATABASE_ENVIRONMENT_CHECK=1", args)
117
+ "DISABLE_DATABASE_ENVIRONMENT_CHECK=1", args
118
+ )
115
119
  end
116
120
 
117
121
  desc "Update test databases by dumping and loading --> parallel:prepare[num_cpus]"
118
- task(:prepare, [:count]) do |_,args|
122
+ task(:prepare, [:count]) do |_, args|
119
123
  ParallelTests::Tasks.check_for_pending_migrations
120
124
  if defined?(ActiveRecord::Base) && [:ruby, :sql].include?(ActiveRecord::Base.schema_format)
121
125
  # fast: dump once, load in parallel
122
- type = (ActiveRecord::Base.schema_format == :ruby ? "schema" : "structure")
126
+ type =
127
+ if Gem::Version.new(Rails.version) >= Gem::Version.new('6.1.0')
128
+ "schema"
129
+ else
130
+ ActiveRecord::Base.schema_format == :ruby ? "schema" : "structure"
131
+ end
132
+
123
133
  Rake::Task["db:#{type}:dump"].invoke
124
134
 
125
135
  # remove database connection to prevent "database is being accessed by other users"
@@ -128,7 +138,7 @@ namespace :parallel do
128
138
  Rake::Task["parallel:load_#{type}"].invoke(args[:count])
129
139
  else
130
140
  # slow: dump and load in in serial
131
- args = args.to_hash.merge(:non_parallel => true) # normal merge returns nil
141
+ args = args.to_hash.merge(non_parallel: true) # normal merge returns nil
132
142
  task_name = Rake::Task.task_defined?('db:test:prepare') ? 'db:test:prepare' : 'app:db:test:prepare'
133
143
  ParallelTests::Tasks.run_in_parallel("#{ParallelTests::Tasks.rake_bin} #{task_name}", args)
134
144
  next
@@ -137,49 +147,55 @@ namespace :parallel do
137
147
 
138
148
  # when dumping/resetting takes too long
139
149
  desc "Update test databases via db:migrate --> parallel:migrate[num_cpus]"
140
- task :migrate, :count do |_,args|
150
+ task :migrate, :count do |_, args|
141
151
  ParallelTests::Tasks.run_in_parallel(
142
- "#{ParallelTests::Tasks.rake_bin} db:migrate RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args)
152
+ "#{ParallelTests::Tasks.rake_bin} db:migrate RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
153
+ )
143
154
  end
144
155
 
145
156
  desc "Rollback test databases via db:rollback --> parallel:rollback[num_cpus]"
146
- task :rollback, :count do |_,args|
157
+ task :rollback, :count do |_, args|
147
158
  ParallelTests::Tasks.run_in_parallel(
148
- "#{ParallelTests::Tasks.rake_bin} db:rollback RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args)
159
+ "#{ParallelTests::Tasks.rake_bin} db:rollback RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
160
+ )
149
161
  end
150
162
 
151
163
  # just load the schema (good for integration server <-> no development db)
152
164
  desc "Load dumped schema for test databases via db:schema:load --> parallel:load_schema[num_cpus]"
153
- task :load_schema, :count do |_,args|
165
+ task :load_schema, :count do |_, args|
154
166
  command = "#{ParallelTests::Tasks.rake_bin} #{ParallelTests::Tasks.purge_before_load} " \
155
167
  "db:schema:load RAILS_ENV=#{ParallelTests::Tasks.rails_env} DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
156
168
  ParallelTests::Tasks.run_in_parallel(ParallelTests::Tasks.suppress_schema_load_output(command), args)
157
169
  end
158
170
 
159
171
  # load the structure from the structure.sql file
160
- desc "Load structure for test databases via db:structure:load --> parallel:load_structure[num_cpus]"
161
- task :load_structure, :count do |_,args|
172
+ # (faster for rails < 6.1, deprecated after and only configured by `ActiveRecord::Base.schema_format`)
173
+ desc "Load structure for test databases via db:schema:load --> parallel:load_structure[num_cpus]"
174
+ task :load_structure, :count do |_, args|
162
175
  ParallelTests::Tasks.run_in_parallel(
163
176
  "#{ParallelTests::Tasks.rake_bin} #{ParallelTests::Tasks.purge_before_load} " \
164
- "db:structure:load RAILS_ENV=#{ParallelTests::Tasks.rails_env} DISABLE_DATABASE_ENVIRONMENT_CHECK=1", args)
177
+ "db:structure:load RAILS_ENV=#{ParallelTests::Tasks.rails_env} DISABLE_DATABASE_ENVIRONMENT_CHECK=1", args
178
+ )
165
179
  end
166
180
 
167
181
  desc "Load the seed data from db/seeds.rb via db:seed --> parallel:seed[num_cpus]"
168
- task :seed, :count do |_,args|
182
+ task :seed, :count do |_, args|
169
183
  ParallelTests::Tasks.run_in_parallel(
170
- "#{ParallelTests::Tasks.rake_bin} db:seed RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args)
184
+ "#{ParallelTests::Tasks.rake_bin} db:seed RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
185
+ )
171
186
  end
172
187
 
173
188
  desc "Launch given rake command in parallel"
174
189
  task :rake, :command, :count do |_, args|
175
190
  ParallelTests::Tasks.run_in_parallel(
176
191
  "RAILS_ENV=#{ParallelTests::Tasks.rails_env} #{ParallelTests::Tasks.rake_bin} " \
177
- "#{args.command}", args)
192
+ "#{args.command}", args
193
+ )
178
194
  end
179
195
 
180
196
  ['test', 'spec', 'features', 'features-spinach'].each do |type|
181
197
  desc "Run #{type} in parallel with parallel:#{type}[num_cpus]"
182
- task type, [:count, :pattern, :options, :pass_through] do |t, args|
198
+ task type, [:count, :pattern, :options, :pass_through] do |_t, args|
183
199
  ParallelTests::Tasks.check_for_pending_migrations
184
200
  ParallelTests::Tasks.load_lib
185
201
 
@@ -188,12 +204,10 @@ namespace :parallel do
188
204
  'spec' => 'rspec',
189
205
  'test' => 'test',
190
206
  'features' => 'cucumber',
191
- 'features-spinach' => 'spinach',
207
+ 'features-spinach' => 'spinach'
192
208
  }[type]
193
209
 
194
- if test_framework == 'spinach'
195
- type = 'features'
196
- end
210
+ type = 'features' if test_framework == 'spinach'
197
211
  # Using the relative path to find the binary allow to run a specific version of it
198
212
  executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')
199
213
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests'
2
3
 
3
4
  module ParallelTests
@@ -32,7 +33,7 @@ module ParallelTests
32
33
  # --- usually used by other runners
33
34
 
34
35
  # finds all tests and partitions them into groups
35
- def tests_in_groups(tests, num_groups, options={})
36
+ def tests_in_groups(tests, num_groups, options = {})
36
37
  tests = tests_with_size(tests, options)
37
38
  Grouper.in_even_groups_by_size(tests, num_groups, options)
38
39
  end
@@ -46,10 +47,17 @@ module ParallelTests
46
47
  when :filesize
47
48
  sort_by_filesize(tests)
48
49
  when :runtime
49
- sort_by_runtime(tests, runtimes(tests, options), options.merge(allowed_missing: (options[:allowed_missing_percent] || 50) / 100.0))
50
+ sort_by_runtime(
51
+ tests, runtimes(tests, options),
52
+ options.merge(allowed_missing: (options[:allowed_missing_percent] || 50) / 100.0)
53
+ )
50
54
  when nil
51
55
  # use recorded test runtime if we got enough data
52
- runtimes = runtimes(tests, options) rescue []
56
+ runtimes = begin
57
+ runtimes(tests, options)
58
+ rescue StandardError
59
+ []
60
+ end
53
61
  if runtimes.size * 1.5 > tests.size
54
62
  puts "Using recorded test runtime"
55
63
  sort_by_runtime(tests, runtimes)
@@ -67,7 +75,7 @@ module ParallelTests
67
75
  env = (options[:env] || {}).merge(
68
76
  "TEST_ENV_NUMBER" => test_env_number(process_number, options).to_s,
69
77
  "PARALLEL_TEST_GROUPS" => num_processes.to_s,
70
- "PARALLEL_PID_FILE" => ParallelTests.pid_file_path,
78
+ "PARALLEL_PID_FILE" => ParallelTests.pid_file_path
71
79
  )
72
80
  cmd = "nice #{cmd}" if options[:nice]
73
81
  cmd = "#{cmd} 2>&1" if options[:combine_stderr]
@@ -86,13 +94,11 @@ module ParallelTests
86
94
  end
87
95
  ParallelTests.pids.delete(pid) if pid
88
96
  exitstatus = $?.exitstatus
89
- seed = output[/seed (\d+)/,1]
97
+ seed = output[/seed (\d+)/, 1]
90
98
 
91
- if report_process_command?(options) && options[:serialize_stdout]
92
- output = [cmd, output].join("\n")
93
- end
99
+ output = [cmd, output].join("\n") if report_process_command?(options) && options[:serialize_stdout]
94
100
 
95
- {:stdout => output, :exit_status => exitstatus, :command => cmd, :seed => seed}
101
+ { stdout: output, exit_status: exitstatus, command: cmd, seed: seed }
96
102
  end
97
103
 
98
104
  def find_results(test_output)
@@ -104,7 +110,7 @@ module ParallelTests
104
110
  end.compact
105
111
  end
106
112
 
107
- def test_env_number(process_number, options={})
113
+ def test_env_number(process_number, options = {})
108
114
  if process_number == 0 && !options[:first_is_1]
109
115
  ''
110
116
  else
@@ -114,7 +120,7 @@ module ParallelTests
114
120
 
115
121
  def summarize_results(results)
116
122
  sums = sum_up_results(results)
117
- sums.sort.map{|word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
123
+ sums.sort.map { |word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
118
124
  end
119
125
 
120
126
  # remove old seed and add new seed
@@ -134,19 +140,18 @@ module ParallelTests
134
140
  end
135
141
 
136
142
  def sum_up_results(results)
137
- results = results.join(' ').gsub(/s\b/,'') # combine and singularize results
143
+ results = results.join(' ').gsub(/s\b/, '') # combine and singularize results
138
144
  counts = results.scan(/(\d+) (\w+)/)
139
- counts.inject(Hash.new(0)) do |sum, (number, word)|
145
+ counts.each_with_object(Hash.new(0)) do |(number, word), sum|
140
146
  sum[word] += number.to_i
141
- sum
142
147
  end
143
148
  end
144
149
 
145
150
  # read output of the process and print it in chunks
146
- def capture_output(out, env, options={})
147
- result = ""
148
- loop do
149
- begin
151
+ def capture_output(out, env, options = {})
152
+ result = +""
153
+ begin
154
+ loop do
150
155
  read = out.readpartial(1000000) # read whatever chunk we can get
151
156
  if Encoding.default_internal
152
157
  read = read.force_encoding(Encoding.default_internal)
@@ -159,11 +164,13 @@ module ParallelTests
159
164
  $stdout.flush
160
165
  end
161
166
  end
162
- end rescue EOFError
167
+ rescue EOFError
168
+ nil
169
+ end
163
170
  result
164
171
  end
165
172
 
166
- def sort_by_runtime(tests, runtimes, options={})
173
+ def sort_by_runtime(tests, runtimes, options = {})
167
174
  allowed_missing = options[:allowed_missing] || 1.0
168
175
  allowed_missing = tests.size * allowed_missing
169
176
 
@@ -178,9 +185,7 @@ module ParallelTests
178
185
  [test, time]
179
186
  end
180
187
 
181
- if options[:verbose]
182
- puts "Runtime found for #{tests.count(&:last)} of #{tests.size} tests"
183
- end
188
+ puts "Runtime found for #{tests.count(&:last)} of #{tests.size} tests" if options[:verbose]
184
189
 
185
190
  set_unknown_runtime tests, options
186
191
  end
@@ -190,7 +195,7 @@ module ParallelTests
190
195
  lines = File.read(log).split("\n")
191
196
  lines.each_with_object({}) do |line, times|
192
197
  test, _, time = line.rpartition(':')
193
- next unless test and time
198
+ next unless test && time
194
199
  times[test] = time.to_f if tests.include?(test)
195
200
  end
196
201
  end
@@ -217,7 +222,7 @@ module ParallelTests
217
222
  end.uniq
218
223
  end
219
224
 
220
- def files_in_folder(folder, options={})
225
+ def files_in_folder(folder, options = {})
221
226
  pattern = if options[:symlinks] == false # not nil or true
222
227
  "**/*"
223
228
  else
@@ -225,7 +230,7 @@ module ParallelTests
225
230
  # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
226
231
  "**{,/*/**}/*"
227
232
  end
228
- Dir[File.join(folder, pattern)].uniq
233
+ Dir[File.join(folder, pattern)].uniq.sort
229
234
  end
230
235
 
231
236
  private
@@ -236,7 +241,7 @@ module ParallelTests
236
241
  known, unknown = tests.partition(&:last)
237
242
  return if unknown.empty?
238
243
  unknown_runtime = options[:unknown_runtime] ||
239
- (known.empty? ? 1 : known.map!(&:last).inject(:+) / known.size) # average
244
+ (known.empty? ? 1 : known.map!(&:last).sum / known.size) # average
240
245
  unknown.each { |set| set[1] = unknown_runtime }
241
246
  end
242
247