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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/History.markdown +12 -0
  3. data/README.markdown +24 -1
  4. data/Rakefile +12 -12
  5. data/bin/specjour +3 -1
  6. data/lib/specjour/cli.rb +86 -110
  7. data/lib/specjour/colors.rb +23 -0
  8. data/lib/specjour/configuration.rb +47 -91
  9. data/lib/specjour/connection.rb +69 -20
  10. data/lib/specjour/cpu.rb +4 -0
  11. data/lib/specjour/fork.rb +1 -1
  12. data/lib/specjour/formatter.rb +153 -0
  13. data/lib/specjour/listener.rb +181 -0
  14. data/lib/specjour/loader.rb +55 -119
  15. data/lib/specjour/logger.rb +34 -0
  16. data/lib/specjour/plugin/base.rb +61 -0
  17. data/lib/specjour/plugin/manager.rb +28 -0
  18. data/lib/specjour/plugin/rails.rb +47 -0
  19. data/lib/specjour/plugin/rails_v3.rb +23 -0
  20. data/lib/specjour/plugin/rails_v4.rb +25 -0
  21. data/lib/specjour/plugin/rspec.rb +160 -0
  22. data/lib/specjour/plugin/rspec_v2.rb +53 -0
  23. data/lib/specjour/plugin/rspec_v3.rb +59 -0
  24. data/lib/specjour/plugin/ssh.rb +24 -0
  25. data/lib/specjour/plugin.rb +4 -0
  26. data/lib/specjour/printer.rb +235 -67
  27. data/lib/specjour/protocol.rb +13 -6
  28. data/lib/specjour/rspec_formatter.rb +17 -0
  29. data/lib/specjour/rsync_daemon.rb +6 -3
  30. data/lib/specjour/socket_helper.rb +26 -10
  31. data/lib/specjour/worker.rb +36 -62
  32. data/lib/specjour.rb +50 -24
  33. data/lib/specjour_plugin.rb +5 -0
  34. metadata +52 -84
  35. data/lib/specjour/cucumber/distributed_formatter.rb +0 -82
  36. data/lib/specjour/cucumber/final_report.rb +0 -83
  37. data/lib/specjour/cucumber/preloader.rb +0 -22
  38. data/lib/specjour/cucumber/runner.rb +0 -15
  39. data/lib/specjour/cucumber.rb +0 -16
  40. data/lib/specjour/db_scrub.rb +0 -56
  41. data/lib/specjour/dispatcher.rb +0 -170
  42. data/lib/specjour/manager.rb +0 -174
  43. data/lib/specjour/rspec/distributed_formatter.rb +0 -50
  44. data/lib/specjour/rspec/final_report.rb +0 -73
  45. data/lib/specjour/rspec/marshalable_exception.rb +0 -19
  46. data/lib/specjour/rspec/preloader.rb +0 -15
  47. data/lib/specjour/rspec/runner.rb +0 -14
  48. data/lib/specjour/rspec/shared_example_group_ext.rb +0 -9
  49. data/lib/specjour/rspec.rb +0 -17
@@ -1,82 +1,243 @@
1
1
  module Specjour
2
-
3
2
  class Printer
4
- include Protocol
5
- RANDOM_PORT = 0
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
- def initialize
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
- fds = [@server_socket]
23
- catch(:stop) do
24
- while true
25
- reads = select(fds).first
26
- reads.each do |socket_being_read|
27
- if socket_being_read == @server_socket
28
- client_socket = @server_socket.accept
29
- fds << client_socket
30
- clients[client_socket] = Connection.wrap(client_socket)
31
- elsif socket_being_read.eof?
32
- socket_being_read.close
33
- fds.delete(socket_being_read)
34
- clients.delete(socket_being_read)
35
- disconnecting
36
- else
37
- serve(clients[socket_being_read])
38
- end
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
- fds.each {|c| c.close}
116
+ exit exit_status
45
117
  end
46
118
 
47
119
  def exit_status
48
- reporters.all? {|r| r.exit_status == true}
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
- data = load_object(client.gets(TERMINATOR))
55
- case data
56
- when String
57
- $stdout.print data
58
- $stdout.flush
59
- when Array
60
- send data.first, *(data[1..-1].unshift(client))
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 ready(client)
65
- client.print tests_to_run.shift
66
- client.flush
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 done(client)
70
- self.examples_complete += 1
220
+ def unexpected_error(message)
221
+ @output.puts message
71
222
  end
72
223
 
73
- def tests=(client, tests)
74
- if tests_to_run.empty?
75
- self.tests_to_run = run_order(tests)
76
- self.example_size = tests_to_run.size
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(client, args)
89
- test, time = *args
90
- self.profiler[test] = time
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
- if clients.empty?
95
- throw(:stop)
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(':', 2)[1]}
102
- (tests - ordered_tests) | (ordered_tests & tests)
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 {|a| -a[1].to_f}.map do |test, time|
119
- file.puts "%6f:%s" % [time, test]
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
- summarize_reports
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
- def summarize_reports
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
- tests_to_run.any? || examples_complete != example_size
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
@@ -1,14 +1,21 @@
1
1
  module Specjour
2
2
  module Protocol
3
- TERMINATOR = "|ruojceps|"
4
- TERMINATOR_REGEXP = /#{TERMINATOR}$/
3
+ require 'json'
5
4
 
6
- def dump_object(data)
7
- Marshal.dump(data) << TERMINATOR
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 load_object(data)
11
- Marshal.load(data.sub(TERMINATOR_REGEXP, ''))
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
- Process.kill("KILL", pid)
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} /tmp/#{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
- @hostname ||= Socket.gethostname
34
+ Socket.gethostname
13
35
  end
14
36
 
15
37
  def local_ip
16
- @local_ip ||= UDPSocket.open {|s| s.connect('74.125.224.103', 1); s.addr.last }
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 new_uri
24
- URI::Generic.build :host => faux_server[2], :port => faux_server[1]
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
@@ -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
- attr_accessor :printer_uri
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
- $stdout = StringIO.new if options[:quiet]
12
+ @options = options
12
13
  @number = options[:number].to_i
13
- self.printer_uri = options[:printer_uri]
14
- set_env_variables
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
- Configuration.prepare.call
19
+ Specjour.configuration.prepare.call
24
20
  end
25
21
 
26
22
  def run_tests
27
- Configuration.after_fork.call
28
- run_times = Hash.new(0)
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 { run_test test }
29
+ time = Benchmark.realtime do
30
+ Specjour.plugin_manager.send_task(:run_test, test)
31
+ end
33
32
  profile(test, time)
34
- run_times[test_type(test)] += time
35
- connection.send_message(:done)
33
+ connection.done
36
34
  end
37
35
 
38
- send_run_times(run_times)
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
- connection.disconnect
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 = "[#{ENV['TEST_ENV_NUMBER']}] Running #{test}"
55
- $stdout.puts status
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
- printf "[#{ENV['TEST_ENV_NUMBER']}] Finished #{test} in %.2fs\n", time
65
+ log sprintf("Finished #{test} in %.2fs\n", time)
61
66
  end
62
67
 
63
68
  def profile(test, time)
64
- connection.send_message(:add_to_profiler, [test, time])
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