minitest-parallel_fork 1.3.1 → 2.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 +4 -4
- data/CHANGELOG +8 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +11 -0
- data/lib/minitest/parallel_fork/fail_fast.rb +69 -0
- data/lib/minitest/parallel_fork.rb +88 -45
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2698fbcfb5e14bed9fbf23e2abe38c5395b615f4443e335ce606b2f674883e30
|
4
|
+
data.tar.gz: dff7fbdc3104a729405415688ff35e574d65df94ac0c2086f3791e5005a95dfd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80b75938b994edb1f6cfe17c5b8ad31550ab6081738f178ac3dd3c991a7a61fd1a6730d0e855cec3d1d0f5113dddda6ecec455f310ccf4ebcc74298a763a43ae
|
7
|
+
data.tar.gz: 398db64ae6468875db78f49ba7221f8cb1f789161adf361a2125daee43a85acd194c0afbbeb1faab35290c72bc377b4eba1cd01dbfba9ace41255d6278eb4a5b
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 2.0.0 (2023-11-08)
|
2
|
+
|
3
|
+
* Avoid method redefined warning in verbose warning mode (jeremyevans)
|
4
|
+
|
5
|
+
* Add minitest/parallel_fork/fail_fast with support for stopping execution as soon as there is a failure in any child process (jeremyevans, stackmystack) (#12)
|
6
|
+
|
7
|
+
* Refactor internals for easier extension (jeremyevans)
|
8
|
+
|
1
9
|
=== 1.3.1 (2023-09-25)
|
2
10
|
|
3
11
|
* Bump required_ruby_version to 2.2, since that is lowest version supported by minitest 5.15.0 (jeremyevans)
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -68,6 +68,17 @@ it reports results.
|
|
68
68
|
# Gather relevant logs for more debugging
|
69
69
|
end
|
70
70
|
|
71
|
+
== Fail Fast Support
|
72
|
+
|
73
|
+
If you would like to run tests in parallel, but stop running tests at the first
|
74
|
+
failure, you can use:
|
75
|
+
|
76
|
+
RUBYOPT=-rminitest/parallel_fork/fail_fast rake spec
|
77
|
+
|
78
|
+
Note that minitest-parallel_fork uses suite-based parallelism, so tests will not
|
79
|
+
stop until one child has a failing test suite (test class that has a failing test
|
80
|
+
method), and other children are signaled and also stop processing.
|
81
|
+
|
71
82
|
== ActiveRecord
|
72
83
|
|
73
84
|
To use this with Rails/ActiveRecord, you probably want to use hooks similar to:
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../parallel_fork'
|
2
|
+
|
3
|
+
module Minitest::ParalleForkFailFast
|
4
|
+
def run_after_parallel_fork_hook(i)
|
5
|
+
super
|
6
|
+
Signal.trap(:USR1) do
|
7
|
+
@parallel_fork_stop = true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def parallel_fork_data_to_marshal
|
12
|
+
super << @parallel_fork_stop
|
13
|
+
end
|
14
|
+
|
15
|
+
def parallel_fork_data_from_marshal(data)
|
16
|
+
data = Marshal.load(data)
|
17
|
+
@parallel_fork_stop = true if data.pop
|
18
|
+
data
|
19
|
+
end
|
20
|
+
|
21
|
+
def parallel_fork_run_test_suites(suites, reporter, options)
|
22
|
+
suites.each do |suite|
|
23
|
+
parallel_fork_run_test_suite(suite, reporter, options)
|
24
|
+
|
25
|
+
# Fail fast if this child process had a failure,
|
26
|
+
# Or the USR1 signal was received indicating other child processes had a failure.
|
27
|
+
break if @parallel_fork_stop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def parallel_fork_run_test_suite(suite, reporter, options)
|
32
|
+
super
|
33
|
+
|
34
|
+
|
35
|
+
if parallel_fork_stat_reporter.results.any?{|r| !r.failure.is_a?(Minitest::Skip)}
|
36
|
+
# At least one failure or error, mark as failing fast
|
37
|
+
@parallel_fork_stop = true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parallel_fork_child_data(data)
|
42
|
+
threads = {}
|
43
|
+
data.each{|pid, read| threads[pid] = Thread.new(read, &:read)}
|
44
|
+
results = []
|
45
|
+
|
46
|
+
while sleep(0.01) && !threads.empty?
|
47
|
+
threads.to_a.each do |pid, thread|
|
48
|
+
unless thread.alive?
|
49
|
+
threads.delete(pid)
|
50
|
+
results << parallel_fork_data_from_marshal(thread.value)
|
51
|
+
|
52
|
+
if @parallel_fork_stop
|
53
|
+
# If any child failed fast, signal other children to fail fast
|
54
|
+
threads.each_key do |pid|
|
55
|
+
Process.kill(:USR1, pid)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Set a flag indicating that all child processes have been signaled
|
59
|
+
@parallel_fork_stop = :FINISHED
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
results
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Minitest.singleton_class.prepend(Minitest::ParalleForkFailFast)
|
@@ -1,89 +1,132 @@
|
|
1
|
-
gem 'minitest'
|
2
1
|
require 'minitest'
|
3
2
|
|
3
|
+
module Minitest::Unparallelize
|
4
|
+
define_method(:run_one_method, &Minitest::Test.method(:run_one_method))
|
5
|
+
end
|
6
|
+
|
4
7
|
module Minitest
|
8
|
+
@before_parallel_fork = nil
|
9
|
+
@after_parallel_fork = nil
|
10
|
+
@on_parallel_fork_marshal_failure = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
class << Minitest
|
5
14
|
# Set the before_parallel_fork block to the given block
|
6
|
-
def
|
15
|
+
def before_parallel_fork(&block)
|
7
16
|
@before_parallel_fork = block
|
8
17
|
end
|
9
|
-
@before_parallel_fork = nil
|
10
18
|
|
11
19
|
# Set the after_parallel_fork block to the given block
|
12
|
-
def
|
20
|
+
def after_parallel_fork(i=nil, &block)
|
13
21
|
@after_parallel_fork = block
|
14
22
|
end
|
15
|
-
@after_parallel_fork = nil
|
16
23
|
|
17
24
|
# Set the on_parallel_fork_marshal_failure block to the given block
|
18
|
-
def
|
25
|
+
def on_parallel_fork_marshal_failure(&block)
|
19
26
|
@on_parallel_fork_marshal_failure = block
|
20
27
|
end
|
21
|
-
@on_parallel_fork_marshal_failure = nil
|
22
28
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def self.parallel_fork_stat_reporter(reporter)
|
28
|
-
reporter.reporters.detect do |rep|
|
29
|
+
attr_reader :parallel_fork_stat_reporter
|
30
|
+
|
31
|
+
def set_parallel_fork_stat_reporter(reporter)
|
32
|
+
@parallel_fork_stat_reporter = reporter.reporters.detect do |rep|
|
29
33
|
%w'count assertions results count= assertions='.all?{|meth| rep.respond_to?(meth)}
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
suites = Runnable.runnables.shuffle
|
37
|
-
stat_reporter = parallel_fork_stat_reporter(reporter)
|
37
|
+
def parallel_fork_suites
|
38
|
+
Minitest::Runnable.runnables.shuffle
|
39
|
+
end
|
38
40
|
|
39
|
-
|
40
|
-
reads = []
|
41
|
+
def run_before_parallel_fork_hook
|
41
42
|
if @before_parallel_fork
|
42
43
|
@before_parallel_fork.call
|
43
44
|
end
|
44
|
-
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_after_parallel_fork_hook(i)
|
48
|
+
if @after_parallel_fork
|
49
|
+
@after_parallel_fork.call(i)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def parallel_fork_data_to_marshal
|
54
|
+
%i'count assertions results'.map{|meth| parallel_fork_stat_reporter.send(meth)}
|
55
|
+
end
|
56
|
+
|
57
|
+
def parallel_fork_data_from_marshal(data)
|
58
|
+
Marshal.load(data)
|
59
|
+
rescue ArgumentError
|
60
|
+
if @on_parallel_fork_marshal_failure
|
61
|
+
@on_parallel_fork_marshal_failure.call
|
62
|
+
end
|
63
|
+
raise
|
64
|
+
end
|
65
|
+
|
66
|
+
def parallel_fork_run_test_suites(suites, reporter, options)
|
67
|
+
suites.each do |suite|
|
68
|
+
parallel_fork_run_test_suite(suite, reporter, options)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parallel_fork_run_test_suite(suite, reporter, options)
|
73
|
+
if suite.is_a?(Minitest::Parallel::Test::ClassMethods)
|
74
|
+
suite.extend(Minitest::Unparallelize)
|
75
|
+
end
|
76
|
+
|
77
|
+
suite.run(reporter, options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def parallel_fork_setup_children(suites, reporter, options)
|
81
|
+
set_parallel_fork_stat_reporter(reporter)
|
82
|
+
run_before_parallel_fork_hook
|
83
|
+
|
84
|
+
n = parallel_fork_number
|
85
|
+
n.times.map do |i|
|
45
86
|
read, write = IO.pipe.each{|io| io.binmode}
|
46
|
-
|
47
|
-
Process.fork do
|
87
|
+
pid = Process.fork do
|
48
88
|
read.close
|
49
|
-
|
50
|
-
@after_parallel_fork.call(i)
|
51
|
-
end
|
89
|
+
run_after_parallel_fork_hook(i)
|
52
90
|
|
53
91
|
p_suites = []
|
54
92
|
suites.each_with_index{|s, j| p_suites << s if j % n == i}
|
55
|
-
p_suites
|
56
|
-
if s.is_a?(Minitest::Parallel::Test::ClassMethods)
|
57
|
-
s.extend(Unparallelize)
|
58
|
-
end
|
93
|
+
parallel_fork_run_test_suites(p_suites, reporter, options)
|
59
94
|
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
data = %w'count assertions results'.map{|meth| stat_reporter.send(meth)}
|
64
|
-
write.write(Marshal.dump(data))
|
95
|
+
write.write(Marshal.dump(parallel_fork_data_to_marshal))
|
65
96
|
write.close
|
66
97
|
end
|
67
98
|
write.close
|
99
|
+
[pid, read]
|
68
100
|
end
|
101
|
+
end
|
69
102
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
raise
|
78
|
-
end
|
103
|
+
def parallel_fork_child_data(data)
|
104
|
+
data.map{|_pid, read| Thread.new(read, &:read)}.map(&:value).map{|data| parallel_fork_data_from_marshal(data)}
|
105
|
+
end
|
106
|
+
|
107
|
+
def parallel_fork_wait_for_children(child_info, reporter)
|
108
|
+
parallel_fork_child_data(child_info).each do |data|
|
109
|
+
count, assertions, results = data
|
79
110
|
reporter.reporters.each do |rep|
|
80
|
-
next unless %
|
111
|
+
next unless %i'count assertions results count= assertions='.all?{|meth| rep.respond_to?(meth)}
|
81
112
|
rep.count += count
|
82
113
|
rep.assertions += assertions
|
83
114
|
rep.results.concat(results)
|
84
115
|
end
|
85
116
|
end
|
117
|
+
end
|
86
118
|
|
119
|
+
def parallel_fork_number
|
120
|
+
(ENV['NCPU'] || 4).to_i
|
121
|
+
end
|
122
|
+
|
123
|
+
# Avoid method redefined verbose warning
|
124
|
+
alias __run __run
|
125
|
+
|
126
|
+
# Override __run to use a child forks to run the speeds, which
|
127
|
+
# allows for parallel spec execution on MRI.
|
128
|
+
def __run(reporter, options)
|
129
|
+
parallel_fork_wait_for_children(parallel_fork_setup_children(parallel_fork_suites, reporter, options), reporter)
|
87
130
|
nil
|
88
131
|
end
|
89
132
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minitest-parallel_fork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- MIT-LICENSE
|
72
72
|
- README.rdoc
|
73
73
|
- lib/minitest/parallel_fork.rb
|
74
|
+
- lib/minitest/parallel_fork/fail_fast.rb
|
74
75
|
homepage: http://github.com/jeremyevans/minitest-parallel_fork
|
75
76
|
licenses:
|
76
77
|
- MIT
|