minitest-parallel_fork 1.0.2 → 1.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 +5 -5
- data/CHANGELOG +22 -0
- data/README.rdoc +11 -2
- data/Rakefile +0 -1
- data/lib/minitest/parallel_fork.rb +46 -40
- data/spec/minitest_parallel_fork_example.rb +20 -2
- data/spec/minitest_parallel_fork_spec.rb +16 -2
- metadata +22 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9c152ef9216b8cbc9700f7f7e0281847695b325e660b51268b0d148a44e5aba2
|
4
|
+
data.tar.gz: 9d0739abeb8ccfaeb0ed023cb75b4fe30bb3f6e509af6d952204042229356649
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e075bac95ab8dd13987e59533ace22ff48d878db63399659764f7af6c553cf835282b39044b50592ec776ab5ebc2d1af095bc9d8df955c0b230cce3cfe58a2e3
|
7
|
+
data.tar.gz: 57448ca69342f474102377fb287c67d298db98147bef22450d721cdae0acc4e9f65aad7d3e3937fc834118fd10e6200ced55d04609bc7ac43527dc1d6b63f5c7
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 1.2.0 (2021-08-16)
|
2
|
+
|
3
|
+
* Work correctly with Minitest 5.14+ (jeremyevans)
|
4
|
+
|
5
|
+
* Add on_parallel_fork_marshal_failure hook for handling marshal failures (child process exiting) (jeremyevans)
|
6
|
+
|
7
|
+
=== 1.1.2 (2018-07-05)
|
8
|
+
|
9
|
+
* Handle raised exceptions containing unmarshallable data (jeremyevans)
|
10
|
+
|
11
|
+
=== 1.1.1 (2018-05-07)
|
12
|
+
|
13
|
+
* Work around marshalling issue when there are failures in *_all hooks when using minitest-hooks (jeremyevans)
|
14
|
+
|
15
|
+
* Don't deadlock when a child process sends a large amount of marshalled data (chanks) (#4, #5)
|
16
|
+
|
17
|
+
=== 1.1.0 (2018-04-19)
|
18
|
+
|
19
|
+
* Remove DumpableUnexpectedError, require minitest 5.11.0+ (jeremyevans)
|
20
|
+
|
21
|
+
* Allow overriding the stat reporter used via Minitest.parallel_fork_stat_reporter method (jeremyevans) (#3)
|
22
|
+
|
1
23
|
=== 1.0.2 (2017-02-27)
|
2
24
|
|
3
25
|
* Show correct number of errors in output (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= minitest-parallel_fork
|
2
2
|
|
3
3
|
minitest-parallel_fork adds fork-based parallelization to Minitest. Each test/spec
|
4
|
-
suite is run one of the forks, allowing this to work correctly when using
|
4
|
+
suite is run in one of the forks, allowing this to work correctly when using
|
5
5
|
before_all/after_all/around_all hooks provided by minitest-hooks. Using separate
|
6
6
|
processes via fork can significantly improve spec performance when using MRI,
|
7
7
|
and can work in cases where Minitest's default thread-based parallelism do not work,
|
@@ -55,10 +55,19 @@ of the child process, starting at 0:
|
|
55
55
|
|
56
56
|
The above examples show a fairly easy way to use minitest-parallel_fork with an external
|
57
57
|
database when using Sequel. Before forking, all existing database connections are
|
58
|
-
|
58
|
+
disconnected, and after forking, the database name is changed in each child to reference
|
59
59
|
a child-specific database, so that the child processes do not share a database and are
|
60
60
|
thus independent.
|
61
61
|
|
62
|
+
There is also a hook for debugging. +on_parallel_fork_marshal_failure+ is called if
|
63
|
+
there is an error unmarshalling data sent from the child process to the parent process.
|
64
|
+
This can happen if one of the child processes exits unexpected during the test, before
|
65
|
+
it reports results.
|
66
|
+
|
67
|
+
Minitest.on_parallel_fork_marshal_failure do
|
68
|
+
# Gather relevant logs for more debugging
|
69
|
+
end
|
70
|
+
|
62
71
|
== ActiveRecord
|
63
72
|
|
64
73
|
To use this with Rails/ActiveRecord, you probably want to use hooks similar to:
|
data/Rakefile
CHANGED
@@ -6,49 +6,37 @@ module Minitest
|
|
6
6
|
def self.before_parallel_fork(&block)
|
7
7
|
@before_parallel_fork = block
|
8
8
|
end
|
9
|
+
@before_parallel_fork = nil
|
9
10
|
|
10
11
|
# Set the after_parallel_fork block to the given block
|
11
12
|
def self.after_parallel_fork(i=nil, &block)
|
12
13
|
@after_parallel_fork = block
|
13
14
|
end
|
15
|
+
@after_parallel_fork = nil
|
14
16
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
# it will be dumpable.
|
19
|
-
class DumpableUnexpectedError < Assertion # :nodoc:
|
20
|
-
attr_accessor :backtrace
|
21
|
-
|
22
|
-
def initialize(unexpected)
|
23
|
-
exception_class_name = unexpected.exception.class.name.to_s
|
24
|
-
exception_message = unexpected.exception.message.to_s
|
25
|
-
super("#{exception_class_name}: #{exception_message}")
|
26
|
-
self.backtrace = unexpected.exception.backtrace.map(&:to_s)
|
27
|
-
end
|
28
|
-
|
29
|
-
def message
|
30
|
-
bt = Minitest.filter_backtrace(backtrace).join "\n "
|
31
|
-
"#{super}\n #{bt}"
|
32
|
-
end
|
33
|
-
|
34
|
-
def result_label
|
35
|
-
"Error"
|
36
|
-
end
|
17
|
+
# Set the on_parallel_fork_marshal_failure block to the given block
|
18
|
+
def self.on_parallel_fork_marshal_failure(&block)
|
19
|
+
@on_parallel_fork_marshal_failure = block
|
37
20
|
end
|
21
|
+
@on_parallel_fork_marshal_failure = nil
|
38
22
|
|
39
23
|
module Unparallelize
|
40
24
|
define_method(:run_one_method, &Minitest::Test.method(:run_one_method))
|
41
25
|
end
|
26
|
+
|
27
|
+
def self.parallel_fork_stat_reporter(reporter)
|
28
|
+
reporter.reporters.detect{|rep| rep.is_a?(StatisticsReporter)}
|
29
|
+
end
|
42
30
|
|
43
31
|
# Override __run to use a child forks to run the speeds, which
|
44
32
|
# allows for parallel spec execution on MRI.
|
45
33
|
def self.__run(reporter, options)
|
46
34
|
suites = Runnable.runnables.shuffle
|
47
|
-
stat_reporter = reporter
|
35
|
+
stat_reporter = parallel_fork_stat_reporter(reporter)
|
48
36
|
|
49
37
|
n = (ENV['NCPU'] || 4).to_i
|
50
38
|
reads = []
|
51
|
-
if
|
39
|
+
if @before_parallel_fork
|
52
40
|
@before_parallel_fork.call
|
53
41
|
end
|
54
42
|
n.times do |i|
|
@@ -56,7 +44,7 @@ module Minitest
|
|
56
44
|
reads << read
|
57
45
|
fork do
|
58
46
|
read.close
|
59
|
-
if
|
47
|
+
if @after_parallel_fork
|
60
48
|
@after_parallel_fork.call(i)
|
61
49
|
end
|
62
50
|
|
@@ -71,8 +59,29 @@ module Minitest
|
|
71
59
|
end
|
72
60
|
|
73
61
|
data = %w'count assertions results'.map{|meth| stat_reporter.send(meth)}
|
74
|
-
|
75
|
-
[
|
62
|
+
if data[-1].any?{|result| !result.is_a?(Minitest::Result)}
|
63
|
+
data[-1] = data[-1].map do |result|
|
64
|
+
Minitest::Result.from(result)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
data[-1].each do |result|
|
69
|
+
result.failures.each do |failure|
|
70
|
+
if failure.is_a?(Minitest::UnexpectedError)
|
71
|
+
e = failure.respond_to?(:error) ? failure.error : failure.exception
|
72
|
+
begin
|
73
|
+
Marshal.dump(e)
|
74
|
+
rescue TypeError
|
75
|
+
e2 = RuntimeError.new("Wrapped undumpable exception for: #{e.class}: #{e.message}")
|
76
|
+
e2.set_backtrace(e.backtrace)
|
77
|
+
if failure.respond_to?(:error=)
|
78
|
+
failure.error = e2
|
79
|
+
else
|
80
|
+
failure.exception = e2
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
76
85
|
end
|
77
86
|
|
78
87
|
write.write(Marshal.dump(data))
|
@@ -80,23 +89,20 @@ module Minitest
|
|
80
89
|
end
|
81
90
|
write.close
|
82
91
|
end
|
83
|
-
Process.waitall
|
84
92
|
|
85
|
-
Thread.new do
|
86
|
-
|
87
|
-
data = r.read
|
88
|
-
r.close
|
93
|
+
reads.map{|read| Thread.new(read, &:read)}.map(&:value).each do |data|
|
94
|
+
begin
|
89
95
|
count, assertions, results = Marshal.load(data)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
runnable = Test.new(name)
|
94
|
-
runnable.failures.concat(failures.map{|f| f.is_a?(DumpableUnexpectedError) ? UnexpectedError.new(f) : f})
|
95
|
-
runnable
|
96
|
+
rescue ArgumentError
|
97
|
+
if @on_parallel_fork_marshal_failure
|
98
|
+
@on_parallel_fork_marshal_failure.call
|
96
99
|
end
|
97
|
-
|
100
|
+
raise
|
98
101
|
end
|
99
|
-
|
102
|
+
stat_reporter.count += count
|
103
|
+
stat_reporter.assertions += assertions
|
104
|
+
stat_reporter.results.concat(results)
|
105
|
+
end
|
100
106
|
|
101
107
|
nil
|
102
108
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
gem 'minitest'
|
2
|
-
require 'minitest/autorun'
|
2
|
+
require 'minitest/global_expectations/autorun'
|
3
3
|
require 'minitest/parallel_fork'
|
4
4
|
|
5
5
|
a = nil
|
@@ -12,6 +12,16 @@ Minitest.after_parallel_fork do |i|
|
|
12
12
|
print ":child#{i}#{a}"
|
13
13
|
end
|
14
14
|
|
15
|
+
if ENV['MPF_TEST_CHILD_FAILURE']
|
16
|
+
Minitest.on_parallel_fork_marshal_failure do |i|
|
17
|
+
print ":child-failure#{i}#{a}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class MyExceptionClass < StandardError
|
22
|
+
attr_reader :something
|
23
|
+
end
|
24
|
+
|
15
25
|
4.times do |i|
|
16
26
|
describe 'minitest/parallel_fork' do
|
17
27
|
parallelize_me! if ENV['MPF_PARALLELIZE_ME']
|
@@ -23,7 +33,8 @@ end
|
|
23
33
|
end
|
24
34
|
|
25
35
|
it "should work" do
|
26
|
-
sleep(1)
|
36
|
+
sleep(1)
|
37
|
+
1.must_equal 1
|
27
38
|
end
|
28
39
|
|
29
40
|
it "should fail" do
|
@@ -31,9 +42,16 @@ end
|
|
31
42
|
end
|
32
43
|
|
33
44
|
it "should raise" do
|
45
|
+
exit(1) if ENV['MPF_TEST_CHILD_FAILURE']
|
34
46
|
raise
|
35
47
|
end
|
36
48
|
|
49
|
+
it "should raise exception containing undumpable data" do
|
50
|
+
e = MyExceptionClass.new("error")
|
51
|
+
e.something = Class.new
|
52
|
+
raise e
|
53
|
+
end
|
54
|
+
|
37
55
|
it "should skip" do
|
38
56
|
skip
|
39
57
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
+
ENV['MT_NO_PLUGINS'] = '1'
|
1
2
|
gem 'minitest'
|
2
|
-
require 'minitest/autorun'
|
3
|
+
require 'minitest/global_expectations/autorun'
|
3
4
|
|
4
5
|
describe 'minitest/parallel_fork' do
|
5
6
|
[[nil, ''],
|
@@ -17,10 +18,23 @@ describe 'minitest/parallel_fork' do
|
|
17
18
|
time.must_be :<, 4
|
18
19
|
time.must_be :>, 1
|
19
20
|
output.must_match /:parent/
|
20
|
-
output.must_match /
|
21
|
+
output.must_match /20 runs, 8 assertions, 4 failures, 8 errors, 4 skips/
|
21
22
|
4.times do |i|
|
22
23
|
output.must_match /:child#{i}a/
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
27
|
+
|
28
|
+
it "should call on_parallel_fork_marshal_failure on failure" do
|
29
|
+
t = Time.now
|
30
|
+
ENV['NCPU'] = '4'
|
31
|
+
ENV['MPF_TEST_CHILD_FAILURE'] = '1'
|
32
|
+
output = `#{ENV['RUBY']} -I lib spec/minitest_parallel_fork_example.rb 2>&1`
|
33
|
+
ENV.delete('MPF_TEST_CHILD_FAILURE')
|
34
|
+
|
35
|
+
time = (Time.now - t)
|
36
|
+
time.must_be :<, 4
|
37
|
+
output.must_match /:child-failurea/
|
38
|
+
output.must_match /marshal data too short/
|
39
|
+
end
|
26
40
|
end
|
metadata
CHANGED
@@ -1,32 +1,46 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minitest-parallel_fork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.2.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:
|
11
|
+
date: 2021-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.11.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.11.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-global_expectations
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description: |
|
28
42
|
minitest-parallel_fork adds fork-based parallelization to Minitest. Each test/spec
|
29
|
-
suite is run one of the forks, allowing this to work correctly when using
|
43
|
+
suite is run in one of the forks, allowing this to work correctly when using
|
30
44
|
before_all/after_all/around_all hooks provided by minitest-hooks. Using separate
|
31
45
|
processes via fork can significantly improve spec performance when using MRI,
|
32
46
|
and can work in cases where Minitest's default thread-based parallelism do not work,
|
@@ -72,8 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
86
|
- !ruby/object:Gem::Version
|
73
87
|
version: '0'
|
74
88
|
requirements: []
|
75
|
-
|
76
|
-
rubygems_version: 2.6.8
|
89
|
+
rubygems_version: 3.2.22
|
77
90
|
signing_key:
|
78
91
|
specification_version: 4
|
79
92
|
summary: Fork-based parallelization for minitest
|