minitest-parallel_fork 2.0.0 → 2.1.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
2
  SHA256:
3
- metadata.gz: 2698fbcfb5e14bed9fbf23e2abe38c5395b615f4443e335ce606b2f674883e30
4
- data.tar.gz: dff7fbdc3104a729405415688ff35e574d65df94ac0c2086f3791e5005a95dfd
3
+ metadata.gz: 617125ad38c5fbb98d110eb3374a6f4ffac5d95ffedfed197c6354e7b273d7ef
4
+ data.tar.gz: 7e4768a75b06806da43547902dc02c89ae793a907ea2dc0f00dc18a5bf69ec82
5
5
  SHA512:
6
- metadata.gz: 80b75938b994edb1f6cfe17c5b8ad31550ab6081738f178ac3dd3c991a7a61fd1a6730d0e855cec3d1d0f5113dddda6ecec455f310ccf4ebcc74298a763a43ae
7
- data.tar.gz: 398db64ae6468875db78f49ba7221f8cb1f789161adf361a2125daee43a85acd194c0afbbeb1faab35290c72bc377b4eba1cd01dbfba9ace41255d6278eb4a5b
6
+ metadata.gz: 79d9a44f01350fe19f9e50cfdbef0957b692ddf2fc5322ba91d2ceadf3c0e6c091681e08c8c3b5b86f9115ae3ac80055aa9c5eea8e88f076be5d9b161a888e9d
7
+ data.tar.gz: e86395fb13775e0a4d11249639446ff64a5ca8743350f261cfc8f01b6b4416162e02d15b9977071f75801b95a4fe51a07832749e0493eeb78d67a2b90cd2d63c
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ === 2.1.0 (2025-07-02)
2
+
3
+ * Add minitest/parallel_fork/interrupt with support handling interrupts (stackmystack, jeremyevans) (#14)
4
+
1
5
  === 2.0.0 (2023-11-08)
2
6
 
3
7
  * Avoid method redefined warning in verbose warning mode (jeremyevans)
data/README.rdoc CHANGED
@@ -79,6 +79,20 @@ Note that minitest-parallel_fork uses suite-based parallelism, so tests will not
79
79
  stop until one child has a failing test suite (test class that has a failing test
80
80
  method), and other children are signaled and also stop processing.
81
81
 
82
+ == Interrupt Support
83
+
84
+ If you would like to run tests in parallel, but allow for shutting down children
85
+ when SIGINT is sent to the process, you can use:
86
+
87
+ RUBYOPT=-rminitest/parallel_fork/interrupt ruby spec_runner.rb
88
+
89
+ The first SIGINT to a process will ask the child processes to shut down gracefully
90
+ with SIGUSR1. The second SIGINT to a process will kill the child processes with
91
+ SIGKILL.
92
+
93
+ Note that if you use rake to run the specs, the second SIGINT may not be sent to
94
+ to the parent process, as rake does it's own SIGINT handling.
95
+
82
96
  == ActiveRecord
83
97
 
84
98
  To use this with Rails/ActiveRecord, you probably want to use hooks similar to:
@@ -1,69 +1,17 @@
1
+ require_relative 'halt'
1
2
  require_relative '../parallel_fork'
2
3
 
3
4
  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
5
+ include Minitest::ParallelForkHalt
30
6
 
31
7
  def parallel_fork_run_test_suite(suite, reporter, options)
32
8
  super
33
9
 
34
-
35
10
  if parallel_fork_stat_reporter.results.any?{|r| !r.failure.is_a?(Minitest::Skip)}
36
11
  # At least one failure or error, mark as failing fast
37
12
  @parallel_fork_stop = true
38
13
  end
39
14
  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
15
  end
68
16
 
69
17
  Minitest.singleton_class.prepend(Minitest::ParalleForkFailFast)
@@ -0,0 +1,55 @@
1
+ module Minitest::ParallelForkHalt
2
+ def run_after_parallel_fork_hook(i)
3
+ super
4
+ Signal.trap(:USR1) do
5
+ @parallel_fork_stop = true
6
+ end
7
+ end
8
+
9
+ def parallel_fork_data_to_marshal
10
+ super << @parallel_fork_stop
11
+ end
12
+
13
+ def parallel_fork_data_from_marshal(data)
14
+ data = Marshal.load(data)
15
+ @parallel_fork_stop = true if data.pop
16
+ data
17
+ end
18
+
19
+ def parallel_fork_run_test_suites(suites, reporter, options)
20
+ suites.each do |suite|
21
+ parallel_fork_run_test_suite(suite, reporter, options)
22
+
23
+ # Halt if this child process requested an exit,
24
+ # Or other child processes requested an exit.
25
+ break if @parallel_fork_stop
26
+ end
27
+ end
28
+
29
+ def parallel_fork_child_data(data)
30
+ threads = {}
31
+ data.each{|pid, read| threads[pid] = Thread.new(read, &:read)}
32
+ results = []
33
+
34
+ while sleep(0.01) && !threads.empty?
35
+ threads.to_a.each do |pid, thread|
36
+ unless thread.alive?
37
+ threads.delete(pid)
38
+ results << parallel_fork_data_from_marshal(thread.value)
39
+
40
+ if @parallel_fork_stop
41
+ # If halt is requested, signal other children to halt
42
+ threads.each_key do |pid|
43
+ Process.kill(:USR1, pid)
44
+ end
45
+
46
+ # Set a flag indicating that all child processes have been signaled
47
+ @parallel_fork_stop = :FINISHED
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ results
54
+ end
55
+ end
@@ -0,0 +1,45 @@
1
+ require 'minitest'
2
+
3
+ require_relative 'halt'
4
+ require_relative '../parallel_fork'
5
+
6
+ module Minitest
7
+ @parallel_fork_child_pids = []
8
+ end
9
+
10
+ module Minitest::ParallelForkInterrupt
11
+ include Minitest::ParallelForkHalt
12
+
13
+ def run_before_parallel_fork_hook
14
+ Signal.trap(:INT) do
15
+ Signal.trap(:INT) do
16
+ parallel_fork_kill_all :KILL
17
+ end
18
+ $stderr.puts "\nInterrupted.\nExiting ...\nInterrupt again to exit immediately."
19
+ parallel_fork_kill_all :USR1
20
+ end
21
+ end
22
+
23
+ def run_after_parallel_fork_hook(i)
24
+ super
25
+ Signal.trap(:INT, 'IGNORE')
26
+ end
27
+
28
+ def parallel_fork_fork_child(i, suites, reporter, options)
29
+ res = super
30
+ @parallel_fork_child_pids << res[0]
31
+ res
32
+ end
33
+
34
+ def parallel_fork_kill_all(signal)
35
+ @parallel_fork_child_pids.each do |pid|
36
+ begin
37
+ Process.kill(signal, pid)
38
+ rescue Errno::ESRCH
39
+ # Process already terminated
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ Minitest.singleton_class.prepend(Minitest::ParallelForkInterrupt)
@@ -81,23 +81,27 @@ class << Minitest
81
81
  set_parallel_fork_stat_reporter(reporter)
82
82
  run_before_parallel_fork_hook
83
83
 
84
- n = parallel_fork_number
85
- n.times.map do |i|
86
- read, write = IO.pipe.each{|io| io.binmode}
87
- pid = Process.fork do
88
- read.close
89
- run_after_parallel_fork_hook(i)
90
-
91
- p_suites = []
92
- suites.each_with_index{|s, j| p_suites << s if j % n == i}
93
- parallel_fork_run_test_suites(p_suites, reporter, options)
94
-
95
- write.write(Marshal.dump(parallel_fork_data_to_marshal))
96
- write.close
97
- end
84
+ parallel_fork_number.times.map do |i|
85
+ parallel_fork_fork_child(i, suites, reporter, options)
86
+ end
87
+ end
88
+
89
+ def parallel_fork_fork_child(i, suites, reporter, options)
90
+ read, write = IO.pipe.each{|io| io.binmode}
91
+ pid = Process.fork do
92
+ read.close
93
+ run_after_parallel_fork_hook(i)
94
+
95
+ p_suites = []
96
+ n = parallel_fork_number
97
+ suites.each_with_index{|s, j| p_suites << s if j % n == i}
98
+ parallel_fork_run_test_suites(p_suites, reporter, options)
99
+
100
+ write.write(Marshal.dump(parallel_fork_data_to_marshal))
98
101
  write.close
99
- [pid, read]
100
102
  end
103
+ write.close
104
+ [pid, read]
101
105
  end
102
106
 
103
107
  def parallel_fork_child_data(data)
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitest-parallel_fork
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-11-08 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: minitest
@@ -63,15 +62,17 @@ email: code@jeremyevans.net
63
62
  executables: []
64
63
  extensions: []
65
64
  extra_rdoc_files:
66
- - README.rdoc
67
65
  - CHANGELOG
68
66
  - MIT-LICENSE
67
+ - README.rdoc
69
68
  files:
70
69
  - CHANGELOG
71
70
  - MIT-LICENSE
72
71
  - README.rdoc
73
72
  - lib/minitest/parallel_fork.rb
74
73
  - lib/minitest/parallel_fork/fail_fast.rb
74
+ - lib/minitest/parallel_fork/halt.rb
75
+ - lib/minitest/parallel_fork/interrupt.rb
75
76
  homepage: http://github.com/jeremyevans/minitest-parallel_fork
76
77
  licenses:
77
78
  - MIT
@@ -80,7 +81,6 @@ metadata:
80
81
  changelog_uri: https://github.com/jeremyevans/minitest-parallel_fork/blob/master/CHANGELOG
81
82
  mailing_list_uri: https://github.com/jeremyevans/minitest-parallel_fork/discussions
82
83
  source_code_uri: https://github.com/jeremyevans/minitest-parallel_fork
83
- post_install_message:
84
84
  rdoc_options:
85
85
  - "--quiet"
86
86
  - "--line-numbers"
@@ -102,8 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []
105
- rubygems_version: 3.4.10
106
- signing_key:
105
+ rubygems_version: 3.6.7
107
106
  specification_version: 4
108
107
  summary: Fork-based parallelization for minitest
109
108
  test_files: []