parallel_tests 3.11.0 → 4.2.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 +4 -4
- data/Readme.md +3 -2
- data/lib/parallel_tests/cli.rb +30 -22
- data/lib/parallel_tests/cucumber/scenario_line_logger.rb +1 -1
- data/lib/parallel_tests/cucumber/scenarios.rb +3 -1
- data/lib/parallel_tests/gherkin/runner.rb +1 -1
- data/lib/parallel_tests/grouper.rb +1 -1
- data/lib/parallel_tests/test/runner.rb +18 -10
- data/lib/parallel_tests/version.rb +1 -1
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bcc6e9cd8a207f7a7ec8253139040265cf1c1962e5d25629bca37e5cd2b111e
|
4
|
+
data.tar.gz: 7db53c69e2048799c12504a6ce7b56b6b7b4833ef77fdc298ba50c61fe8ed742
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f682ef1d3752cd3893e186879d905954456b51900ce0d4cfd2213f1c226d13fb38d1a3f251dc75bccb8da663273f59ef977e0aa92707f3ede49db381a53c40f5
|
7
|
+
data.tar.gz: 7a3bad92876b0b225f381b60b89e7049f84e9069a89b710749ecad0e6beed6a60952ed3d850dbe90621a731a974b7694b9182bb02a0f5e3e5292ccb2b6226614
|
data/Readme.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# parallel_tests
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/parallel_tests)
|
4
|
-
[](https://github.com/grosser/parallel_tests/actions?query=workflow%3Awindows)
|
4
|
+
[](https://github.com/grosser/parallel_tests/actions?query=workflow%3Atest)
|
6
5
|
|
7
6
|
Speedup Test::Unit + RSpec + Cucumber + Spinach by running parallel on multiple CPU cores.<br/>
|
8
7
|
ParallelTests splits tests into even groups (by number of lines or runtime) and runs each group in a single process with its own database.
|
@@ -403,6 +402,8 @@ inspired by [pivotal labs](https://blog.pivotal.io/labs/labs/parallelize-your-rs
|
|
403
402
|
- [Joshua Pinter](https://github.com/joshuapinter)
|
404
403
|
- [Zach Dennis](https://github.com/zdennis)
|
405
404
|
- [Jon Dufresne](https://github.com/jdufresne)
|
405
|
+
- [Eric Kessler](https://github.com/enkessler)
|
406
|
+
- [Adis Osmonov](https://github.com/adis-io)
|
406
407
|
|
407
408
|
[Michael Grosser](http://grosser.it)<br/>
|
408
409
|
michael@grosser.it<br/>
|
data/lib/parallel_tests/cli.rb
CHANGED
@@ -32,9 +32,23 @@ module ParallelTests
|
|
32
32
|
@graceful_shutdown_attempted ||= false
|
33
33
|
Kernel.exit if @graceful_shutdown_attempted
|
34
34
|
|
35
|
-
#
|
36
|
-
#
|
37
|
-
|
35
|
+
# In a shell, all sub-processes also get an interrupt, so they shut themselves down.
|
36
|
+
# In a background process this does not happen and we need to do it ourselves.
|
37
|
+
# We cannot always send the interrupt since then the sub-processes would get interrupted twice when in foreground
|
38
|
+
# and that messes with interrupt handling.
|
39
|
+
#
|
40
|
+
# (can simulate detached with `(bundle exec parallel_rspec test/a_spec.rb -n 2 &)`)
|
41
|
+
# also the integration test "passes on int signal to child processes" is detached.
|
42
|
+
#
|
43
|
+
# On windows getpgid does not work so we resort to always killing which is the smaller bug.
|
44
|
+
#
|
45
|
+
# The ParallelTests::Pids `synchronize` method can't be called directly from a trap,
|
46
|
+
# using Thread workaround https://github.com/ddollar/foreman/issues/332
|
47
|
+
Thread.new do
|
48
|
+
if Gem.win_platform? || ((child_pid = ParallelTests.pids.all.first) && Process.getpgid(child_pid) != Process.pid)
|
49
|
+
ParallelTests.stop_all_processes
|
50
|
+
end
|
51
|
+
end
|
38
52
|
|
39
53
|
@graceful_shutdown_attempted = true
|
40
54
|
end
|
@@ -61,20 +75,15 @@ module ParallelTests
|
|
61
75
|
groups = @runner.tests_in_groups(options[:files], num_processes, options)
|
62
76
|
groups.reject!(&:empty?)
|
63
77
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
execute_in_parallel(groups_to_run, groups_to_run.size, options) do |group|
|
68
|
-
run_tests(group, groups_to_run.index(group), 1, options)
|
69
|
-
end
|
70
|
-
else
|
71
|
-
report_number_of_tests(groups) unless options[:quiet]
|
72
|
-
|
73
|
-
execute_in_parallel(groups, groups.size, options) do |group|
|
74
|
-
run_tests(group, groups.index(group), num_processes, options)
|
75
|
-
end
|
78
|
+
if options[:only_group]
|
79
|
+
groups = options[:only_group].map { |i| groups[i - 1] }.compact
|
80
|
+
num_processes = 1
|
76
81
|
end
|
77
82
|
|
83
|
+
report_number_of_tests(groups) unless options[:quiet]
|
84
|
+
test_results = execute_in_parallel(groups, groups.size, options) do |group|
|
85
|
+
run_tests(group, groups.index(group), num_processes, options)
|
86
|
+
end
|
78
87
|
report_results(test_results, options) unless options[:quiet]
|
79
88
|
end
|
80
89
|
|
@@ -136,12 +145,12 @@ module ParallelTests
|
|
136
145
|
failing_sets = test_results.reject { |r| r[:exit_status] == 0 }
|
137
146
|
return if failing_sets.none?
|
138
147
|
|
139
|
-
if options[:verbose] || options[:
|
148
|
+
if options[:verbose] || options[:verbose_command]
|
140
149
|
puts "\n\nTests have failed for a parallel_test group. Use the following command to run the group again:\n\n"
|
141
150
|
failing_sets.each do |failing_set|
|
142
151
|
command = failing_set[:command]
|
143
152
|
command = @runner.command_with_seed(command, failing_set[:seed]) if failing_set[:seed]
|
144
|
-
|
153
|
+
@runner.print_command(command, failing_set[:env] || {})
|
145
154
|
end
|
146
155
|
end
|
147
156
|
end
|
@@ -266,8 +275,7 @@ module ParallelTests
|
|
266
275
|
opts.on("--first-is-1", "Use \"1\" as TEST_ENV_NUMBER to not reuse the default test environment") { options[:first_is_1] = true }
|
267
276
|
opts.on("--fail-fast", "Stop all groups when one group fails (best used with --test-options '--fail-fast' if supported") { options[:fail_fast] = true }
|
268
277
|
opts.on("--verbose", "Print debug output") { options[:verbose] = true }
|
269
|
-
opts.on("--verbose-
|
270
|
-
opts.on("--verbose-rerun-command", "When there are failures, displays the command executed by each process that failed") { options[:verbose_rerun_command] = true }
|
278
|
+
opts.on("--verbose-command", "Displays the command that will be executed by each process and when there are failures displays the command executed by each process that failed") { options[:verbose_command] = true }
|
271
279
|
opts.on("--quiet", "Print only tests output") { options[:quiet] = true }
|
272
280
|
opts.on("-v", "--version", "Show Version") do
|
273
281
|
puts ParallelTests::VERSION
|
@@ -321,12 +329,12 @@ module ParallelTests
|
|
321
329
|
def extract_file_paths(argv)
|
322
330
|
dash_index = argv.rindex("--")
|
323
331
|
file_args_at = (dash_index || -1) + 1
|
324
|
-
[argv[file_args_at
|
332
|
+
[argv[file_args_at..], argv[0...(dash_index || 0)]]
|
325
333
|
end
|
326
334
|
|
327
335
|
def extract_test_options(argv)
|
328
336
|
dash_index = argv.index("--") || -1
|
329
|
-
argv[dash_index + 1
|
337
|
+
argv[dash_index + 1..]
|
330
338
|
end
|
331
339
|
|
332
340
|
def append_test_options(options, argv)
|
@@ -396,7 +404,7 @@ module ParallelTests
|
|
396
404
|
def simulate_output_for_ci(simulate)
|
397
405
|
if simulate
|
398
406
|
progress_indicator = Thread.new do
|
399
|
-
interval = Float(ENV
|
407
|
+
interval = Float(ENV['PARALLEL_TEST_HEARTBEAT_INTERVAL'] || 60)
|
400
408
|
loop do
|
401
409
|
sleep interval
|
402
410
|
print '.'
|
@@ -27,7 +27,7 @@ module ParallelTests
|
|
27
27
|
example_tags = example.tags.map(&:name)
|
28
28
|
example_tags = scenario_tags + example_tags
|
29
29
|
next unless matches_tags?(example_tags)
|
30
|
-
example.rows[1
|
30
|
+
example.rows[1..].each do |row|
|
31
31
|
test_line = row.source_line
|
32
32
|
next if line_numbers.any? && !line_numbers.include?(test_line)
|
33
33
|
|
@@ -52,7 +52,9 @@ module ParallelTests
|
|
52
52
|
feature_tags = feature.tags.map(&:name)
|
53
53
|
|
54
54
|
# We loop on each children of the feature
|
55
|
-
feature.tests
|
55
|
+
test_models = feature.tests
|
56
|
+
test_models += feature.rules.flat_map(&:tests) if feature.respond_to?(:rules) # cuke_modeler >= 3.2 supports rules
|
57
|
+
test_models.each do |test|
|
56
58
|
# It's a scenario, we add it to the scenario_line_logger
|
57
59
|
scenario_line_logger.visit_feature_element(document.path, test, feature_tags, line_numbers: test_lines)
|
58
60
|
end
|
@@ -38,7 +38,7 @@ module ParallelTests
|
|
38
38
|
# add all files that should run in a multiple isolated processes to their own groups
|
39
39
|
group_features_by_size(items_to_group(single_items), groups[0..(isolate_count - 1)])
|
40
40
|
# group the non-isolated by size
|
41
|
-
group_features_by_size(items_to_group(items), groups[isolate_count
|
41
|
+
group_features_by_size(items_to_group(items), groups[isolate_count..])
|
42
42
|
else
|
43
43
|
# add all files that should run in a single non-isolated process to first group
|
44
44
|
single_items.each { |item, size| add_to_group(groups.first, item, size) }
|
@@ -73,7 +73,7 @@ module ParallelTests
|
|
73
73
|
[]
|
74
74
|
end
|
75
75
|
if runtimes.size * 1.5 > tests.size
|
76
|
-
puts "Using recorded test runtime"
|
76
|
+
puts "Using recorded test runtime" unless options[:quiet]
|
77
77
|
sort_by_runtime(tests, runtimes)
|
78
78
|
else
|
79
79
|
sort_by_filesize(tests)
|
@@ -86,24 +86,32 @@ module ParallelTests
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def execute_command(cmd, process_number, num_processes, options)
|
89
|
+
number = test_env_number(process_number, options).to_s
|
89
90
|
env = (options[:env] || {}).merge(
|
90
|
-
"TEST_ENV_NUMBER" =>
|
91
|
+
"TEST_ENV_NUMBER" => number,
|
91
92
|
"PARALLEL_TEST_GROUPS" => num_processes.to_s,
|
92
93
|
"PARALLEL_PID_FILE" => ParallelTests.pid_file_path
|
93
94
|
)
|
94
95
|
cmd = ["nice", *cmd] if options[:nice]
|
95
96
|
|
96
|
-
|
97
|
+
# being able to run with for example `-output foo-$TEST_ENV_NUMBER` worked originally and is convenient
|
98
|
+
cmd.map! { |c| c.gsub("$TEST_ENV_NUMBER", number).gsub("${TEST_ENV_NUMBER}", number) }
|
99
|
+
|
100
|
+
print_command(cmd, env) if report_process_command?(options) && !options[:serialize_stdout]
|
97
101
|
|
98
102
|
execute_command_and_capture_output(env, cmd, options)
|
99
103
|
end
|
100
104
|
|
101
|
-
def
|
102
|
-
|
105
|
+
def print_command(command, env)
|
106
|
+
env_str = ['TEST_ENV_NUMBER', 'PARALLEL_TEST_GROUPS'].map { |e| "#{e}=#{env[e]}" }.join(' ')
|
107
|
+
puts [env_str, Shellwords.shelljoin(command)].compact.join(' ')
|
108
|
+
end
|
103
109
|
|
104
|
-
|
110
|
+
def execute_command_and_capture_output(env, cmd, options)
|
111
|
+
popen_options = {} # do not add `pgroup: true`, it will break `binding.irb` inside the test
|
105
112
|
popen_options[:err] = [:child, :out] if options[:combine_stderr]
|
106
113
|
|
114
|
+
pid = nil
|
107
115
|
output = IO.popen(env, cmd, popen_options) do |io|
|
108
116
|
pid = io.pid
|
109
117
|
ParallelTests.pids.add(pid)
|
@@ -115,7 +123,7 @@ module ParallelTests
|
|
115
123
|
|
116
124
|
output = "#{Shellwords.shelljoin(cmd)}\n#{output}" if report_process_command?(options) && options[:serialize_stdout]
|
117
125
|
|
118
|
-
{ stdout: output, exit_status: exitstatus, command: cmd, seed: seed }
|
126
|
+
{ env: env, stdout: output, exit_status: exitstatus, command: cmd, seed: seed }
|
119
127
|
end
|
120
128
|
|
121
129
|
def find_results(test_output)
|
@@ -149,8 +157,8 @@ module ParallelTests
|
|
149
157
|
protected
|
150
158
|
|
151
159
|
def executable
|
152
|
-
if ENV
|
153
|
-
[
|
160
|
+
if (executable = ENV['PARALLEL_TESTS_EXECUTABLE'])
|
161
|
+
[executable]
|
154
162
|
else
|
155
163
|
determine_executable
|
156
164
|
end
|
@@ -282,7 +290,7 @@ module ParallelTests
|
|
282
290
|
end
|
283
291
|
|
284
292
|
def report_process_command?(options)
|
285
|
-
options[:verbose] || options[:
|
293
|
+
options[:verbose] || options[:verbose_command]
|
286
294
|
end
|
287
295
|
end
|
288
296
|
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:
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parallel
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
description:
|
27
|
+
description:
|
28
28
|
email: michael@grosser.it
|
29
29
|
executables:
|
30
30
|
- parallel_spinach
|
@@ -68,10 +68,10 @@ licenses:
|
|
68
68
|
- MIT
|
69
69
|
metadata:
|
70
70
|
bug_tracker_uri: https://github.com/grosser/parallel_tests/issues
|
71
|
-
documentation_uri: https://github.com/grosser/parallel_tests/blob/
|
72
|
-
source_code_uri: https://github.com/grosser/parallel_tests/tree/
|
71
|
+
documentation_uri: https://github.com/grosser/parallel_tests/blob/v4.2.0/Readme.md
|
72
|
+
source_code_uri: https://github.com/grosser/parallel_tests/tree/v4.2.0
|
73
73
|
wiki_uri: https://github.com/grosser/parallel_tests/wiki
|
74
|
-
post_install_message:
|
74
|
+
post_install_message:
|
75
75
|
rdoc_options: []
|
76
76
|
require_paths:
|
77
77
|
- lib
|
@@ -79,15 +79,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 2.
|
82
|
+
version: 2.7.0
|
83
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
84
|
requirements:
|
85
85
|
- - ">="
|
86
86
|
- !ruby/object:Gem::Version
|
87
87
|
version: '0'
|
88
88
|
requirements: []
|
89
|
-
rubygems_version: 3.
|
90
|
-
signing_key:
|
89
|
+
rubygems_version: 3.3.3
|
90
|
+
signing_key:
|
91
91
|
specification_version: 4
|
92
92
|
summary: Run Test::Unit / RSpec / Cucumber / Spinach in parallel
|
93
93
|
test_files: []
|