specjour 0.2.5 → 0.3.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.
- 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
|