rspec-multiprocess_runner 0.1.0 → 0.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 +7 -0
- data/CHANGELOG.md +13 -0
- data/README.md +13 -2
- data/exe/multirspec +21 -5
- data/lib/rspec/multiprocess_runner/command_line_options.rb +19 -6
- data/lib/rspec/multiprocess_runner/coordinator.rb +79 -16
- data/lib/rspec/multiprocess_runner/rake_task.rb +8 -1
- data/lib/rspec/multiprocess_runner/version.rb +1 -1
- data/lib/rspec/multiprocess_runner/worker.rb +62 -22
- metadata +20 -36
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4c75ed167254b35531b51f11a1dda272f3587055
|
4
|
+
data.tar.gz: c5cb965e6c3e0caaecc65f7e4236124c98d407af
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 35139e859a97c7b20cbff792b652be8172c1a70dbed65ac2277bf53a18486092cae8199820782d8747ce4830cbf927ec73f4ae6625888f2939b10a6103668c0e
|
7
|
+
data.tar.gz: 79868c598e2dfc09f467ccf9badc6382afce4b9930f6711784bdb7f336878a3921608a8fd240dbaf77fdac6818be1c99816b20687fe29755f9f4259e895738ad
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 0.2.0
|
2
|
+
|
3
|
+
* Terminate workers when the coordinator process is interrupted with SIGINT (^C)
|
4
|
+
or SIGTERM (`kill` with no args) (#3)
|
5
|
+
* Add per-example timeout option. When using `multirspec` or the rake tasks, it
|
6
|
+
defaults to 15 seconds (#2)
|
7
|
+
* **Breaking change**: per-file timeout is now disabled by default (#2)
|
8
|
+
* **Breaking change**: changed arguments for `Coordinator`'s constructor
|
9
|
+
* Correct signal used when requesting that stalled processes quit (use TERM
|
10
|
+
instead of QUIT)
|
11
|
+
* Stop idle processes once there's no work, instead of waiting and stopping
|
12
|
+
everything when the suite is complete
|
13
|
+
|
1
14
|
# 0.1.0
|
2
15
|
|
3
16
|
* Initial version
|
data/README.md
CHANGED
@@ -95,16 +95,27 @@ Create a coordinator and tell it to run:
|
|
95
95
|
|
96
96
|
worker_count = 4
|
97
97
|
per_file_timeout = 5 * 60 # 5 minutes in seconds
|
98
|
-
|
98
|
+
rspec_options = %w(--backtrace)
|
99
99
|
files = Dir['**/*_spec.rb']
|
100
100
|
|
101
|
-
coordinator = RSpec::MultiprocessRunner::Coordinator(
|
101
|
+
coordinator = RSpec::MultiprocessRunner::Coordinator.new(
|
102
|
+
worker_count, files,
|
103
|
+
{
|
104
|
+
file_timeout_seconds: per_file_timeout,
|
105
|
+
rspec_options: rspec_options
|
106
|
+
}
|
107
|
+
)
|
102
108
|
coordinator.run
|
103
109
|
|
104
110
|
## Contributing
|
105
111
|
|
106
112
|
Bug reports and pull requests are welcome on GitHub at https://github.com/cdd/rspec-multiprocess_runner.
|
107
113
|
|
114
|
+
### Project infrastructure
|
115
|
+
|
116
|
+
* [Source on GitHub](https://github.com/cdd/rspec-multiprocess_runner)
|
117
|
+
* [](https://travis-ci.org/cdd/rspec-multiprocess_runner)
|
118
|
+
[Continuous Integration on Travis-CI](https://travis-ci.org/cdd/rspec-multiprocess_runner)
|
108
119
|
|
109
120
|
## License
|
110
121
|
|
data/exe/multirspec
CHANGED
@@ -6,11 +6,27 @@ require 'rspec/multiprocess_runner/coordinator'
|
|
6
6
|
options = RSpec::MultiprocessRunner::CommandLineOptions.new.parse(ARGV.dup)
|
7
7
|
exit(2) unless options
|
8
8
|
|
9
|
-
|
9
|
+
coordinator = RSpec::MultiprocessRunner::Coordinator.new(
|
10
10
|
options.worker_count,
|
11
|
-
options.
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
options.files_to_run,
|
12
|
+
{
|
13
|
+
file_timeout_seconds: options.file_timeout_seconds,
|
14
|
+
example_timeout_seconds: options.example_timeout_seconds,
|
15
|
+
rspec_options: options.rspec_options
|
16
|
+
}
|
17
|
+
)
|
18
|
+
|
19
|
+
trap("INT") do
|
20
|
+
$stderr.puts "INT happened"
|
21
|
+
coordinator.shutdown(print_summary: true)
|
22
|
+
Kernel.exit(4)
|
23
|
+
end
|
24
|
+
|
25
|
+
trap("TERM") do
|
26
|
+
coordinator.shutdown
|
27
|
+
Kernel.exit(3)
|
28
|
+
end
|
29
|
+
|
30
|
+
success = coordinator.run
|
15
31
|
|
16
32
|
exit(success ? 0 : 1)
|
@@ -5,12 +5,13 @@ require 'pathname'
|
|
5
5
|
module RSpec::MultiprocessRunner
|
6
6
|
# @private
|
7
7
|
class CommandLineOptions
|
8
|
-
attr_accessor :worker_count, :file_timeout_seconds, :
|
9
|
-
:explicit_files_or_directories, :pattern
|
8
|
+
attr_accessor :worker_count, :file_timeout_seconds, :example_timeout_seconds,
|
9
|
+
:rspec_options, :explicit_files_or_directories, :pattern
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
self.worker_count = 3
|
13
|
-
self.file_timeout_seconds =
|
13
|
+
self.file_timeout_seconds = nil
|
14
|
+
self.example_timeout_seconds = 15
|
14
15
|
self.pattern = "**/*_spec.rb"
|
15
16
|
self.rspec_options = []
|
16
17
|
end
|
@@ -58,19 +59,31 @@ module RSpec::MultiprocessRunner
|
|
58
59
|
@help_requested
|
59
60
|
end
|
60
61
|
|
62
|
+
def print_default(default_value)
|
63
|
+
if default_value
|
64
|
+
"default: #{default_value}"
|
65
|
+
else
|
66
|
+
"none by default"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
61
70
|
def build_parser
|
62
71
|
OptionParser.new do |parser|
|
63
72
|
parser.banner = "#{File.basename $0} [options] [files or directories] [-- rspec options]"
|
64
73
|
|
65
|
-
parser.on("-w", "--worker-count COUNT", Integer, "Number of workers to run (
|
74
|
+
parser.on("-w", "--worker-count COUNT", Integer, "Number of workers to run (#{print_default worker_count})") do |n|
|
66
75
|
self.worker_count = n
|
67
76
|
end
|
68
77
|
|
69
|
-
parser.on("-t", "--file-timeout SECONDS",
|
78
|
+
parser.on("-t", "--file-timeout SECONDS", Float, "Maximum time to allow any single file to run (#{print_default file_timeout_seconds})") do |s|
|
70
79
|
self.file_timeout_seconds = s
|
71
80
|
end
|
72
81
|
|
73
|
-
parser.on("-
|
82
|
+
parser.on("-T", "--example-timeout SECONDS", Float, "Maximum time to allow any single example to run (#{print_default example_timeout_seconds})") do |s|
|
83
|
+
self.example_timeout_seconds = s
|
84
|
+
end
|
85
|
+
|
86
|
+
parser.on("-P", "--pattern PATTERN", "A glob to use to select files to run (#{print_default pattern})") do |pattern|
|
74
87
|
self.pattern = pattern
|
75
88
|
end
|
76
89
|
|
@@ -4,13 +4,14 @@ require 'rspec/multiprocess_runner/worker'
|
|
4
4
|
|
5
5
|
module RSpec::MultiprocessRunner
|
6
6
|
class Coordinator
|
7
|
-
def initialize(worker_count,
|
7
|
+
def initialize(worker_count, files, options={})
|
8
8
|
@worker_count = worker_count
|
9
|
-
@
|
10
|
-
@
|
9
|
+
@file_timeout_seconds = options[:file_timeout_seconds]
|
10
|
+
@example_timeout_seconds = options[:example_timeout_seconds]
|
11
|
+
@rspec_options = options[:rspec_options]
|
11
12
|
@spec_files = files
|
12
13
|
@workers = []
|
13
|
-
@
|
14
|
+
@stopped_workers = []
|
14
15
|
end
|
15
16
|
|
16
17
|
def run
|
@@ -26,7 +27,23 @@ module RSpec::MultiprocessRunner
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def failed?
|
29
|
-
!@
|
30
|
+
!failed_workers.empty? || !@spec_files.empty? || any_example_failed?
|
31
|
+
end
|
32
|
+
|
33
|
+
def shutdown(options={})
|
34
|
+
if @shutting_down
|
35
|
+
# Immediately kill the workers if shutdown is requested again
|
36
|
+
end_workers_in_parallel(@workers.dup, :kill_now)
|
37
|
+
else
|
38
|
+
@shutting_down = true
|
39
|
+
print "Shutting down #{pluralize(@workers.size, "worker")} …" if options[:print_summary]
|
40
|
+
# end_workers_in_parallel modifies @workers, so dup before sending in
|
41
|
+
end_workers_in_parallel(@workers.dup, :shutdown_now)
|
42
|
+
if options[:print_summary]
|
43
|
+
puts " done"
|
44
|
+
print_summary
|
45
|
+
end
|
46
|
+
end
|
30
47
|
end
|
31
48
|
|
32
49
|
private
|
@@ -40,28 +57,46 @@ module RSpec::MultiprocessRunner
|
|
40
57
|
act_on_available_worker_messages(0.3)
|
41
58
|
reap_stalled_workers
|
42
59
|
start_missing_workers
|
60
|
+
quit_idle_unnecessary_workers
|
43
61
|
break unless @workers.detect(&:working?)
|
44
62
|
end
|
45
63
|
end
|
46
64
|
|
47
65
|
def quit_all_workers
|
48
|
-
|
66
|
+
# quit_workers modifies @workers, so dup before sending in
|
67
|
+
quit_workers(@workers.dup)
|
68
|
+
end
|
69
|
+
|
70
|
+
def end_workers_in_parallel(some_workers, end_method)
|
71
|
+
end_threads = some_workers.map do |worker|
|
72
|
+
# This method is not threadsafe because it updates instance variables.
|
73
|
+
# But it's fine to run it outside of the thread because it doesn't
|
74
|
+
# block.
|
75
|
+
mark_worker_as_stopped(worker)
|
49
76
|
Thread.new do
|
50
|
-
worker.
|
51
|
-
worker.wait_until_quit
|
77
|
+
worker.send(end_method)
|
52
78
|
end
|
53
79
|
end
|
54
|
-
|
80
|
+
end_threads.each(&:join)
|
81
|
+
end
|
82
|
+
|
83
|
+
def quit_workers(some_workers)
|
84
|
+
end_workers_in_parallel(some_workers, :quit_when_idle_and_wait_for_quit)
|
55
85
|
end
|
56
86
|
|
57
87
|
def work_left_to_do?
|
58
88
|
!@spec_files.empty?
|
59
89
|
end
|
60
90
|
|
91
|
+
def failed_workers
|
92
|
+
@stopped_workers.select { |w| w.deactivation_reason }
|
93
|
+
end
|
94
|
+
|
61
95
|
def act_on_available_worker_messages(timeout)
|
62
96
|
while (select_result = IO.select(worker_sockets, nil, nil, timeout))
|
63
97
|
select_result.first.each do |readable_socket|
|
64
98
|
ready_worker = @workers.detect { |worker| worker.socket == readable_socket }
|
99
|
+
next unless ready_worker # Worker is already gone
|
65
100
|
worker_status = ready_worker.receive_and_act_on_message_from_worker
|
66
101
|
if worker_status == :dead
|
67
102
|
reap_one_worker(ready_worker, "died")
|
@@ -74,8 +109,12 @@ module RSpec::MultiprocessRunner
|
|
74
109
|
|
75
110
|
def reap_one_worker(worker, reason)
|
76
111
|
worker.reap
|
77
|
-
@deactivated_workers << worker
|
78
112
|
worker.deactivation_reason = reason
|
113
|
+
mark_worker_as_stopped(worker)
|
114
|
+
end
|
115
|
+
|
116
|
+
def mark_worker_as_stopped(worker)
|
117
|
+
@stopped_workers << worker
|
79
118
|
@workers.reject! { |w| w == worker }
|
80
119
|
end
|
81
120
|
|
@@ -92,7 +131,12 @@ module RSpec::MultiprocessRunner
|
|
92
131
|
def create_and_start_worker_if_necessary(n)
|
93
132
|
if work_left_to_do?
|
94
133
|
$stderr.puts "(Re)starting worker #{n}"
|
95
|
-
new_worker = Worker.new(
|
134
|
+
new_worker = Worker.new(
|
135
|
+
n,
|
136
|
+
file_timeout_seconds: @file_timeout_seconds,
|
137
|
+
example_timeout_seconds: @example_timeout_seconds,
|
138
|
+
rspec_options: @rspec_options
|
139
|
+
)
|
96
140
|
@workers << new_worker
|
97
141
|
new_worker.start
|
98
142
|
new_worker.run_file(@spec_files.shift)
|
@@ -109,11 +153,19 @@ module RSpec::MultiprocessRunner
|
|
109
153
|
end
|
110
154
|
end
|
111
155
|
|
156
|
+
def quit_idle_unnecessary_workers
|
157
|
+
unless work_left_to_do?
|
158
|
+
idle_workers = @workers.reject(&:working?)
|
159
|
+
quit_workers(idle_workers)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
112
163
|
def print_summary
|
113
164
|
elapsed = Time.now - @start_time
|
114
165
|
by_status_and_time = combine_example_results.each_with_object({}) do |result, idx|
|
115
166
|
(idx[result.status] ||= []) << result
|
116
167
|
end
|
168
|
+
print_skipped_files_details
|
117
169
|
print_pending_example_details(by_status_and_time["pending"])
|
118
170
|
print_failed_example_details(by_status_and_time["failed"])
|
119
171
|
print_failed_process_details
|
@@ -124,11 +176,20 @@ module RSpec::MultiprocessRunner
|
|
124
176
|
end
|
125
177
|
|
126
178
|
def combine_example_results
|
127
|
-
(@workers + @
|
179
|
+
(@workers + @stopped_workers).flat_map(&:example_results).sort_by { |r| r.time_finished }
|
128
180
|
end
|
129
181
|
|
130
182
|
def any_example_failed?
|
131
|
-
(@workers + @
|
183
|
+
(@workers + @stopped_workers).detect { |w| w.example_results.detect { |r| r.status == "failed" } }
|
184
|
+
end
|
185
|
+
|
186
|
+
def print_skipped_files_details
|
187
|
+
return if @spec_files.empty?
|
188
|
+
puts
|
189
|
+
puts "Skipped files:"
|
190
|
+
@spec_files.each do |spec_file|
|
191
|
+
puts " - #{spec_file}"
|
192
|
+
end
|
132
193
|
end
|
133
194
|
|
134
195
|
def print_pending_example_details(pending_example_results)
|
@@ -161,21 +222,23 @@ module RSpec::MultiprocessRunner
|
|
161
222
|
example_count = by_status_and_time.map { |status, results| results.size }.inject(0) { |sum, ct| sum + ct }
|
162
223
|
failure_count = by_status_and_time["failed"] ? by_status_and_time["failed"].size : 0
|
163
224
|
pending_count = by_status_and_time["pending"] ? by_status_and_time["pending"].size : 0
|
164
|
-
process_failure_count =
|
225
|
+
process_failure_count = failed_workers.size
|
226
|
+
skipped_count = @spec_files.size
|
165
227
|
|
166
228
|
# Copied from RSpec
|
167
229
|
summary = pluralize(example_count, "example")
|
168
230
|
summary << ", " << pluralize(failure_count, "failure")
|
169
231
|
summary << ", #{pending_count} pending" if pending_count > 0
|
170
232
|
summary << ", " << pluralize(process_failure_count, "failed proc") if process_failure_count > 0
|
233
|
+
summary << ", " << pluralize(skipped_count, "skipped file") if skipped_count > 0
|
171
234
|
puts summary
|
172
235
|
end
|
173
236
|
|
174
237
|
def print_failed_process_details
|
175
|
-
return if
|
238
|
+
return if failed_workers.empty?
|
176
239
|
puts
|
177
240
|
puts "Failed processes:"
|
178
|
-
|
241
|
+
failed_workers.each do |worker|
|
179
242
|
puts " - #{worker.pid} (env #{worker.environment_number}) #{worker.deactivation_reason} on #{worker.current_file}"
|
180
243
|
end
|
181
244
|
end
|
@@ -30,9 +30,13 @@ module RSpec::MultiprocessRunner
|
|
30
30
|
attr_accessor :worker_count
|
31
31
|
|
32
32
|
# The maximum number of seconds to allow a single spec file to run
|
33
|
-
# before killing it. Defaults to
|
33
|
+
# before killing it. Defaults to disabled.
|
34
34
|
attr_accessor :file_timeout_seconds
|
35
35
|
|
36
|
+
# The maximum number of seconds to allow a single example to run
|
37
|
+
# before killing it. Defaults to 15.
|
38
|
+
attr_accessor :example_timeout_seconds
|
39
|
+
|
36
40
|
# Whether or not to fail Rake when an error occurs (typically when
|
37
41
|
# examples fail). Defaults to `true`.
|
38
42
|
attr_accessor :fail_on_error
|
@@ -94,6 +98,9 @@ module RSpec::MultiprocessRunner
|
|
94
98
|
if worker_count
|
95
99
|
cmd_parts << '--worker-count' << worker_count.to_s
|
96
100
|
end
|
101
|
+
if example_timeout_seconds
|
102
|
+
cmd_parts << '--example-timeout' << example_timeout_seconds.to_s
|
103
|
+
end
|
97
104
|
if file_timeout_seconds
|
98
105
|
cmd_parts << '--file-timeout' << file_timeout_seconds.to_s
|
99
106
|
end
|
@@ -29,11 +29,12 @@ module RSpec::MultiprocessRunner
|
|
29
29
|
STATUS_EXAMPLE_COMPLETE = "example_complete"
|
30
30
|
STATUS_RUN_COMPLETE = "run_complete"
|
31
31
|
|
32
|
-
def initialize(environment_number,
|
32
|
+
def initialize(environment_number, options)
|
33
33
|
@environment_number = environment_number
|
34
34
|
@worker_socket, @coordinator_socket = Socket.pair(:UNIX, :STREAM)
|
35
|
-
@rspec_arguments =
|
36
|
-
@
|
35
|
+
@rspec_arguments = (options[:rspec_options] || []) + ["--format", ReportingFormatter.to_s]
|
36
|
+
@example_timeout_seconds = options[:example_timeout_seconds]
|
37
|
+
@file_timeout_seconds = options[:file_timeout_seconds]
|
37
38
|
@example_results = []
|
38
39
|
|
39
40
|
# Use a single configuration and world across all individual runs
|
@@ -65,6 +66,17 @@ module RSpec::MultiprocessRunner
|
|
65
66
|
@coordinator_socket.close
|
66
67
|
@pid = Process.pid
|
67
68
|
ENV["TEST_ENV_NUMBER"] = environment_number.to_s
|
69
|
+
|
70
|
+
# reset TERM handler so that
|
71
|
+
# - the coordinator's version (if any) is not executed twice
|
72
|
+
# - it actually terminates the process, instead of doing the ruby
|
73
|
+
# default (throw an exception, which gets caught by RSpec)
|
74
|
+
Kernel.trap("TERM", "SYSTEM_DEFAULT")
|
75
|
+
# rely on the coordinator to handle INT
|
76
|
+
Kernel.trap("INT", "IGNORE")
|
77
|
+
# prevent RSpec from trapping INT, also
|
78
|
+
::RSpec::Core::Runner.instance_eval { def self.trap_interrupt; end }
|
79
|
+
|
68
80
|
set_process_name
|
69
81
|
run_loop
|
70
82
|
end
|
@@ -84,18 +96,15 @@ module RSpec::MultiprocessRunner
|
|
84
96
|
|
85
97
|
public
|
86
98
|
|
87
|
-
def
|
99
|
+
def quit_when_idle_and_wait_for_quit
|
88
100
|
send_message_to_worker(command: COMMAND_QUIT)
|
89
|
-
end
|
90
|
-
|
91
|
-
def wait_until_quit
|
92
101
|
Process.wait(self.pid)
|
93
102
|
end
|
94
103
|
|
95
104
|
def run_file(filename)
|
96
105
|
send_message_to_worker(command: COMMAND_RUN_FILE, filename: filename)
|
97
106
|
@current_file = filename
|
98
|
-
@current_file_started_at = Time.now
|
107
|
+
@current_file_started_at = @current_example_started_at = Time.now
|
99
108
|
end
|
100
109
|
|
101
110
|
def working?
|
@@ -103,21 +112,28 @@ module RSpec::MultiprocessRunner
|
|
103
112
|
end
|
104
113
|
|
105
114
|
def stalled?
|
106
|
-
|
115
|
+
file_stalled =
|
116
|
+
if @file_timeout_seconds
|
117
|
+
working? && (Time.now - @current_file_started_at > @file_timeout_seconds)
|
118
|
+
end
|
119
|
+
example_stalled =
|
120
|
+
if @example_timeout_seconds
|
121
|
+
working? && (Time.now - @current_example_started_at > @example_timeout_seconds)
|
122
|
+
end
|
123
|
+
file_stalled || example_stalled
|
124
|
+
end
|
125
|
+
|
126
|
+
def shutdown_now
|
127
|
+
terminate_then_kill(5)
|
128
|
+
end
|
129
|
+
|
130
|
+
def kill_now
|
131
|
+
Process.kill(:KILL, pid)
|
132
|
+
Process.detach(pid)
|
107
133
|
end
|
108
134
|
|
109
135
|
def reap
|
110
|
-
|
111
|
-
Timeout.timeout(4) do
|
112
|
-
$stderr.puts "Reaping troubled process #{environment_number} (#{pid}; #{@current_file}) with QUIT"
|
113
|
-
Process.kill(:QUIT, pid)
|
114
|
-
Process.wait(pid)
|
115
|
-
end
|
116
|
-
rescue Timeout::Error
|
117
|
-
$stderr.puts "Reaping troubled process #{environment_number} (#{pid}) with KILL"
|
118
|
-
Process.kill(:KILL, pid)
|
119
|
-
Process.wait(pid)
|
120
|
-
end
|
136
|
+
terminate_then_kill(3, "Reaping troubled process #{environment_number} (#{pid}; #{@current_file})")
|
121
137
|
end
|
122
138
|
|
123
139
|
def receive_and_act_on_message_from_worker
|
@@ -126,6 +142,19 @@ module RSpec::MultiprocessRunner
|
|
126
142
|
|
127
143
|
private
|
128
144
|
|
145
|
+
def terminate_then_kill(timeout, message=nil)
|
146
|
+
begin
|
147
|
+
Timeout.timeout(timeout) do
|
148
|
+
$stderr.puts "#{message} with TERM" if message
|
149
|
+
Process.kill(:TERM, pid)
|
150
|
+
Process.wait(pid)
|
151
|
+
end
|
152
|
+
rescue Timeout::Error
|
153
|
+
$stderr.puts "#{message} with KILL" if message
|
154
|
+
kill_now
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
129
158
|
def receive_message_from_worker
|
130
159
|
receive_message(@coordinator_socket)
|
131
160
|
end
|
@@ -136,6 +165,7 @@ module RSpec::MultiprocessRunner
|
|
136
165
|
when STATUS_RUN_COMPLETE
|
137
166
|
@current_file = nil
|
138
167
|
@current_file_started_at = nil
|
168
|
+
@current_example_started_at = nil
|
139
169
|
when STATUS_EXAMPLE_COMPLETE
|
140
170
|
example_results << ExampleResult.new(message_hash)
|
141
171
|
suffix =
|
@@ -149,6 +179,7 @@ module RSpec::MultiprocessRunner
|
|
149
179
|
suffix += "\n#{message_hash["details"]}"
|
150
180
|
end
|
151
181
|
$stdout.puts "#{environment_number} (#{pid}): #{message_hash["description"]}#{suffix}"
|
182
|
+
@current_example_started_at = Time.now
|
152
183
|
else
|
153
184
|
$stderr.puts "Received unsupported status #{message_hash["status"].inspect} in worker #{pid}"
|
154
185
|
end
|
@@ -194,16 +225,25 @@ module RSpec::MultiprocessRunner
|
|
194
225
|
end
|
195
226
|
end
|
196
227
|
|
228
|
+
def handle_closed_coordinator_socket
|
229
|
+
# when the coordinator socket is closed, there's nothing more to do
|
230
|
+
exit
|
231
|
+
end
|
232
|
+
|
197
233
|
def receive_message_from_coordinator(socket)
|
198
234
|
receive_message(socket)
|
199
235
|
end
|
200
236
|
|
201
237
|
def send_message_to_coordinator(message_hash)
|
202
|
-
|
238
|
+
begin
|
239
|
+
send_message(@worker_socket, message_hash)
|
240
|
+
rescue Errno::EPIPE
|
241
|
+
handle_closed_coordinator_socket
|
242
|
+
end
|
203
243
|
end
|
204
244
|
|
205
245
|
def act_on_message_from_coordinator(message_hash)
|
206
|
-
return unless message_hash #
|
246
|
+
return handle_closed_coordinator_socket unless message_hash # EOF
|
207
247
|
case message_hash["command"]
|
208
248
|
when "quit"
|
209
249
|
exit
|
metadata
CHANGED
@@ -1,84 +1,75 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-multiprocess_runner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Rhett Sutphin
|
9
8
|
autorequire:
|
10
9
|
bindir: exe
|
11
10
|
cert_chain: []
|
12
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-24 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec-core
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '2.0'
|
22
|
-
- - <
|
20
|
+
- - "<"
|
23
21
|
- !ruby/object:Gem::Version
|
24
22
|
version: 2.99.0
|
25
23
|
type: :runtime
|
26
24
|
prerelease: false
|
27
25
|
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
26
|
requirements:
|
30
|
-
- - ~>
|
27
|
+
- - "~>"
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '2.0'
|
33
|
-
- - <
|
30
|
+
- - "<"
|
34
31
|
- !ruby/object:Gem::Version
|
35
32
|
version: 2.99.0
|
36
33
|
- !ruby/object:Gem::Dependency
|
37
34
|
name: bundler
|
38
35
|
requirement: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
36
|
requirements:
|
41
|
-
- - ~>
|
37
|
+
- - "~>"
|
42
38
|
- !ruby/object:Gem::Version
|
43
39
|
version: '1.10'
|
44
40
|
type: :development
|
45
41
|
prerelease: false
|
46
42
|
version_requirements: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
43
|
requirements:
|
49
|
-
- - ~>
|
44
|
+
- - "~>"
|
50
45
|
- !ruby/object:Gem::Version
|
51
46
|
version: '1.10'
|
52
47
|
- !ruby/object:Gem::Dependency
|
53
48
|
name: rake
|
54
49
|
requirement: !ruby/object:Gem::Requirement
|
55
|
-
none: false
|
56
50
|
requirements:
|
57
|
-
- - ~>
|
51
|
+
- - "~>"
|
58
52
|
- !ruby/object:Gem::Version
|
59
53
|
version: '10.0'
|
60
54
|
type: :development
|
61
55
|
prerelease: false
|
62
56
|
version_requirements: !ruby/object:Gem::Requirement
|
63
|
-
none: false
|
64
57
|
requirements:
|
65
|
-
- - ~>
|
58
|
+
- - "~>"
|
66
59
|
- !ruby/object:Gem::Version
|
67
60
|
version: '10.0'
|
68
61
|
- !ruby/object:Gem::Dependency
|
69
62
|
name: rspec
|
70
63
|
requirement: !ruby/object:Gem::Requirement
|
71
|
-
none: false
|
72
64
|
requirements:
|
73
|
-
- -
|
65
|
+
- - ">="
|
74
66
|
- !ruby/object:Gem::Version
|
75
67
|
version: '0'
|
76
68
|
type: :development
|
77
69
|
prerelease: false
|
78
70
|
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
none: false
|
80
71
|
requirements:
|
81
|
-
- -
|
72
|
+
- - ">="
|
82
73
|
- !ruby/object:Gem::Version
|
83
74
|
version: '0'
|
84
75
|
description:
|
@@ -89,9 +80,9 @@ executables:
|
|
89
80
|
extensions: []
|
90
81
|
extra_rdoc_files: []
|
91
82
|
files:
|
92
|
-
- .gitignore
|
93
|
-
- .rspec
|
94
|
-
- .travis.yml
|
83
|
+
- ".gitignore"
|
84
|
+
- ".rspec"
|
85
|
+
- ".travis.yml"
|
95
86
|
- CHANGELOG.md
|
96
87
|
- Gemfile
|
97
88
|
- LICENSE.txt
|
@@ -112,32 +103,25 @@ files:
|
|
112
103
|
homepage: https://github.com/cdd/rspec-multiprocess_runner
|
113
104
|
licenses:
|
114
105
|
- MIT
|
106
|
+
metadata: {}
|
115
107
|
post_install_message:
|
116
108
|
rdoc_options: []
|
117
109
|
require_paths:
|
118
110
|
- lib
|
119
111
|
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
-
none: false
|
121
112
|
requirements:
|
122
|
-
- -
|
113
|
+
- - ">="
|
123
114
|
- !ruby/object:Gem::Version
|
124
115
|
version: '0'
|
125
|
-
segments:
|
126
|
-
- 0
|
127
|
-
hash: -1609184745420465239
|
128
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
117
|
requirements:
|
131
|
-
- -
|
118
|
+
- - ">="
|
132
119
|
- !ruby/object:Gem::Version
|
133
120
|
version: '0'
|
134
|
-
segments:
|
135
|
-
- 0
|
136
|
-
hash: -1609184745420465239
|
137
121
|
requirements: []
|
138
122
|
rubyforge_project:
|
139
|
-
rubygems_version:
|
123
|
+
rubygems_version: 2.4.5.1
|
140
124
|
signing_key:
|
141
|
-
specification_version:
|
125
|
+
specification_version: 4
|
142
126
|
summary: A runner for RSpec 2 that uses multiple processes to execute specs in parallel
|
143
127
|
test_files: []
|