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.
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