specjour 0.7.0 → 2.0.0.rc1
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/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
|