minitest-parallel_fork 1.0.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 42f6c6919472888f0ad2799def6d83c39bf69ab6
4
- data.tar.gz: e4fe5385b3e8682001470c2c6891bc8eadb49696
2
+ SHA256:
3
+ metadata.gz: 9c152ef9216b8cbc9700f7f7e0281847695b325e660b51268b0d148a44e5aba2
4
+ data.tar.gz: 9d0739abeb8ccfaeb0ed023cb75b4fe30bb3f6e509af6d952204042229356649
5
5
  SHA512:
6
- metadata.gz: 007440c55981f82d53333b49efc3ca47609eed8ad9e7601fb215b125cabe4b8d809181a97b4f467e3f87a97373907dc169401de53f8b547711c9408f67842536
7
- data.tar.gz: 80d30b6fde62eaec6ca63ab203890a19a564243677b7bc9fa5ca01ac56a741c95f79b3dfb484d6b8abb52c2a64b35e7b1a2bb6e546a2dba94bf3ff0a434e8f2b
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
- disconnecting, and after forking, the database name is changed in each child to reference
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
@@ -13,7 +13,6 @@ end
13
13
  desc "Run specs"
14
14
  task :spec do
15
15
  ENV['RUBY'] = FileUtils::RUBY
16
- ENV['RUBYOPT'] = "#{ENV['RUBYOPT']} -rubygems"
17
16
  sh %{#{FileUtils::RUBY} spec/minitest_parallel_fork_spec.rb}
18
17
  end
19
18
 
@@ -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
- # Subclass of Assertion for unexpected errors. UnexpectedError
16
- # can not be used as it can include undumpable objects. This
17
- # class converts all data it needs to plain strings, so that
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.reporters.detect{|rep| rep.is_a?(StatisticsReporter)}
35
+ stat_reporter = parallel_fork_stat_reporter(reporter)
48
36
 
49
37
  n = (ENV['NCPU'] || 4).to_i
50
38
  reads = []
51
- if defined?(@before_parallel_fork)
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 defined?(@after_parallel_fork)
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
- data[-1] = data[-1].map do |res|
75
- [res.name, res.failures.map{|f| f.is_a?(UnexpectedError) ? DumpableUnexpectedError.new(f) : f}]
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
- reads.each do |r|
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
- stat_reporter.count += count
91
- stat_reporter.assertions += assertions
92
- results.map! do |name, failures|
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
- stat_reporter.results.concat(results)
100
+ raise
98
101
  end
99
- end.join
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).must_equal 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 /16 runs, 8 assertions, 4 failures, 4 errors, 4 skips/
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.2
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: 2017-02-27 00:00:00.000000000 Z
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: '5'
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: '5'
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
- rubyforge_project:
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