specjour 0.2.5 → 0.3.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.markdown +51 -20
- data/README.markdown +53 -26
- data/Rakefile +3 -5
- data/VERSION +1 -1
- data/bin/specjour +1 -48
- data/lib/specjour/cli.rb +97 -0
- data/lib/specjour/configuration.rb +73 -0
- data/lib/specjour/connection.rb +1 -1
- data/lib/specjour/cucumber/distributed_formatter.rb +2 -5
- data/lib/specjour/cucumber/preloader.rb +13 -0
- data/lib/specjour/cucumber.rb +3 -2
- data/lib/specjour/db_scrub.rb +8 -1
- data/lib/specjour/dispatcher.rb +99 -36
- data/lib/specjour/manager.rb +63 -37
- data/lib/specjour/printer.rb +33 -11
- data/lib/specjour/quiet_fork.rb +11 -0
- data/lib/specjour/rspec/distributed_formatter.rb +4 -15
- data/lib/specjour/rspec/preloader.rb +8 -0
- data/lib/specjour/rspec.rb +1 -0
- data/lib/specjour/rsync_daemon.rb +1 -1
- data/lib/specjour/socket_helper.rb +28 -0
- data/lib/specjour/worker.rb +42 -25
- data/lib/specjour.rb +13 -5
- data/spec/spec_helper.rb +12 -0
- data/spec/specjour/cli_spec.rb +104 -0
- data/spec/specjour/configuration_spec.rb +112 -0
- data/spec/{cpu_spec.rb → specjour/cpu_spec.rb} +0 -0
- data/spec/{manager_spec.rb → specjour/manager_spec.rb} +2 -2
- data/spec/specjour/printer_spec.rb +101 -0
- data/spec/{rsync_daemon_spec.rb → specjour/rsync_daemon_spec.rb} +2 -0
- data/spec/specjour_spec.rb +13 -2
- data/specjour.gemspec +33 -26
- metadata +69 -31
- data/lib/specjour/cucumber/dispatcher.rb +0 -18
- data/lib/specjour/cucumber/printer.rb +0 -9
- data/lib/specjour/socket_helpers.rb +0 -11
- data/lib/specjour/tasks/dispatch.rake +0 -21
- data/lib/specjour/tasks/specjour.rb +0 -1
- data/rails/init.rb +0 -6
- data/spec/lib/specjour/worker_spec.rb +0 -14
data/lib/specjour/dispatcher.rb
CHANGED
@@ -2,39 +2,65 @@ module Specjour
|
|
2
2
|
class Dispatcher
|
3
3
|
require 'dnssd'
|
4
4
|
Thread.abort_on_exception = true
|
5
|
-
include
|
5
|
+
include SocketHelper
|
6
6
|
|
7
7
|
class << self
|
8
8
|
attr_accessor :interrupted
|
9
9
|
alias interrupted? interrupted
|
10
10
|
end
|
11
11
|
|
12
|
-
attr_reader :
|
13
|
-
attr_accessor :worker_size
|
12
|
+
attr_reader :project_alias, :managers, :manager_threads, :hosts, :options, :all_tests
|
13
|
+
attr_accessor :worker_size, :project_path
|
14
14
|
|
15
|
-
def initialize(
|
16
|
-
@
|
17
|
-
@
|
15
|
+
def initialize(options = {})
|
16
|
+
@options = options
|
17
|
+
@project_path = File.expand_path options[:project_path]
|
18
18
|
@worker_size = 0
|
19
|
-
|
19
|
+
@managers = []
|
20
|
+
find_tests
|
21
|
+
clear_manager_threads
|
20
22
|
end
|
21
23
|
|
22
24
|
def start
|
23
|
-
|
25
|
+
abort("#{project_path} doesn't exist") unless File.exists?(project_path)
|
24
26
|
gather_managers
|
27
|
+
rsync_daemon.start
|
25
28
|
dispatch_work
|
26
|
-
|
29
|
+
Signal.trap('INT') { Dispatcher.interrupted = true; exit 1 }
|
30
|
+
printer.join if dispatching_tests?
|
31
|
+
wait_on_managers
|
27
32
|
exit printer.exit_status
|
28
33
|
end
|
29
34
|
|
30
35
|
protected
|
31
36
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
37
|
+
def find_tests
|
38
|
+
if project_path.match(/(.+)\/((spec|features)(?:\/\w+)*)$/)
|
39
|
+
self.project_path = $1
|
40
|
+
@all_tests = $3 == 'spec' ? all_specs($2) : all_features($2)
|
41
|
+
else
|
42
|
+
@all_tests = Array(all_specs) | Array(all_features)
|
35
43
|
end
|
36
44
|
end
|
37
45
|
|
46
|
+
def all_specs(tests_path = 'spec')
|
47
|
+
Dir.chdir(project_path) do
|
48
|
+
Dir[File.join(tests_path, "**/*_spec.rb")].sort
|
49
|
+
end if File.exists? File.join(project_path, tests_path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def all_features(tests_path = 'features')
|
53
|
+
Dir.chdir(project_path) do
|
54
|
+
Dir[File.join(tests_path, "**/*.feature")].sort
|
55
|
+
end if File.exists? File.join(project_path, tests_path)
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_manager(manager)
|
59
|
+
set_up_manager(manager)
|
60
|
+
managers << manager
|
61
|
+
self.worker_size += manager.worker_size
|
62
|
+
end
|
63
|
+
|
38
64
|
def command_managers(async = false, &block)
|
39
65
|
managers.each do |manager|
|
40
66
|
manager_threads << Thread.new(manager, &block)
|
@@ -42,53 +68,83 @@ module Specjour
|
|
42
68
|
wait_on_managers unless async
|
43
69
|
end
|
44
70
|
|
71
|
+
def dispatcher_uri
|
72
|
+
@dispatcher_uri ||= URI::Generic.build :scheme => "specjour", :host => hostname, :port => printer.port
|
73
|
+
end
|
74
|
+
|
45
75
|
def dispatch_work
|
76
|
+
puts "Managers found: #{managers.size}"
|
77
|
+
puts "Workers found: #{worker_size}"
|
78
|
+
printer.worker_size = worker_size
|
46
79
|
command_managers(true) { |m| m.dispatch }
|
47
80
|
end
|
48
81
|
|
82
|
+
def dispatching_tests?
|
83
|
+
worker_task == 'run_tests'
|
84
|
+
end
|
85
|
+
|
49
86
|
def fetch_manager(uri)
|
50
87
|
Timeout.timeout(8) do
|
51
88
|
manager = DRbObject.new_with_uri(uri.to_s)
|
52
|
-
if !managers.include?(manager) && manager.available_for?(
|
53
|
-
|
54
|
-
managers << manager
|
55
|
-
self.worker_size += manager.worker_size
|
89
|
+
if !managers.include?(manager) && manager.available_for?(project_alias)
|
90
|
+
add_manager(manager)
|
56
91
|
end
|
57
92
|
end
|
58
93
|
rescue Timeout::Error
|
59
94
|
Specjour.logger.debug "Couldn't work with manager at #{uri}"
|
95
|
+
rescue DRb::DRbConnError
|
96
|
+
retry
|
97
|
+
end
|
98
|
+
|
99
|
+
def fork_local_manager
|
100
|
+
puts "No remote managers found, starting a local manager..."
|
101
|
+
manager_options = {:worker_size => options[:worker_size], :registered_projects => [project_alias]}
|
102
|
+
manager = Manager.start_quietly manager_options
|
103
|
+
fetch_manager(manager.drb_uri)
|
104
|
+
at_exit { Process.kill('TERM', manager.pid) rescue Errno::ESRCH }
|
60
105
|
end
|
61
106
|
|
62
107
|
def gather_managers
|
63
|
-
puts "
|
64
|
-
|
108
|
+
puts "Looking for managers..."
|
109
|
+
gather_remote_managers
|
110
|
+
fork_local_manager if local_manager_needed?
|
111
|
+
abort "No managers found" if managers.size.zero?
|
112
|
+
end
|
113
|
+
|
114
|
+
def gather_remote_managers
|
65
115
|
browser = DNSSD::Service.new
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
resolve_reply(reply)
|
71
|
-
end
|
72
|
-
browser.stop unless reply.flags.more_coming?
|
116
|
+
Timeout.timeout(10) do
|
117
|
+
browser.browse '_druby._tcp' do |reply|
|
118
|
+
if reply.flags.add?
|
119
|
+
resolve_reply(reply)
|
73
120
|
end
|
121
|
+
browser.stop unless reply.flags.more_coming?
|
74
122
|
end
|
75
|
-
rescue Timeout::Error
|
76
123
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
124
|
+
rescue Timeout::Error
|
125
|
+
end
|
126
|
+
|
127
|
+
def local_manager_needed?
|
128
|
+
options[:worker_size] > 0 && no_local_managers?
|
129
|
+
end
|
130
|
+
|
131
|
+
def no_local_managers?
|
132
|
+
!managers.any? {|m| m.hostname == hostname}
|
81
133
|
end
|
82
134
|
|
83
135
|
def printer
|
84
|
-
@printer ||= Printer.start(
|
136
|
+
@printer ||= Printer.start(all_tests)
|
137
|
+
end
|
138
|
+
|
139
|
+
def project_alias
|
140
|
+
@project_alias ||= options[:project_alias] || project_name
|
85
141
|
end
|
86
142
|
|
87
143
|
def project_name
|
88
144
|
@project_name ||= File.basename(project_path)
|
89
145
|
end
|
90
146
|
|
91
|
-
def
|
147
|
+
def clear_manager_threads
|
92
148
|
@manager_threads = []
|
93
149
|
end
|
94
150
|
|
@@ -105,15 +161,22 @@ module Specjour
|
|
105
161
|
@rsync_daemon ||= RsyncDaemon.new(project_path, project_name)
|
106
162
|
end
|
107
163
|
|
108
|
-
def set_up_manager(manager
|
164
|
+
def set_up_manager(manager)
|
109
165
|
manager.project_name = project_name
|
110
|
-
manager.dispatcher_uri =
|
111
|
-
|
166
|
+
manager.dispatcher_uri = dispatcher_uri
|
167
|
+
manager.preload_spec = all_tests.detect {|f| f =~ /_spec\.rb$/}
|
168
|
+
manager.preload_feature = all_tests.detect {|f| f =~ /\.feature$/}
|
169
|
+
manager.worker_task = worker_task
|
170
|
+
at_exit { manager.kill_worker_processes rescue DRb::DRbConnError }
|
112
171
|
end
|
113
172
|
|
114
173
|
def wait_on_managers
|
115
174
|
manager_threads.each {|t| t.join; t.exit}
|
116
|
-
|
175
|
+
clear_manager_threads
|
176
|
+
end
|
177
|
+
|
178
|
+
def worker_task
|
179
|
+
options[:worker_task] || 'run_tests'
|
117
180
|
end
|
118
181
|
end
|
119
182
|
end
|
data/lib/specjour/manager.rb
CHANGED
@@ -1,29 +1,33 @@
|
|
1
1
|
module Specjour
|
2
2
|
class Manager
|
3
3
|
require 'dnssd'
|
4
|
+
require 'specjour/rspec'
|
5
|
+
require 'specjour/cucumber'
|
6
|
+
|
4
7
|
include DRbUndumped
|
5
|
-
include
|
8
|
+
include SocketHelper
|
9
|
+
|
10
|
+
attr_accessor :project_name, :preload_spec, :preload_feature, :worker_task, :pid
|
11
|
+
attr_reader :worker_size, :dispatcher_uri, :registered_projects, :bonjour_service, :worker_pids, :options
|
6
12
|
|
7
|
-
|
8
|
-
|
13
|
+
def self.start_quietly(options)
|
14
|
+
manager = new options.merge(:quiet => true)
|
15
|
+
manager.drb_uri
|
16
|
+
manager.pid = QuietFork.fork { manager.start }
|
17
|
+
manager
|
18
|
+
end
|
9
19
|
|
10
20
|
def initialize(options = {})
|
21
|
+
@options = options
|
11
22
|
@worker_size = options[:worker_size]
|
12
|
-
@
|
23
|
+
@worker_task = options[:worker_task]
|
13
24
|
@registered_projects = options[:registered_projects]
|
14
25
|
@worker_pids = []
|
26
|
+
at_exit { kill_worker_processes }
|
15
27
|
end
|
16
28
|
|
17
29
|
def available_for?(project_name)
|
18
|
-
registered_projects ? registered_projects.include?(project_name) :
|
19
|
-
end
|
20
|
-
|
21
|
-
def bundle_install
|
22
|
-
Dir.chdir(project_path) do
|
23
|
-
unless system('bundle check > /dev/null')
|
24
|
-
system("bundle install --relock > /dev/null")
|
25
|
-
end
|
26
|
-
end
|
30
|
+
registered_projects ? registered_projects.include?(project_name) : false
|
27
31
|
end
|
28
32
|
|
29
33
|
def dispatcher_uri=(uri)
|
@@ -31,50 +35,67 @@ module Specjour
|
|
31
35
|
@dispatcher_uri = uri
|
32
36
|
end
|
33
37
|
|
34
|
-
def kill_worker_processes
|
35
|
-
Process.kill('TERM', *worker_pids) rescue nil
|
36
|
-
end
|
37
|
-
|
38
|
-
def project_path
|
39
|
-
File.join("/tmp", project_name)
|
40
|
-
end
|
41
|
-
|
42
38
|
def dispatch
|
43
39
|
suspend_bonjour do
|
44
40
|
sync
|
45
|
-
|
41
|
+
in_project { Configuration.before_fork.call }
|
46
42
|
dispatch_workers
|
47
43
|
end
|
48
44
|
end
|
49
45
|
|
46
|
+
def drb_start
|
47
|
+
DRb.start_service drb_uri.to_s, self
|
48
|
+
at_exit { DRb.stop_service }
|
49
|
+
end
|
50
|
+
|
51
|
+
def drb_uri
|
52
|
+
@drb_uri ||= begin
|
53
|
+
current_uri.scheme = "druby"
|
54
|
+
current_uri
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
50
58
|
def dispatch_workers
|
51
|
-
|
59
|
+
worker_pids.clear
|
52
60
|
(1..worker_size).each do |index|
|
53
61
|
worker_pids << fork do
|
54
|
-
exec
|
55
|
-
Kernel.exit!
|
62
|
+
exec "specjour work #{worker_options(index)}"
|
56
63
|
end
|
57
64
|
end
|
58
|
-
at_exit { kill_worker_processes }
|
59
65
|
Process.waitall
|
60
66
|
end
|
61
67
|
|
68
|
+
def in_project(&block)
|
69
|
+
Dir.chdir(project_path, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def kill_worker_processes
|
73
|
+
Process.kill('TERM', *worker_pids) rescue Errno::ESRCH
|
74
|
+
end
|
75
|
+
|
76
|
+
def pid
|
77
|
+
@pid || Process.pid
|
78
|
+
end
|
79
|
+
|
80
|
+
def project_path
|
81
|
+
File.join("/tmp", project_name)
|
82
|
+
end
|
83
|
+
|
62
84
|
def start
|
63
85
|
drb_start
|
64
|
-
puts "Workers ready: #{worker_size}"
|
86
|
+
puts "Workers ready: #{worker_size}."
|
87
|
+
puts "Listening for #{registered_projects.join(', ')}"
|
65
88
|
bonjour_announce
|
66
|
-
Signal.trap('INT') { puts; puts "Shutting down manager..."; exit }
|
89
|
+
Signal.trap('INT') { puts; puts "Shutting down manager..."; exit 1 }
|
67
90
|
DRb.thread.join
|
68
91
|
end
|
69
92
|
|
70
|
-
def
|
71
|
-
|
72
|
-
puts "Manager started at #{drb_uri}"
|
73
|
-
at_exit { DRb.stop_service }
|
93
|
+
def quiet?
|
94
|
+
options.has_key? :quiet
|
74
95
|
end
|
75
96
|
|
76
97
|
def sync
|
77
|
-
cmd "rsync -
|
98
|
+
cmd "rsync -aL --delete --port=8989 #{dispatcher_uri.host}::#{project_name} #{project_path}"
|
78
99
|
end
|
79
100
|
|
80
101
|
protected
|
@@ -88,14 +109,19 @@ module Specjour
|
|
88
109
|
system command
|
89
110
|
end
|
90
111
|
|
91
|
-
def drb_uri
|
92
|
-
@drb_uri ||= URI.parse(DRb.uri)
|
93
|
-
end
|
94
|
-
|
95
112
|
def suspend_bonjour(&block)
|
96
113
|
bonjour_service.stop
|
97
114
|
block.call
|
98
115
|
bonjour_announce
|
99
116
|
end
|
117
|
+
|
118
|
+
def worker_options(index)
|
119
|
+
exec_options = "--project-path #{project_path} --printer-uri #{dispatcher_uri} --number #{index} --task #{worker_task}"
|
120
|
+
exec_options << " --preload-spec #{preload_spec}" if preload_spec
|
121
|
+
exec_options << " --preload-feature #{preload_feature}" if preload_feature
|
122
|
+
exec_options << " --log" if Specjour.log?
|
123
|
+
exec_options << " --quiet" if quiet?
|
124
|
+
exec_options
|
125
|
+
end
|
100
126
|
end
|
101
127
|
end
|
data/lib/specjour/printer.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Specjour
|
2
2
|
require 'specjour/rspec'
|
3
|
+
require 'specjour/cucumber'
|
3
4
|
|
4
5
|
class Printer < GServer
|
5
6
|
include Protocol
|
@@ -44,11 +45,15 @@ module Specjour
|
|
44
45
|
end
|
45
46
|
|
46
47
|
def exit_status
|
47
|
-
|
48
|
+
reporters.all? {|r| r.exit_status == true}
|
48
49
|
end
|
49
50
|
|
50
|
-
def
|
51
|
-
|
51
|
+
def rspec_summary=(client, summary)
|
52
|
+
rspec_report.add(summary)
|
53
|
+
end
|
54
|
+
|
55
|
+
def cucumber_summary=(client, summary)
|
56
|
+
cucumber_report.add(summary)
|
52
57
|
end
|
53
58
|
|
54
59
|
protected
|
@@ -66,7 +71,7 @@ module Specjour
|
|
66
71
|
end
|
67
72
|
|
68
73
|
def error(exception)
|
69
|
-
Specjour.logger.debug exception.inspect
|
74
|
+
Specjour.logger.debug "#{exception.inspect}\n#{exception.backtrace.join("\n")}"
|
70
75
|
end
|
71
76
|
|
72
77
|
def process(message, client)
|
@@ -78,22 +83,39 @@ module Specjour
|
|
78
83
|
end
|
79
84
|
end
|
80
85
|
|
81
|
-
def
|
82
|
-
@
|
86
|
+
def rspec_report
|
87
|
+
@rspec_report ||= Rspec::FinalReport.new
|
88
|
+
end
|
89
|
+
|
90
|
+
def cucumber_report
|
91
|
+
@cucumber_report ||= Cucumber::FinalReport.new
|
92
|
+
end
|
93
|
+
|
94
|
+
def reporters
|
95
|
+
[@rspec_report, @cucumber_report].compact
|
83
96
|
end
|
84
97
|
|
85
98
|
def stopping
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
99
|
+
summarize_reports
|
100
|
+
warn_if_workers_deserted
|
101
|
+
end
|
102
|
+
|
103
|
+
def summarize_reports
|
104
|
+
reporters.each {|r| r.summarize}
|
90
105
|
end
|
91
106
|
|
92
107
|
def synchronize(&block)
|
93
108
|
@connectionsMutex.synchronize &block
|
94
109
|
end
|
95
110
|
|
96
|
-
def
|
111
|
+
def warn_if_workers_deserted
|
112
|
+
if disconnections != completed_workers && !Dispatcher.interrupted?
|
113
|
+
puts
|
114
|
+
puts workers_deserted_message
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def workers_deserted_message
|
97
119
|
data = "* ERROR: NOT ALL WORKERS COMPLETED PROPERLY *"
|
98
120
|
filler = "*" * data.size
|
99
121
|
[filler, data, filler].join "\n"
|
@@ -2,11 +2,6 @@ module Specjour::Rspec
|
|
2
2
|
class DistributedFormatter < Spec::Runner::Formatter::BaseTextFormatter
|
3
3
|
require 'specjour/rspec/marshalable_rspec_failure'
|
4
4
|
|
5
|
-
class << self
|
6
|
-
attr_accessor :batch_size
|
7
|
-
end
|
8
|
-
@batch_size = 1
|
9
|
-
|
10
5
|
attr_reader :failing_messages, :passing_messages, :pending_messages, :output
|
11
6
|
attr_reader :duration, :example_count, :failure_count, :pending_count, :pending_examples, :failing_examples
|
12
7
|
|
@@ -22,18 +17,18 @@ module Specjour::Rspec
|
|
22
17
|
|
23
18
|
def example_failed(example, counter, failure)
|
24
19
|
failing_messages << colorize_failure('F', failure)
|
25
|
-
|
20
|
+
print_and_flush(failing_messages)
|
26
21
|
end
|
27
22
|
|
28
23
|
def example_passed(example)
|
29
24
|
passing_messages << green('.')
|
30
|
-
|
25
|
+
print_and_flush(passing_messages)
|
31
26
|
end
|
32
27
|
|
33
28
|
def example_pending(example, message, deprecated_pending_location=nil)
|
34
29
|
super
|
35
30
|
pending_messages << yellow('*')
|
36
|
-
|
31
|
+
print_and_flush(pending_messages)
|
37
32
|
end
|
38
33
|
|
39
34
|
def dump_summary(duration, example_count, failure_count, pending_count)
|
@@ -41,7 +36,7 @@ module Specjour::Rspec
|
|
41
36
|
@example_count = example_count
|
42
37
|
@failure_count = failure_count
|
43
38
|
@pending_count = pending_count
|
44
|
-
output.send_message(:
|
39
|
+
output.send_message(:rspec_summary=, to_hash)
|
45
40
|
end
|
46
41
|
|
47
42
|
def dump_pending
|
@@ -68,12 +63,6 @@ module Specjour::Rspec
|
|
68
63
|
|
69
64
|
protected
|
70
65
|
|
71
|
-
def batch_print(messages)
|
72
|
-
if messages.size == self.class.batch_size
|
73
|
-
print_and_flush(messages)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
66
|
def print_and_flush(messages)
|
78
67
|
output.print messages.to_s
|
79
68
|
output.flush
|
data/lib/specjour/rspec.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Specjour
|
2
|
+
module SocketHelper
|
3
|
+
def ip_from_hostname(hostname)
|
4
|
+
Socket.getaddrinfo(hostname, nil, Socket::AF_INET, Socket::SOCK_STREAM).first.fetch(3)
|
5
|
+
end
|
6
|
+
|
7
|
+
def hostname
|
8
|
+
@hostname ||= Socket.gethostname
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_uri
|
12
|
+
@current_uri ||= new_uri
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_uri
|
16
|
+
URI::Generic.build :host => faux_server[2], :port => faux_server[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def faux_server
|
22
|
+
server = TCPServer.new('0.0.0.0', nil)
|
23
|
+
server.addr
|
24
|
+
ensure
|
25
|
+
server.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/specjour/worker.rb
CHANGED
@@ -1,51 +1,78 @@
|
|
1
1
|
module Specjour
|
2
|
+
require 'specjour/rspec'
|
2
3
|
require 'specjour/cucumber'
|
3
4
|
|
4
5
|
class Worker
|
5
6
|
include Protocol
|
6
|
-
include
|
7
|
+
include SocketHelper
|
7
8
|
attr_accessor :printer_uri
|
8
|
-
attr_reader :project_path, :
|
9
|
-
|
10
|
-
def initialize(
|
11
|
-
|
12
|
-
|
13
|
-
@
|
14
|
-
@
|
15
|
-
|
16
|
-
|
9
|
+
attr_reader :project_path, :number, :preload_spec, :preload_feature, :task
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
ARGV.replace []
|
13
|
+
$stdout = StringIO.new if options[:quiet]
|
14
|
+
@project_path = options[:project_path]
|
15
|
+
@number = options[:number].to_i
|
16
|
+
@preload_spec = options[:preload_spec]
|
17
|
+
@preload_feature = options[:preload_feature]
|
18
|
+
@task = options[:task]
|
19
|
+
self.printer_uri = options[:printer_uri]
|
17
20
|
set_env_variables
|
21
|
+
Dir.chdir(project_path)
|
18
22
|
end
|
19
23
|
|
20
24
|
def printer_uri=(val)
|
21
25
|
@printer_uri = URI.parse(val)
|
22
26
|
end
|
23
27
|
|
24
|
-
def
|
28
|
+
def prepare
|
29
|
+
load_app
|
30
|
+
Configuration.prepare.call
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_tests
|
34
|
+
load_app
|
35
|
+
Configuration.after_fork.call
|
25
36
|
run_time = 0
|
26
|
-
|
37
|
+
|
27
38
|
while test = connection.next_test
|
28
|
-
|
39
|
+
time = Benchmark.realtime do
|
29
40
|
run_test test
|
30
41
|
end
|
42
|
+
run_time += time if test =~ /_spec\.rb$/
|
31
43
|
end
|
32
|
-
connection.send_message(:
|
44
|
+
connection.send_message(:rspec_summary=, {:duration => sprintf("%6f", run_time)})
|
33
45
|
connection.send_message(:done)
|
34
46
|
connection.disconnect
|
35
47
|
end
|
36
48
|
|
49
|
+
def start
|
50
|
+
send task
|
51
|
+
end
|
52
|
+
|
37
53
|
protected
|
38
54
|
|
39
55
|
def connection
|
40
56
|
@connection ||= printer_connection
|
41
57
|
end
|
42
58
|
|
59
|
+
def load_app
|
60
|
+
Rspec::Preloader.load(preload_spec) if preload_spec
|
61
|
+
Cucumber::Preloader.load(preload_feature) if preload_feature
|
62
|
+
end
|
63
|
+
|
43
64
|
def printer_connection
|
44
65
|
Connection.new printer_uri
|
45
66
|
end
|
46
67
|
|
68
|
+
def print_status(test)
|
69
|
+
status = "[#{ENV['TEST_ENV_NUMBER']}] Running #{test}"
|
70
|
+
puts status
|
71
|
+
$PROGRAM_NAME = "specjour#{status}"
|
72
|
+
end
|
73
|
+
|
47
74
|
def run_test(test)
|
48
|
-
|
75
|
+
print_status(test)
|
49
76
|
if test =~ /\.feature$/
|
50
77
|
run_feature test
|
51
78
|
else
|
@@ -54,7 +81,6 @@ module Specjour
|
|
54
81
|
end
|
55
82
|
|
56
83
|
def run_feature(feature)
|
57
|
-
set_up_cucumber
|
58
84
|
cli = ::Cucumber::Cli::Main.new(['--format', 'Specjour::Cucumber::DistributedFormatter', feature], connection)
|
59
85
|
cli.execute!(::Cucumber::Cli::Main.step_mother)
|
60
86
|
end
|
@@ -70,17 +96,8 @@ module Specjour
|
|
70
96
|
end
|
71
97
|
|
72
98
|
def set_env_variables
|
73
|
-
ENV['PREPARE_DB'] = 'true'
|
74
99
|
ENV['RSPEC_COLOR'] = 'true'
|
75
100
|
ENV['TEST_ENV_NUMBER'] = number.to_s
|
76
101
|
end
|
77
|
-
|
78
|
-
def set_up_cucumber
|
79
|
-
unless @cucumber_loaded
|
80
|
-
Cucumber::DistributedFormatter.batch_size = batch_size
|
81
|
-
::Cucumber::Cli::Options.class_eval { def print_profile_information; end }
|
82
|
-
@cucumber_loaded = true
|
83
|
-
end
|
84
|
-
end
|
85
102
|
end
|
86
103
|
end
|