parallel_tests 1.3.7 → 3.7.3
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 +5 -5
- data/Readme.md +153 -61
- data/bin/parallel_cucumber +2 -1
- data/bin/parallel_rspec +2 -1
- data/bin/parallel_spinach +2 -1
- data/bin/parallel_test +2 -1
- data/lib/parallel_tests/cli.rb +264 -69
- data/lib/parallel_tests/cucumber/failures_logger.rb +10 -8
- data/lib/parallel_tests/cucumber/features_with_steps.rb +32 -0
- data/lib/parallel_tests/cucumber/runner.rb +16 -9
- data/lib/parallel_tests/cucumber/scenario_line_logger.rb +30 -31
- data/lib/parallel_tests/cucumber/scenarios.rb +44 -11
- data/lib/parallel_tests/gherkin/io.rb +2 -3
- data/lib/parallel_tests/gherkin/listener.rb +10 -12
- data/lib/parallel_tests/gherkin/runner.rb +24 -25
- data/lib/parallel_tests/gherkin/runtime_logger.rb +13 -12
- data/lib/parallel_tests/grouper.rb +94 -22
- data/lib/parallel_tests/pids.rb +60 -0
- data/lib/parallel_tests/railtie.rb +1 -0
- data/lib/parallel_tests/rspec/failures_logger.rb +7 -35
- data/lib/parallel_tests/rspec/logger_base.rb +13 -20
- data/lib/parallel_tests/rspec/runner.rb +45 -27
- data/lib/parallel_tests/rspec/runtime_logger.rb +23 -35
- data/lib/parallel_tests/rspec/summary_logger.rb +5 -13
- data/lib/parallel_tests/spinach/runner.rb +6 -2
- data/lib/parallel_tests/tasks.rb +125 -59
- data/lib/parallel_tests/test/runner.rb +102 -62
- data/lib/parallel_tests/test/runtime_logger.rb +33 -61
- data/lib/parallel_tests/version.rb +2 -1
- data/lib/parallel_tests.rb +46 -19
- metadata +12 -7
@@ -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,25 +15,29 @@ 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
|
26
25
|
|
27
26
|
def run_tests(test_files, process_number, num_processes, options)
|
28
|
-
require_list = test_files.map { |file| file.
|
27
|
+
require_list = test_files.map { |file| file.gsub(" ", "\\ ") }.join(" ")
|
29
28
|
cmd = "#{executable} -Itest -e '%w[#{require_list}].each { |f| require %{./\#{f}} }' -- #{options[:test_options]}"
|
30
29
|
execute_command(cmd, process_number, num_processes, options)
|
31
30
|
end
|
32
31
|
|
32
|
+
# ignores other commands runner noise
|
33
33
|
def line_is_result?(line)
|
34
|
-
line
|
35
|
-
line =~ /\d+ failure/
|
34
|
+
line =~ /\d+ failure(?!:)/
|
36
35
|
end
|
37
36
|
|
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(
|
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 =
|
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)
|
@@ -71,49 +77,60 @@ module ParallelTests
|
|
71
77
|
|
72
78
|
def execute_command(cmd, process_number, num_processes, options)
|
73
79
|
env = (options[:env] || {}).merge(
|
74
|
-
"TEST_ENV_NUMBER" => test_env_number(process_number),
|
75
|
-
"PARALLEL_TEST_GROUPS" => num_processes
|
80
|
+
"TEST_ENV_NUMBER" => test_env_number(process_number, options).to_s,
|
81
|
+
"PARALLEL_TEST_GROUPS" => num_processes.to_s,
|
82
|
+
"PARALLEL_PID_FILE" => ParallelTests.pid_file_path
|
76
83
|
)
|
77
84
|
cmd = "nice #{cmd}" if options[:nice]
|
78
85
|
cmd = "#{cmd} 2>&1" if options[:combine_stderr]
|
79
|
-
puts cmd if options[:verbose]
|
80
86
|
|
81
|
-
|
82
|
-
end
|
87
|
+
puts cmd if report_process_command?(options) && !options[:serialize_stdout]
|
83
88
|
|
84
|
-
|
85
|
-
|
86
|
-
separator = (WINDOWS ? ' & ' : ';')
|
87
|
-
exports = env.map do |k,v|
|
88
|
-
if WINDOWS
|
89
|
-
"(SET \"#{k}=#{v}\")"
|
90
|
-
else
|
91
|
-
"#{k}=#{v};export #{k}"
|
92
|
-
end
|
93
|
-
end.join(separator)
|
94
|
-
cmd = "#{exports}#{separator}#{cmd}"
|
89
|
+
execute_command_and_capture_output(env, cmd, options)
|
90
|
+
end
|
95
91
|
|
96
|
-
|
92
|
+
def execute_command_and_capture_output(env, cmd, options)
|
93
|
+
pid = nil
|
94
|
+
output = IO.popen(env, cmd) do |io|
|
95
|
+
pid = io.pid
|
96
|
+
ParallelTests.pids.add(pid)
|
97
|
+
capture_output(io, env, options)
|
98
|
+
end
|
99
|
+
ParallelTests.pids.delete(pid) if pid
|
97
100
|
exitstatus = $?.exitstatus
|
101
|
+
seed = output[/seed (\d+)/, 1]
|
102
|
+
|
103
|
+
output = [cmd, output].join("\n") if report_process_command?(options) && options[:serialize_stdout]
|
98
104
|
|
99
|
-
{:
|
105
|
+
{ stdout: output, exit_status: exitstatus, command: cmd, seed: seed }
|
100
106
|
end
|
101
107
|
|
102
108
|
def find_results(test_output)
|
103
|
-
test_output.
|
104
|
-
line.
|
109
|
+
test_output.lines.map do |line|
|
110
|
+
line.chomp!
|
111
|
+
line.gsub!(/\e\[\d+m/, '') # remove color coding
|
105
112
|
next unless line_is_result?(line)
|
106
113
|
line
|
107
|
-
|
114
|
+
end.compact
|
108
115
|
end
|
109
116
|
|
110
|
-
def test_env_number(process_number)
|
111
|
-
process_number == 0
|
117
|
+
def test_env_number(process_number, options = {})
|
118
|
+
if process_number == 0 && !options[:first_is_1]
|
119
|
+
''
|
120
|
+
else
|
121
|
+
process_number + 1
|
122
|
+
end
|
112
123
|
end
|
113
124
|
|
114
125
|
def summarize_results(results)
|
115
126
|
sums = sum_up_results(results)
|
116
|
-
sums.sort.map{|word, number|
|
127
|
+
sums.sort.map { |word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
|
128
|
+
end
|
129
|
+
|
130
|
+
# remove old seed and add new seed
|
131
|
+
def command_with_seed(cmd, seed)
|
132
|
+
clean = cmd.sub(/\s--seed\s+\d+\b/, '')
|
133
|
+
"#{clean} --seed #{seed}"
|
117
134
|
end
|
118
135
|
|
119
136
|
protected
|
@@ -127,34 +144,37 @@ module ParallelTests
|
|
127
144
|
end
|
128
145
|
|
129
146
|
def sum_up_results(results)
|
130
|
-
results = results.join(' ').gsub(/s\b/,'') # combine and singularize results
|
147
|
+
results = results.join(' ').gsub(/s\b/, '') # combine and singularize results
|
131
148
|
counts = results.scan(/(\d+) (\w+)/)
|
132
|
-
counts.
|
149
|
+
counts.each_with_object(Hash.new(0)) do |(number, word), sum|
|
133
150
|
sum[word] += number.to_i
|
134
|
-
sum
|
135
151
|
end
|
136
152
|
end
|
137
153
|
|
138
154
|
# read output of the process and print it in chunks
|
139
|
-
def capture_output(out,
|
140
|
-
result = ""
|
141
|
-
|
142
|
-
|
155
|
+
def capture_output(out, env, options = {})
|
156
|
+
result = +""
|
157
|
+
begin
|
158
|
+
loop do
|
143
159
|
read = out.readpartial(1000000) # read whatever chunk we can get
|
144
160
|
if Encoding.default_internal
|
145
161
|
read = read.force_encoding(Encoding.default_internal)
|
146
162
|
end
|
147
163
|
result << read
|
148
|
-
unless
|
149
|
-
|
164
|
+
unless options[:serialize_stdout]
|
165
|
+
message = read
|
166
|
+
message = "[TEST GROUP #{env['TEST_ENV_NUMBER']}] #{message}" if options[:prefix_output_with_test_env_number]
|
167
|
+
$stdout.print message
|
150
168
|
$stdout.flush
|
151
169
|
end
|
152
170
|
end
|
153
|
-
|
171
|
+
rescue EOFError
|
172
|
+
nil
|
173
|
+
end
|
154
174
|
result
|
155
175
|
end
|
156
176
|
|
157
|
-
def sort_by_runtime(tests, runtimes, options={})
|
177
|
+
def sort_by_runtime(tests, runtimes, options = {})
|
158
178
|
allowed_missing = options[:allowed_missing] || 1.0
|
159
179
|
allowed_missing = tests.size * allowed_missing
|
160
180
|
|
@@ -162,26 +182,24 @@ module ParallelTests
|
|
162
182
|
tests.sort!
|
163
183
|
tests.map! do |test|
|
164
184
|
allowed_missing -= 1 unless time = runtimes[test]
|
165
|
-
|
185
|
+
if allowed_missing < 0
|
186
|
+
log = options[:runtime_log] || runtime_log
|
187
|
+
raise "Runtime log file '#{log}' does not contain sufficient data to sort #{tests.size} test files, please update or remove it."
|
188
|
+
end
|
166
189
|
[test, time]
|
167
190
|
end
|
168
191
|
|
169
|
-
if options[:verbose]
|
170
|
-
puts "Runtime found for #{tests.count(&:last)} of #{tests.size} tests"
|
171
|
-
end
|
192
|
+
puts "Runtime found for #{tests.count(&:last)} of #{tests.size} tests" if options[:verbose]
|
172
193
|
|
173
|
-
|
174
|
-
known, unknown = tests.partition(&:last)
|
175
|
-
average = (known.any? ? known.map!(&:last).inject(:+) / known.size : 1)
|
176
|
-
unknown.each { |set| set[1] = average }
|
194
|
+
set_unknown_runtime tests, options
|
177
195
|
end
|
178
196
|
|
179
197
|
def runtimes(tests, options)
|
180
198
|
log = options[:runtime_log] || runtime_log
|
181
199
|
lines = File.read(log).split("\n")
|
182
200
|
lines.each_with_object({}) do |line, times|
|
183
|
-
test, time = line.
|
184
|
-
next unless test
|
201
|
+
test, _, time = line.rpartition(':')
|
202
|
+
next unless test && time
|
185
203
|
times[test] = time.to_f if tests.include?(test)
|
186
204
|
end
|
187
205
|
end
|
@@ -192,17 +210,23 @@ module ParallelTests
|
|
192
210
|
end
|
193
211
|
|
194
212
|
def find_tests(tests, options = {})
|
195
|
-
|
213
|
+
suffix_pattern = options[:suffix] || test_suffix
|
214
|
+
include_pattern = options[:pattern] || //
|
215
|
+
exclude_pattern = options[:exclude_pattern]
|
216
|
+
|
217
|
+
(tests || []).flat_map do |file_or_folder|
|
196
218
|
if File.directory?(file_or_folder)
|
197
219
|
files = files_in_folder(file_or_folder, options)
|
198
|
-
files.grep(
|
220
|
+
files = files.grep(suffix_pattern).grep(include_pattern)
|
221
|
+
files -= files.grep(exclude_pattern) if exclude_pattern
|
222
|
+
files
|
199
223
|
else
|
200
224
|
file_or_folder
|
201
225
|
end
|
202
|
-
end.
|
226
|
+
end.uniq
|
203
227
|
end
|
204
228
|
|
205
|
-
def files_in_folder(folder, options={})
|
229
|
+
def files_in_folder(folder, options = {})
|
206
230
|
pattern = if options[:symlinks] == false # not nil or true
|
207
231
|
"**/*"
|
208
232
|
else
|
@@ -210,7 +234,23 @@ module ParallelTests
|
|
210
234
|
# http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
|
211
235
|
"**{,/*/**}/*"
|
212
236
|
end
|
213
|
-
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]
|
214
254
|
end
|
215
255
|
end
|
216
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
|
|
@@ -18,19 +19,28 @@ module ParallelTests
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def unique_log
|
21
|
-
|
22
|
+
with_locked_log do |logfile|
|
22
23
|
separator = "\n"
|
23
|
-
groups =
|
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).
|
26
|
+
time = "%.2f" % times.map(&:last).map(&:to_f).sum
|
26
27
|
"#{file}:#{time}"
|
27
28
|
end
|
28
|
-
|
29
|
+
logfile.rewind
|
30
|
+
logfile.write(lines.join(separator) + separator)
|
31
|
+
logfile.truncate(logfile.pos)
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
32
35
|
private
|
33
36
|
|
37
|
+
def with_locked_log
|
38
|
+
File.open(logfile, File::RDWR | File::CREAT) do |logfile|
|
39
|
+
logfile.flock(File::LOCK_EX)
|
40
|
+
yield logfile
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
34
44
|
# ensure folder exists + clean out previous log
|
35
45
|
# this will happen in multiple processes, but should be roughly at the same time
|
36
46
|
# so there should be no log message lost
|
@@ -43,28 +53,18 @@ module ParallelTests
|
|
43
53
|
|
44
54
|
def log(test, time)
|
45
55
|
return unless message = message(test, time)
|
46
|
-
|
47
|
-
|
56
|
+
with_locked_log do |logfile|
|
57
|
+
logfile.seek(0, IO::SEEK_END)
|
58
|
+
logfile.puts message
|
48
59
|
end
|
49
60
|
end
|
50
61
|
|
51
62
|
def message(test, delta)
|
52
|
-
return unless method = test.public_instance_methods(true).detect { |
|
63
|
+
return unless method = test.public_instance_methods(true).detect { |m| m =~ /^test_/ }
|
53
64
|
filename = test.instance_method(method).source_location.first.sub("#{Dir.pwd}/", "")
|
54
65
|
"#{filename}:#{delta}"
|
55
66
|
end
|
56
67
|
|
57
|
-
def lock
|
58
|
-
File.open(logfile, 'r') do |f|
|
59
|
-
begin
|
60
|
-
f.flock File::LOCK_EX
|
61
|
-
yield
|
62
|
-
ensure
|
63
|
-
f.flock File::LOCK_UN
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
68
|
def logfile
|
69
69
|
ParallelTests::Test::Runner.runtime_log
|
70
70
|
end
|
@@ -75,54 +75,26 @@ end
|
|
75
75
|
|
76
76
|
if defined?(Minitest::Runnable) # Minitest 5
|
77
77
|
class << Minitest::Runnable
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
prepend(
|
79
|
+
Module.new do
|
80
|
+
def run(*)
|
81
|
+
ParallelTests::Test::RuntimeLogger.log_test_run(self) do
|
82
|
+
super
|
83
|
+
end
|
84
|
+
end
|
82
85
|
end
|
83
|
-
|
86
|
+
)
|
84
87
|
end
|
85
88
|
|
86
89
|
class << Minitest
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
elsif defined?(MiniTest::Unit) # Minitest 4
|
95
|
-
MiniTest::Unit.class_eval do
|
96
|
-
alias_method :_run_suite_without_runtime_log, :_run_suite
|
97
|
-
def _run_suite(*args)
|
98
|
-
ParallelTests::Test::RuntimeLogger.log_test_run(args.first) do
|
99
|
-
_run_suite_without_runtime_log(*args)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
alias_method :_run_suites_without_runtime_log, :_run_suites
|
104
|
-
def _run_suites(*args)
|
105
|
-
result = _run_suites_without_runtime_log(*args)
|
106
|
-
ParallelTests::Test::RuntimeLogger.unique_log
|
107
|
-
result
|
108
|
-
end
|
109
|
-
end
|
110
|
-
else # Test::Unit
|
111
|
-
require 'test/unit/testsuite'
|
112
|
-
class ::Test::Unit::TestSuite
|
113
|
-
alias_method :run_without_timing, :run
|
114
|
-
|
115
|
-
def run(result, &block)
|
116
|
-
test = tests.first
|
117
|
-
|
118
|
-
if test.is_a? ::Test::Unit::TestSuite # all tests ?
|
119
|
-
run_without_timing(result, &block)
|
120
|
-
ParallelTests::Test::RuntimeLogger.unique_log
|
121
|
-
else
|
122
|
-
ParallelTests::Test::RuntimeLogger.log_test_run(test.class) do
|
123
|
-
run_without_timing(result, &block)
|
90
|
+
prepend(
|
91
|
+
Module.new do
|
92
|
+
def run(*args)
|
93
|
+
result = super
|
94
|
+
ParallelTests::Test::RuntimeLogger.unique_log
|
95
|
+
result
|
124
96
|
end
|
125
97
|
end
|
126
|
-
|
98
|
+
)
|
127
99
|
end
|
128
100
|
end
|
data/lib/parallel_tests.rb
CHANGED
@@ -1,19 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "parallel"
|
2
3
|
require "parallel_tests/railtie" if defined? Rails::Railtie
|
3
4
|
require "rbconfig"
|
4
5
|
|
5
6
|
module ParallelTests
|
6
7
|
WINDOWS = (RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
|
7
|
-
|
8
|
-
if WINDOWS
|
9
|
-
"wmic process get commandline | findstr TEST_ENV_NUMBER | find /c \"TEST_ENV_NUMBER=\" 2>&1"
|
10
|
-
else
|
11
|
-
"ps -ef | grep [T]EST_ENV_NUMBER= 2>&1"
|
12
|
-
end
|
8
|
+
RUBY_BINARY = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
|
13
9
|
|
14
10
|
autoload :CLI, "parallel_tests/cli"
|
15
11
|
autoload :VERSION, "parallel_tests/version"
|
16
12
|
autoload :Grouper, "parallel_tests/grouper"
|
13
|
+
autoload :Pids, "parallel_tests/pids"
|
17
14
|
|
18
15
|
class << self
|
19
16
|
def determine_number_of_processes(count)
|
@@ -21,7 +18,32 @@ module ParallelTests
|
|
21
18
|
count,
|
22
19
|
ENV["PARALLEL_TEST_PROCESSORS"],
|
23
20
|
Parallel.processor_count
|
24
|
-
].detect{|c|
|
21
|
+
].detect { |c| !c.to_s.strip.empty? }.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_pid_file
|
25
|
+
Tempfile.open('parallel_tests-pidfile') do |f|
|
26
|
+
ENV['PARALLEL_PID_FILE'] = f.path
|
27
|
+
# Pids object should be created before threads will start adding pids to it
|
28
|
+
# Otherwise we would have to use Mutex to prevent creation of several instances
|
29
|
+
@pids = pids
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
ENV['PARALLEL_PID_FILE'] = nil
|
33
|
+
@pids = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def pids
|
38
|
+
@pids ||= Pids.new(pid_file_path)
|
39
|
+
end
|
40
|
+
|
41
|
+
def pid_file_path
|
42
|
+
ENV.fetch('PARALLEL_PID_FILE')
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_all_processes
|
46
|
+
pids.all.each { |pid| Process.kill(:INT, pid) }
|
25
47
|
end
|
26
48
|
|
27
49
|
# copied from http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
|
@@ -34,14 +56,27 @@ module ParallelTests
|
|
34
56
|
until !File.directory?(current) || current == previous
|
35
57
|
filename = File.join(current, "Gemfile")
|
36
58
|
return true if File.exist?(filename)
|
37
|
-
|
59
|
+
previous = current
|
60
|
+
current = File.expand_path("..", current)
|
38
61
|
end
|
39
62
|
|
40
63
|
false
|
41
64
|
end
|
42
65
|
|
43
66
|
def first_process?
|
44
|
-
|
67
|
+
ENV["TEST_ENV_NUMBER"].to_i <= 1
|
68
|
+
end
|
69
|
+
|
70
|
+
def last_process?
|
71
|
+
current_process_number = ENV['TEST_ENV_NUMBER']
|
72
|
+
total_processes = ENV['PARALLEL_TEST_GROUPS']
|
73
|
+
return true if current_process_number.nil? && total_processes.nil?
|
74
|
+
current_process_number = '1' if current_process_number.nil?
|
75
|
+
current_process_number == total_processes
|
76
|
+
end
|
77
|
+
|
78
|
+
def with_ruby_binary(command)
|
79
|
+
WINDOWS ? "#{RUBY_BINARY} -- #{command}" : command
|
45
80
|
end
|
46
81
|
|
47
82
|
def wait_for_other_processes_to_finish
|
@@ -49,20 +84,12 @@ module ParallelTests
|
|
49
84
|
sleep 1 until number_of_running_processes <= 1
|
50
85
|
end
|
51
86
|
|
52
|
-
# Fun fact: this includes the current process if it's run via parallel_tests
|
53
87
|
def number_of_running_processes
|
54
|
-
|
55
|
-
raise "Could not grep for processes -> #{result}" if result.strip != "" && !$?.success?
|
56
|
-
result.split("\n").size
|
88
|
+
pids.count
|
57
89
|
end
|
58
90
|
|
59
|
-
# real time even if someone messed with timecop in tests
|
60
91
|
def now
|
61
|
-
|
62
|
-
Time.now_without_mock_time
|
63
|
-
else
|
64
|
-
Time.now
|
65
|
-
end
|
92
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
66
93
|
end
|
67
94
|
|
68
95
|
def delta
|
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:
|
4
|
+
version: 3.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-17 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
|
@@ -50,6 +51,7 @@ files:
|
|
50
51
|
- lib/parallel_tests/gherkin/runner.rb
|
51
52
|
- lib/parallel_tests/gherkin/runtime_logger.rb
|
52
53
|
- lib/parallel_tests/grouper.rb
|
54
|
+
- lib/parallel_tests/pids.rb
|
53
55
|
- lib/parallel_tests/railtie.rb
|
54
56
|
- lib/parallel_tests/rspec/failures_logger.rb
|
55
57
|
- lib/parallel_tests/rspec/logger_base.rb
|
@@ -61,10 +63,14 @@ files:
|
|
61
63
|
- lib/parallel_tests/test/runner.rb
|
62
64
|
- lib/parallel_tests/test/runtime_logger.rb
|
63
65
|
- lib/parallel_tests/version.rb
|
64
|
-
homepage:
|
66
|
+
homepage: https://github.com/grosser/parallel_tests
|
65
67
|
licenses:
|
66
68
|
- MIT
|
67
|
-
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.3/Readme.md
|
72
|
+
source_code_uri: https://github.com/grosser/parallel_tests/tree/v3.7.3
|
73
|
+
wiki_uri: https://github.com/grosser/parallel_tests/wiki
|
68
74
|
post_install_message:
|
69
75
|
rdoc_options: []
|
70
76
|
require_paths:
|
@@ -73,15 +79,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
79
|
requirements:
|
74
80
|
- - ">="
|
75
81
|
- !ruby/object:Gem::Version
|
76
|
-
version:
|
82
|
+
version: 2.5.0
|
77
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
84
|
requirements:
|
79
85
|
- - ">="
|
80
86
|
- !ruby/object:Gem::Version
|
81
87
|
version: '0'
|
82
88
|
requirements: []
|
83
|
-
|
84
|
-
rubygems_version: 2.2.2
|
89
|
+
rubygems_version: 3.2.16
|
85
90
|
signing_key:
|
86
91
|
specification_version: 4
|
87
92
|
summary: Run Test::Unit / RSpec / Cucumber / Spinach in parallel
|