1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
class ParallelRunner
def initialize(files, processes, formatter, argv)
@files = files
@processes = processes
@formatter = formatter
@argv = argv
@last_files = {}
@output_files = []
@success = true
end
def launch_children
@children = @processes.times.map { |i|
name = tmp "mspec-multi-#{i}"
@output_files << name
env = {
"SPEC_TEMP_DIR" => "#{SPEC_TEMP_DIR}_#{i}",
"MSPEC_MULTI" => i.to_s
}
command = @argv + ["-fy", "-o", name]
$stderr.puts "$ #{command.join(' ')}" if $MSPEC_DEBUG
IO.popen([env, *command, close_others: false], "rb+")
}
end
def handle(child, message)
case message
when '.'
@formatter.unload
send_new_file_or_quit(child)
else
if message == nil
msg = "A child mspec-run process died unexpectedly"
else
msg = "A child mspec-run process printed unexpected output on STDOUT"
while chunk = (child.read_nonblock(4096) rescue nil)
message += chunk
end
message.chomp!('.')
msg += ": #{message.inspect}"
end
if last_file = @last_files[child]
msg += " while running #{last_file}"
end
@success = false
quit(child)
abort "\n#{msg}"
end
end
def quit(child)
begin
child.puts "QUIT"
rescue Errno::EPIPE
# The child process already died
end
_pid, status = Process.wait2(child.pid)
@success &&= status.success?
child.close
@children.delete(child)
end
def send_new_file_or_quit(child)
if @files.empty?
quit(child)
else
file = @files.shift
@last_files[child] = file
child.puts file
end
end
def run
MSpec.register_files @files
launch_children
puts @children.map { |child| child.gets }.uniq
@formatter.start
begin
@children.each { |child| send_new_file_or_quit(child) }
until @children.empty?
IO.select(@children)[0].each { |child|
handle(child, child.read(1))
}
end
ensure
@children.dup.each { |child| quit(child) }
@formatter.aggregate_results(@output_files)
@formatter.finish
end
@success
end
end
|