specjour 0.7.0 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/History.markdown +12 -0
- data/README.markdown +24 -1
- data/Rakefile +12 -12
- data/bin/specjour +3 -1
- data/lib/specjour/cli.rb +86 -110
- data/lib/specjour/colors.rb +23 -0
- data/lib/specjour/configuration.rb +47 -91
- data/lib/specjour/connection.rb +69 -20
- data/lib/specjour/cpu.rb +4 -0
- data/lib/specjour/fork.rb +1 -1
- data/lib/specjour/formatter.rb +153 -0
- data/lib/specjour/listener.rb +181 -0
- data/lib/specjour/loader.rb +55 -119
- data/lib/specjour/logger.rb +34 -0
- data/lib/specjour/plugin/base.rb +61 -0
- data/lib/specjour/plugin/manager.rb +28 -0
- data/lib/specjour/plugin/rails.rb +47 -0
- data/lib/specjour/plugin/rails_v3.rb +23 -0
- data/lib/specjour/plugin/rails_v4.rb +25 -0
- data/lib/specjour/plugin/rspec.rb +160 -0
- data/lib/specjour/plugin/rspec_v2.rb +53 -0
- data/lib/specjour/plugin/rspec_v3.rb +59 -0
- data/lib/specjour/plugin/ssh.rb +24 -0
- data/lib/specjour/plugin.rb +4 -0
- data/lib/specjour/printer.rb +235 -67
- data/lib/specjour/protocol.rb +13 -6
- data/lib/specjour/rspec_formatter.rb +17 -0
- data/lib/specjour/rsync_daemon.rb +6 -3
- data/lib/specjour/socket_helper.rb +26 -10
- data/lib/specjour/worker.rb +36 -62
- data/lib/specjour.rb +50 -24
- data/lib/specjour_plugin.rb +5 -0
- metadata +52 -84
- data/lib/specjour/cucumber/distributed_formatter.rb +0 -82
- data/lib/specjour/cucumber/final_report.rb +0 -83
- data/lib/specjour/cucumber/preloader.rb +0 -22
- data/lib/specjour/cucumber/runner.rb +0 -15
- data/lib/specjour/cucumber.rb +0 -16
- data/lib/specjour/db_scrub.rb +0 -56
- data/lib/specjour/dispatcher.rb +0 -170
- data/lib/specjour/manager.rb +0 -174
- data/lib/specjour/rspec/distributed_formatter.rb +0 -50
- data/lib/specjour/rspec/final_report.rb +0 -73
- data/lib/specjour/rspec/marshalable_exception.rb +0 -19
- data/lib/specjour/rspec/preloader.rb +0 -15
- data/lib/specjour/rspec/runner.rb +0 -14
- data/lib/specjour/rspec/shared_example_group_ext.rb +0 -9
- data/lib/specjour/rspec.rb +0 -17
data/lib/specjour/printer.rb
CHANGED
@@ -1,82 +1,243 @@
|
|
1
1
|
module Specjour
|
2
|
-
|
3
2
|
class Printer
|
4
|
-
|
5
|
-
|
3
|
+
require 'dnssd'
|
4
|
+
|
5
|
+
include Logger
|
6
|
+
include SocketHelper
|
6
7
|
|
7
|
-
attr_reader :port, :clients
|
8
|
-
attr_accessor :tests_to_run, :example_size, :examples_complete, :profiler
|
8
|
+
attr_reader :host, :port, :clients
|
9
|
+
attr_accessor :tests_to_run, :test_paths, :example_size, :examples_complete, :profiler, :machines
|
9
10
|
|
10
|
-
|
11
|
+
Thread.abort_on_exception = true
|
12
|
+
|
13
|
+
def initialize(options={})
|
14
|
+
@options = options
|
11
15
|
@host = "0.0.0.0"
|
12
|
-
@server_socket = TCPServer.new(@host, RANDOM_PORT)
|
13
|
-
@port = @server_socket.addr[1]
|
14
16
|
@profiler = {}
|
15
17
|
@clients = {}
|
16
18
|
@tests_to_run = []
|
19
|
+
@test_paths = options[:test_paths]
|
17
20
|
@example_size = 0
|
21
|
+
@machines = []
|
22
|
+
@send_threads = []
|
23
|
+
@bonjour_service = nil
|
24
|
+
@mutex = Mutex.new
|
25
|
+
@running = false
|
26
|
+
@output = options[:output] || $stdout
|
27
|
+
@loader_clients = []
|
18
28
|
self.examples_complete = 0
|
29
|
+
set_paths
|
30
|
+
Specjour.configuration.printer_port ||= SocketHelper.new_uri.port
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_paths
|
34
|
+
paths = test_paths.map {|tp| Pathname.new(tp).expand_path}
|
35
|
+
if paths.any?
|
36
|
+
paths.each do |p|
|
37
|
+
pn = Pathname.new(p)
|
38
|
+
abort("Request test path: #{p} does not exist") unless pn.exist?
|
39
|
+
end
|
40
|
+
@project_path = Pathname.new(find_project_base_dir(paths.first.to_s))
|
41
|
+
else
|
42
|
+
@project_path = Pathname.new(Dir.pwd)
|
43
|
+
end
|
44
|
+
if paths.size == 1 and paths.first == project_path
|
45
|
+
@test_paths = []
|
46
|
+
else
|
47
|
+
@test_paths = paths
|
48
|
+
end
|
49
|
+
@test_paths = @test_paths.map do |path|
|
50
|
+
test_path = path.relative_path_from(project_path)
|
51
|
+
abort("Test path #{test_path} doesn't exist") unless test_path.exist?
|
52
|
+
test_path.to_s
|
53
|
+
end
|
54
|
+
if !project_path.exist?
|
55
|
+
abort("Project path #{project_path} doesn't exist")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def announce
|
60
|
+
@output.puts("Looking for listeners...")
|
61
|
+
text = DNSSD::TextRecord.new
|
62
|
+
text['version'] = Specjour::VERSION
|
63
|
+
text['project_alias'] = Specjour.configuration.project_aliases.first
|
64
|
+
@bonjour_service = DNSSD.register "#{hostname}".tr(".","-"), "_specjour._tcp", domain=nil, Specjour.configuration.printer_port, text
|
65
|
+
end
|
66
|
+
|
67
|
+
def start_rsync
|
68
|
+
rsync_daemon.start
|
69
|
+
end
|
70
|
+
|
71
|
+
def rsync_daemon
|
72
|
+
@rsync_daemon ||= RsyncDaemon.new(project_path.to_s, project_name, Specjour.configuration.rsync_port)
|
73
|
+
end
|
74
|
+
|
75
|
+
def running?
|
76
|
+
@mutex.synchronize do
|
77
|
+
@running
|
78
|
+
end
|
19
79
|
end
|
20
80
|
|
21
81
|
def start
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
82
|
+
@running = true
|
83
|
+
@server_socket ||= TCPServer.new(@host, Specjour.configuration.printer_port)
|
84
|
+
debug "server socket #{@host}:#{Specjour.configuration.printer_port}"
|
85
|
+
done_reader, @done_writer = IO.pipe
|
86
|
+
while running? do
|
87
|
+
debug "loop going to select #{running?}"
|
88
|
+
result = select([@server_socket, done_reader], [], [])
|
89
|
+
reads = result.first
|
90
|
+
reads.each do |socket_being_read|
|
91
|
+
if socket_being_read == @server_socket
|
92
|
+
debug "adding connection"
|
93
|
+
client_socket = @server_socket.accept
|
94
|
+
client_socket = Connection.wrap(client_socket)
|
95
|
+
@send_threads << Thread.new(client_socket) { |sock| serve(sock) }
|
96
|
+
elsif socket_being_read == @done_writer
|
97
|
+
debug "breaking on done socket"
|
98
|
+
break
|
39
99
|
end
|
40
100
|
end
|
41
101
|
end
|
102
|
+
done_reader.close
|
103
|
+
@done_writer.close
|
104
|
+
rescue => error
|
105
|
+
$stderr.puts "Server got an error #{error.inspect} #{error.backtrace.join("\n")}"
|
42
106
|
ensure
|
107
|
+
@loader_clients.each do |client|
|
108
|
+
if Specjour.interrupted?
|
109
|
+
client.send_server_done("INT")
|
110
|
+
else
|
111
|
+
client.send_server_done("TERM")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
@server_socket.close
|
43
115
|
stopping
|
44
|
-
|
116
|
+
exit exit_status
|
45
117
|
end
|
46
118
|
|
47
119
|
def exit_status
|
48
|
-
|
120
|
+
if Specjour.interrupted?
|
121
|
+
2
|
122
|
+
else
|
123
|
+
statuses = Specjour.plugin_manager.send_task(:exit_status, Specjour.configuration.formatter)
|
124
|
+
plugin_status = statuses.detect {|s| !s.nil?}
|
125
|
+
plugin_status.nil? ? Specjour.configuration.formatter.exit_status : plugin_status
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def uri
|
130
|
+
@uri ||= URI::Generic.build host: host, port: port
|
131
|
+
end
|
132
|
+
|
133
|
+
def project_name
|
134
|
+
options[:project_alias] || project_path.basename.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
def project_path
|
138
|
+
@project_path
|
49
139
|
end
|
50
140
|
|
51
141
|
protected
|
52
142
|
|
53
143
|
def serve(client)
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
144
|
+
debug "serving #{client.inspect}"
|
145
|
+
loop do
|
146
|
+
if client.eof?
|
147
|
+
debug "client eof"
|
148
|
+
client.disconnect
|
149
|
+
disconnecting
|
150
|
+
break
|
151
|
+
end
|
152
|
+
debug "socket about to read data #{client.inspect}"
|
153
|
+
data = client.recv_data
|
154
|
+
command = data[:command]
|
155
|
+
case command
|
156
|
+
when "add_to_profiler"
|
157
|
+
add_to_profiler(*data[:args])
|
158
|
+
when "done"
|
159
|
+
done(*data[:args])
|
160
|
+
when "next_test"
|
161
|
+
client.send_data next_test(*data[:args])
|
162
|
+
when "ready"
|
163
|
+
@mutex.synchronize do
|
164
|
+
@loader_clients |= [client]
|
165
|
+
end
|
166
|
+
client.send_data ready(*data[:args])
|
167
|
+
when "register_tests"
|
168
|
+
register_tests(*data[:args])
|
169
|
+
when "report_test"
|
170
|
+
report_test(*data[:args])
|
171
|
+
when "error"
|
172
|
+
unexpected_error(*data[:args])
|
173
|
+
else
|
174
|
+
raise Error.new("COMMAND NOT FOUND: #{command}")
|
175
|
+
end
|
176
|
+
IO.select([client.socket])
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def done
|
181
|
+
@mutex.synchronize do
|
182
|
+
self.examples_complete += 1
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def next_test
|
187
|
+
@mutex.synchronize do
|
188
|
+
log "test size: #{tests_to_run.size}"
|
189
|
+
if tests_to_run.size == example_size
|
190
|
+
Specjour.configuration.formatter.start_time = Specjour::Time.now
|
191
|
+
end
|
192
|
+
tests_to_run.shift
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def ready(info)
|
197
|
+
@output.puts "Found #{info[:hostname]}(#{info[:worker_size]})"
|
198
|
+
{
|
199
|
+
project_name: project_name,
|
200
|
+
project_path: project_path.to_s,
|
201
|
+
test_paths: test_paths
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
def register_tests(tests)
|
206
|
+
@mutex.synchronize do
|
207
|
+
if example_size == 0
|
208
|
+
self.tests_to_run = run_order(tests)
|
209
|
+
self.example_size = tests_to_run.size
|
210
|
+
end
|
61
211
|
end
|
62
212
|
end
|
63
213
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
214
|
+
def report_test(test)
|
215
|
+
@mutex.synchronize do
|
216
|
+
Specjour.configuration.formatter.report_test(test)
|
217
|
+
end
|
67
218
|
end
|
68
219
|
|
69
|
-
def
|
70
|
-
|
220
|
+
def unexpected_error(message)
|
221
|
+
@output.puts message
|
71
222
|
end
|
72
223
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
224
|
+
def find_project_base_dir(directory)
|
225
|
+
dirs = Dir["#{directory}/Rakefile"]
|
226
|
+
if dirs.any?
|
227
|
+
File.dirname dirs.first
|
228
|
+
else
|
229
|
+
find_project_base_dir(File.dirname(directory))
|
77
230
|
end
|
78
231
|
end
|
79
232
|
|
233
|
+
def options
|
234
|
+
{}
|
235
|
+
end
|
236
|
+
|
237
|
+
def machines=(client, machines)
|
238
|
+
@machines = machines
|
239
|
+
end
|
240
|
+
|
80
241
|
def rspec_summary=(client, summary)
|
81
242
|
rspec_report.add(summary)
|
82
243
|
end
|
@@ -85,23 +246,30 @@ module Specjour
|
|
85
246
|
cucumber_report.add(summary)
|
86
247
|
end
|
87
248
|
|
88
|
-
def add_to_profiler(
|
89
|
-
|
90
|
-
|
249
|
+
def add_to_profiler(test, time, host)
|
250
|
+
@mutex.synchronize do
|
251
|
+
self.profiler[test] = [time, host]
|
252
|
+
end
|
91
253
|
end
|
92
254
|
|
93
255
|
def disconnecting
|
94
|
-
|
95
|
-
|
256
|
+
@mutex.synchronize do
|
257
|
+
debug "DISCONNECT #{@running} #{example_size} #{examples_complete}"
|
258
|
+
if @running && examples_complete == example_size
|
259
|
+
@running = false
|
260
|
+
debug "writing done"
|
261
|
+
@done_writer.write("DONE")
|
262
|
+
debug "done with writer"
|
263
|
+
end
|
96
264
|
end
|
97
265
|
end
|
98
266
|
|
99
267
|
def run_order(tests)
|
100
268
|
if File.exist?('.specjour/performance')
|
101
|
-
ordered_tests = File.readlines('.specjour/performance').map {|l| l.chop.split(':'
|
102
|
-
|
269
|
+
ordered_tests = File.readlines('.specjour/performance').map {|l| l.chop.split(':')[1]}
|
270
|
+
ordered_tests & tests | tests
|
103
271
|
else
|
104
|
-
tests
|
272
|
+
tests.sort
|
105
273
|
end
|
106
274
|
end
|
107
275
|
|
@@ -113,40 +281,40 @@ module Specjour
|
|
113
281
|
@cucumber_report ||= Cucumber::FinalReport.new
|
114
282
|
end
|
115
283
|
|
284
|
+
# "test.rb" => [1.12, "host.local[2]"]
|
116
285
|
def record_performance
|
117
286
|
File.open('.specjour/performance', 'w') do |file|
|
118
|
-
ordered_specs = profiler.to_a.sort_by {|
|
119
|
-
file.puts "%6f:%s" % [
|
287
|
+
ordered_specs = profiler.to_a.sort_by {|test, data| -data[0].to_f}.map do |test, data|
|
288
|
+
file.puts "%6f:%s:%s" % [data[0], test, data[1]]
|
120
289
|
end
|
121
290
|
end
|
122
291
|
end
|
123
292
|
|
124
|
-
def reporters
|
125
|
-
[@rspec_report, @cucumber_report].compact
|
126
|
-
end
|
127
|
-
|
128
293
|
def stopping
|
129
|
-
|
294
|
+
Specjour.configuration.formatter.set_end_time!
|
295
|
+
@bonjour_service.stop
|
296
|
+
|
297
|
+
Specjour.plugin_manager.send_task(:before_print_summary, Specjour.configuration.formatter)
|
298
|
+
Specjour.configuration.formatter.print_summary
|
299
|
+
|
130
300
|
unless Specjour.interrupted?
|
131
301
|
record_performance
|
132
302
|
print_missing_tests if missing_tests?
|
133
303
|
end
|
134
|
-
end
|
135
304
|
|
136
|
-
|
137
|
-
reporters.each {|r| r.summarize}
|
305
|
+
Specjour.plugin_manager.send_task(:after_print_summary, Specjour.configuration.formatter)
|
138
306
|
end
|
139
307
|
|
140
308
|
def missing_tests?
|
141
|
-
|
309
|
+
examples_complete != example_size && tests_to_run.any?
|
142
310
|
end
|
143
311
|
|
144
312
|
def print_missing_tests
|
145
|
-
puts "*" * 60
|
146
|
-
puts "Oops! The following tests were not run:"
|
147
|
-
puts "*" * 60
|
148
|
-
puts tests_to_run
|
149
|
-
puts "*" * 60
|
313
|
+
@output.puts "*" * 60
|
314
|
+
@output.puts "Oops! The following tests were not run:"
|
315
|
+
@output.puts "*" * 60
|
316
|
+
@output.puts tests_to_run
|
317
|
+
@output.puts "*" * 60
|
150
318
|
end
|
151
319
|
|
152
320
|
end
|
data/lib/specjour/protocol.rb
CHANGED
@@ -1,14 +1,21 @@
|
|
1
1
|
module Specjour
|
2
2
|
module Protocol
|
3
|
-
|
4
|
-
TERMINATOR_REGEXP = /#{TERMINATOR}$/
|
3
|
+
require 'json'
|
5
4
|
|
6
|
-
def
|
7
|
-
|
5
|
+
def recv_data
|
6
|
+
bytes = socket.gets.to_i
|
7
|
+
string = socket.read(bytes)
|
8
|
+
debug "recv_string #{bytes} #{string.inspect} #{socket.inspect}"
|
9
|
+
if !string.empty?
|
10
|
+
Marshal.load(string)
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
|
-
def
|
11
|
-
Marshal.
|
14
|
+
def send_data(data)
|
15
|
+
mdata = Marshal.dump(data)
|
16
|
+
debug "send_data: #{mdata.bytesize} #{mdata.inspect} #{socket.inspect}"
|
17
|
+
socket.puts mdata.bytesize
|
18
|
+
socket.write mdata
|
12
19
|
end
|
13
20
|
end
|
14
21
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Specjour
|
2
|
+
require 'rspec/core/formatters/json_formatter'
|
3
|
+
|
4
|
+
class RspecFormatter < ::RSpec::Core::Formatters::JsonFormatter
|
5
|
+
def hostname
|
6
|
+
@hostname ||= Socket.gethostname
|
7
|
+
end
|
8
|
+
|
9
|
+
def close(_notification=nil)
|
10
|
+
@output_hash[:examples].each do |e|
|
11
|
+
e[:hostname] = hostname
|
12
|
+
e[:worker_number] = ENV["TEST_ENV_NUMBER"]
|
13
|
+
@output.report_test(e)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -2,6 +2,7 @@ module Specjour
|
|
2
2
|
class RsyncDaemon
|
3
3
|
require 'fileutils'
|
4
4
|
include SocketHelper
|
5
|
+
include Logger
|
5
6
|
|
6
7
|
# Corresponds to the version of specjour that changed the configuration
|
7
8
|
# file.
|
@@ -40,13 +41,15 @@ module Specjour
|
|
40
41
|
write_config
|
41
42
|
Dir.chdir(project_path) do
|
42
43
|
Kernel.system *command
|
44
|
+
rsync_pid = $?.pid
|
43
45
|
sleep 0.1
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
49
|
def stop
|
48
|
-
if pid
|
49
|
-
|
50
|
+
if process_id = pid
|
51
|
+
log("#{self.class.name} Shutting down")
|
52
|
+
Process.kill("KILL", process_id) rescue nil
|
50
53
|
FileUtils.rm(pid_file)
|
51
54
|
end
|
52
55
|
end
|
@@ -94,7 +97,7 @@ remove it, and re-run the dispatcher to generate the new config file.
|
|
94
97
|
# $ #{(command | ['--no-detach']).join(' ')}
|
95
98
|
#
|
96
99
|
# Rsync with the following command:
|
97
|
-
# $ rsync -a --port=#{port} #{hostname}::#{project_name}
|
100
|
+
# $ rsync -a --port=#{port} #{hostname}::#{project_name} #{Specjour.configuration.tmp_path}/#{project_name}
|
98
101
|
#
|
99
102
|
use chroot = no
|
100
103
|
timeout = 20
|
@@ -2,6 +2,28 @@ module Specjour
|
|
2
2
|
module SocketHelper
|
3
3
|
Socket.do_not_reverse_lookup = true
|
4
4
|
|
5
|
+
def connection
|
6
|
+
return @connection if connection?
|
7
|
+
@connection = Connection.new Specjour.configuration.printer_uri
|
8
|
+
@connection.connect
|
9
|
+
@connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def connection?
|
13
|
+
!@connection.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_connection
|
17
|
+
connection.disconnect if connection?
|
18
|
+
@connection = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
module_function
|
22
|
+
|
23
|
+
def new_uri
|
24
|
+
URI::Generic.build :host => faux_server[2], :port => faux_server[1]
|
25
|
+
end
|
26
|
+
|
5
27
|
def ip_from_hostname(hostname)
|
6
28
|
Socket.getaddrinfo(hostname, nil, Socket::AF_INET, Socket::SOCK_STREAM).first.fetch(3)
|
7
29
|
rescue SocketError
|
@@ -9,23 +31,17 @@ module Specjour
|
|
9
31
|
end
|
10
32
|
|
11
33
|
def hostname
|
12
|
-
|
34
|
+
Socket.gethostname
|
13
35
|
end
|
14
36
|
|
15
37
|
def local_ip
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
def current_uri
|
20
|
-
@current_uri ||= new_uri
|
38
|
+
UDPSocket.open {|s| s.connect('74.125.224.103', 1); s.addr.last }
|
21
39
|
end
|
22
40
|
|
23
|
-
def
|
24
|
-
|
41
|
+
def remote_ip?(ip)
|
42
|
+
Socket.ip_address_list.detect {|a| a.ip_address == ip}.nil?
|
25
43
|
end
|
26
44
|
|
27
|
-
protected
|
28
|
-
|
29
45
|
def faux_server
|
30
46
|
server = TCPServer.new('0.0.0.0', nil)
|
31
47
|
server.addr
|
data/lib/specjour/worker.rb
CHANGED
@@ -1,99 +1,73 @@
|
|
1
1
|
module Specjour
|
2
2
|
|
3
3
|
class Worker
|
4
|
+
include Logger
|
4
5
|
include Protocol
|
5
6
|
include SocketHelper
|
6
|
-
|
7
|
-
attr_reader :number
|
7
|
+
attr_reader :number, :options
|
8
8
|
|
9
9
|
def initialize(options = {})
|
10
|
+
Specjour.trap_interrupt_with_exit
|
10
11
|
ARGV.replace []
|
11
|
-
|
12
|
+
@options = options
|
12
13
|
@number = options[:number].to_i
|
13
|
-
|
14
|
-
|
15
|
-
Specjour.load_custom_hooks
|
16
|
-
end
|
17
|
-
|
18
|
-
def printer_uri=(val)
|
19
|
-
@printer_uri = URI.parse(val)
|
14
|
+
Specjour.configuration.worker_number = number
|
15
|
+
ENV['TEST_ENV_NUMBER'] = Specjour.configuration.worker_number.to_s
|
20
16
|
end
|
21
17
|
|
22
18
|
def prepare
|
23
|
-
|
19
|
+
Specjour.configuration.prepare.call
|
24
20
|
end
|
25
21
|
|
26
22
|
def run_tests
|
27
|
-
|
28
|
-
|
23
|
+
log "Worker running tests"
|
24
|
+
|
25
|
+
Specjour.plugin_manager.send_task(:before_suite)
|
29
26
|
|
30
27
|
while test = connection.next_test
|
31
28
|
print_status(test)
|
32
|
-
time = Benchmark.realtime
|
29
|
+
time = Benchmark.realtime do
|
30
|
+
Specjour.plugin_manager.send_task(:run_test, test)
|
31
|
+
end
|
33
32
|
profile(test, time)
|
34
|
-
|
35
|
-
connection.send_message(:done)
|
33
|
+
connection.done
|
36
34
|
end
|
37
35
|
|
38
|
-
|
36
|
+
Specjour.plugin_manager.send_task(:after_suite)
|
37
|
+
|
38
|
+
rescue StandardError, ScriptError => e
|
39
|
+
$stderr.puts "RESCUED #{e.message}"
|
40
|
+
$stderr.puts e.backtrace
|
41
|
+
connection.error(e)
|
39
42
|
ensure
|
40
|
-
|
43
|
+
remove_connection
|
44
|
+
log "Worker disconnecting #{Process.pid}"
|
45
|
+
r = IO.popen("ps -eo pid,ppid,command | grep #{Process.pid}")
|
46
|
+
r.each_line do |line|
|
47
|
+
pid, ppid = line.split(" ")
|
48
|
+
pid = pid.to_i
|
49
|
+
ppid = ppid.to_i
|
50
|
+
if ppid == Process.pid && pid != r.pid
|
51
|
+
Process.kill("KILL", pid)
|
52
|
+
end
|
53
|
+
end
|
41
54
|
end
|
42
55
|
|
43
56
|
protected
|
44
57
|
|
45
|
-
def connection
|
46
|
-
@connection ||= printer_connection
|
47
|
-
end
|
48
|
-
|
49
|
-
def printer_connection
|
50
|
-
Connection.new printer_uri
|
51
|
-
end
|
52
|
-
|
53
58
|
def print_status(test)
|
54
|
-
status = "
|
55
|
-
|
56
|
-
$PROGRAM_NAME = "specjour#{status}"
|
59
|
+
status = "Running #{test}"
|
60
|
+
log status
|
61
|
+
$PROGRAM_NAME = "specjour #{status}"
|
57
62
|
end
|
58
63
|
|
59
64
|
def print_time_for(test, time)
|
60
|
-
|
65
|
+
log sprintf("Finished #{test} in %.2fs\n", time)
|
61
66
|
end
|
62
67
|
|
63
68
|
def profile(test, time)
|
64
|
-
connection.
|
69
|
+
connection.add_to_profiler(test, time, "#{hostname}[#{ENV["TEST_ENV_NUMBER"]}]")
|
65
70
|
print_time_for(test, time)
|
66
71
|
end
|
67
|
-
|
68
|
-
def run_test(test)
|
69
|
-
if test_type(test) == :cucumber
|
70
|
-
run_feature test
|
71
|
-
else
|
72
|
-
run_spec test
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def run_feature(feature)
|
77
|
-
Cucumber::Runner.run(feature)
|
78
|
-
end
|
79
|
-
|
80
|
-
def run_spec(spec)
|
81
|
-
RSpec::Runner.run(spec, connection)
|
82
|
-
end
|
83
|
-
|
84
|
-
def send_run_times(run_times)
|
85
|
-
[:rspec, :cucumber].each do |type|
|
86
|
-
connection.send_message(:"#{type}_summary=", {:duration => sprintf("%6f", run_times[type])}) if run_times[type] > 0
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def test_type(test)
|
91
|
-
test =~ /\.feature(:\d+)?$/ ? :cucumber : :rspec
|
92
|
-
end
|
93
|
-
|
94
|
-
def set_env_variables
|
95
|
-
ENV['RSPEC_COLOR'] ||= 'true'
|
96
|
-
ENV['TEST_ENV_NUMBER'] ||= number.to_s
|
97
|
-
end
|
98
72
|
end
|
99
73
|
end
|