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 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