jcukeforker 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.mdown +45 -0
- data/Rakefile +7 -0
- data/bin/cukeforker +9 -0
- data/jcukeforker.gemspec +29 -0
- data/lib/jcukeforker/abstract_listener.rb +57 -0
- data/lib/jcukeforker/configurable_vnc_server.rb +16 -0
- data/lib/jcukeforker/formatters/junit_scenario_formatter.rb +40 -0
- data/lib/jcukeforker/formatters/scenario_line_logger.rb +28 -0
- data/lib/jcukeforker/logging_listener.rb +59 -0
- data/lib/jcukeforker/recording_vnc_listener.rb +60 -0
- data/lib/jcukeforker/runner.rb +155 -0
- data/lib/jcukeforker/scenarios.rb +43 -0
- data/lib/jcukeforker/status_server.rb +37 -0
- data/lib/jcukeforker/task_manager.rb +45 -0
- data/lib/jcukeforker/version.rb +3 -0
- data/lib/jcukeforker/worker.rb +122 -0
- data/lib/jcukeforker/worker_script.rb +6 -0
- data/lib/jcukeforker.rb +29 -0
- data/spec/jcukeforker/formatters/scenario_line_logger_spec.rb +44 -0
- data/spec/jcukeforker/logging_listener_spec.rb +43 -0
- data/spec/jcukeforker/recording_vnc_listener_spec.rb +81 -0
- data/spec/jcukeforker/runner_spec.rb +94 -0
- data/spec/jcukeforker/scenarios_spec.rb +67 -0
- data/spec/jcukeforker/status_server_spec.rb +36 -0
- data/spec/jcukeforker/task_manager_spec.rb +48 -0
- data/spec/jcukeforker/worker_spec.rb +27 -0
- data/spec/spec_helper.rb +6 -0
- metadata +183 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 32563d887a984beb002a2661539769d5908beeb3
|
4
|
+
data.tar.gz: 602298483da878759729ab39644dfae2058012d9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f956f1cc6927646155a24209b2b796a058818a39ca038df79e7f4de673ea15803840a8bba9f638d6dd658c2a6c8c0f34b5a76bcd474321e50404561249d2345e
|
7
|
+
data.tar.gz: 4245453e0c2a25cc5cfe430932cf499fbd4fbeb2ff6fa312c2724b380543b6981ab8eeb7f8cbd86c6c5771fb01a044a1b816ffd73efdbfc79d19c941fbc0d0f1
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color -fs
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2011-2014 Jari Bakken
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
|
data/README.mdown
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# jcukeforker
|
2
|
+
|
3
|
+
Forking cukes and VNC displays.
|
4
|
+
|
5
|
+
Jcukeforker is a fork of cukeforker desgined for jruby.
|
6
|
+
|
7
|
+
### NB!
|
8
|
+
|
9
|
+
If you're using cukeforker with selenium-webdriver and Firefox, all versions prior to 2.40 has a bug where custom
|
10
|
+
Firefox profiles created in a forked process would not get cleaned up. Please make sure you're using selenium-webdriver >= 2.40
|
11
|
+
to avoid this.
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# parallelize per feature
|
18
|
+
JCukeForker::Runner.run Dir['features/**/*.feature'],
|
19
|
+
:max => 4 # number of workers
|
20
|
+
:out => "/path/to/reports", # output path
|
21
|
+
:format => :html # passed to `cucumber --format`,
|
22
|
+
:extra_args => %w[--extra arguments] # passed to cucumber,
|
23
|
+
:vnc => true # manage a pool of VNC displays, assign one per worker.
|
24
|
+
|
25
|
+
# parallelize per scenario, with one JUnit XML file per scenario.
|
26
|
+
JCukeForker::Runner.run JCukeForker::Scenarios.tagged(%W[@edition ~@wip])
|
27
|
+
:extra_args => %W[-f CukeForker::Formatters::JunitScenarioFormatter --out results/junit]
|
28
|
+
```
|
29
|
+
|
30
|
+
Note on Patches/Pull Requests
|
31
|
+
=============================
|
32
|
+
|
33
|
+
* Fork the project.
|
34
|
+
* Make your feature addition or bug fix.
|
35
|
+
* Add tests for it. This is important so I don't break it in a
|
36
|
+
future version unintentionally.
|
37
|
+
* Commit, do not mess with rakefile, version, or history.
|
38
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
39
|
+
* Send me a pull request. Bonus points for topic branches.
|
40
|
+
|
41
|
+
Copyright
|
42
|
+
=========
|
43
|
+
|
44
|
+
Copyright (c) 2011-2014 Jari Bakken. See LICENSE for details.
|
45
|
+
|
data/Rakefile
ADDED
data/bin/cukeforker
ADDED
data/jcukeforker.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "jcukeforker/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "jcukeforker"
|
7
|
+
s.version = JCukeForker::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jason Gowan", "Jari Bakken"]
|
10
|
+
s.email = ["gowanjason@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Library to maintain a forking queue of Cucumber processes}
|
13
|
+
s.description = %q{Library to maintain a forking queue of Cucumber processes, with optional VNC displays.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "jcukeforker"
|
16
|
+
|
17
|
+
s.add_dependency "cucumber", ">= 1.1.5"
|
18
|
+
s.add_dependency "vnctools", ">= 0.1.1"
|
19
|
+
s.add_dependency "celluloid-io", ">= 0.15.0"
|
20
|
+
s.add_dependency "childprocess", ">= 0.5.3"
|
21
|
+
s.add_development_dependency "rspec", "~> 2.5"
|
22
|
+
s.add_development_dependency "coveralls"
|
23
|
+
s.add_development_dependency "rake", "~> 0.9.2"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module JCukeForker
|
2
|
+
class AbstractListener
|
3
|
+
|
4
|
+
def on_run_starting
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_worker_waiting(worker_path)
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_worker_dead(worker_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_task_starting(worker_path, feature)
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_task_finished(worker_path, feature, status)
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_worker_forked(worker)
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_worker_register(worker_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_worker_waiting(worker_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_worker_dead(worker_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_run_interrupted
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_run_finished(failed)
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_display_fetched(server)
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_display_released(server)
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_display_starting(worker_path, display)
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_display_stopping(worker_path, display)
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_eta(time, remaining, finished)
|
50
|
+
end
|
51
|
+
|
52
|
+
def update(meth, *args)
|
53
|
+
__send__(meth, *args)
|
54
|
+
end
|
55
|
+
|
56
|
+
end # AbstractListener
|
57
|
+
end # CukeForker
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'cucumber/formatter/junit'
|
2
|
+
require 'cucumber/formatter/ordered_xml_markup'
|
3
|
+
module JCukeForker
|
4
|
+
module Formatters
|
5
|
+
class JunitScenarioFormatter < Cucumber::Formatter::Junit
|
6
|
+
def feature_result_filename(feature_file)
|
7
|
+
File.join(@reportdir, "TEST-#{basename(feature_file)}.xml")
|
8
|
+
end
|
9
|
+
|
10
|
+
def after_feature(feature)
|
11
|
+
# do nothing
|
12
|
+
end
|
13
|
+
|
14
|
+
def feature_element_line_number(feature_element)
|
15
|
+
if feature_element.respond_to? :line
|
16
|
+
feature_element.line
|
17
|
+
else
|
18
|
+
feature_element.instance_variable_get(:@line)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def after_feature_element(feature_element)
|
23
|
+
@testsuite = Cucumber::Formatter::OrderedXmlMarkup.new( :indent => 2 )
|
24
|
+
@testsuite.instruct!
|
25
|
+
@testsuite.testsuite(
|
26
|
+
:failures => @failures,
|
27
|
+
:errors => @errors,
|
28
|
+
:skipped => @skipped,
|
29
|
+
:tests => @tests,
|
30
|
+
:time => "%.6f" % @time,
|
31
|
+
:name => @feature_name ) do
|
32
|
+
@testsuite << @builder.target!
|
33
|
+
end
|
34
|
+
|
35
|
+
line_number = feature_element_line_number(feature_element)
|
36
|
+
write_file(feature_result_filename(feature_element.feature.file+"-#{line_number}"), @testsuite.target!)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'gherkin/tag_expression'
|
2
|
+
module JCukeForker
|
3
|
+
module Formatters
|
4
|
+
class ScenarioLineLogger
|
5
|
+
attr_reader :scenarios
|
6
|
+
|
7
|
+
def initialize(tag_expression = Gherkin::TagExpression.new([]))
|
8
|
+
@scenarios = []
|
9
|
+
@tag_expression = tag_expression
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_feature_element(feature_element)
|
13
|
+
if @tag_expression.evaluate(feature_element.source_tags)
|
14
|
+
line_number = if feature_element.respond_to?(:line)
|
15
|
+
feature_element.line
|
16
|
+
else
|
17
|
+
feature_element.location.line
|
18
|
+
end
|
19
|
+
|
20
|
+
@scenarios << [feature_element.feature.file, line_number].join(':')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(*args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module JCukeForker
|
4
|
+
class LoggingListener < AbstractListener
|
5
|
+
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
6
|
+
|
7
|
+
def initialize(io = STDOUT)
|
8
|
+
@io = io
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_run_starting
|
12
|
+
log.info "[ run ] starting"
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_worker_register(worker_path)
|
16
|
+
log.info "[ worker #{worker_id(worker_path).ljust 3} ] register: #{worker_path}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_worker_dead(worker_path)
|
20
|
+
log.info "[ worker #{worker_id(worker_path).ljust 3} ] dead : #{worker_path}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_task_starting(worker_path, feature)
|
24
|
+
log.info "[ worker #{worker_id(worker_path).ljust 3} ] starting: #{feature}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_task_finished(worker_path, feature, status)
|
28
|
+
log.info "[ worker #{worker_id(worker_path).ljust 3} ] #{status_string(status).ljust(8)}: #{feature}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_run_finished(failed)
|
32
|
+
log.info "[ run ] finished, #{status_string failed}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_run_interrupted
|
36
|
+
puts "\n"
|
37
|
+
log.info "[ run ] interrupted - please wait"
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def status_string(failed)
|
43
|
+
failed == 'false' ? 'failed' : 'passed'
|
44
|
+
end
|
45
|
+
|
46
|
+
def worker_id(worker_path)
|
47
|
+
/\-(\d+)$/.match(worker_path).captures[0]
|
48
|
+
end
|
49
|
+
|
50
|
+
def log
|
51
|
+
@log ||= (
|
52
|
+
log = Logger.new @io
|
53
|
+
log.datetime_format = TIME_FORMAT
|
54
|
+
|
55
|
+
log
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end # LoggingListener
|
59
|
+
end # CukeForker
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
module JCukeForker
|
3
|
+
class RecordingVncListener < AbstractListener
|
4
|
+
|
5
|
+
attr_reader :output
|
6
|
+
|
7
|
+
def initialize(worker, opts = {})
|
8
|
+
@ext = opts[:codec] || "webm"
|
9
|
+
@options = opts
|
10
|
+
@worker = worker
|
11
|
+
|
12
|
+
@recorder = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_task_starting(worker_path, feature)
|
16
|
+
|
17
|
+
@recorder = recorder_for(feature)
|
18
|
+
@recorder.start
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_task_finished(worker, feature, status)
|
22
|
+
if @recorder.crashed?
|
23
|
+
raise 'ffmpeg failed'
|
24
|
+
end
|
25
|
+
|
26
|
+
unless worker.failed?
|
27
|
+
FileUtils.rm_rf output
|
28
|
+
end
|
29
|
+
|
30
|
+
@recorder.stop
|
31
|
+
|
32
|
+
@recorder = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_worker_dead(worker_path)
|
36
|
+
@recorder && @recorder.stop
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def recorder_for(feature)
|
42
|
+
@output = File.join(@worker.out, "#{feature.gsub(/\W/, '_')}.#{@ext}")
|
43
|
+
|
44
|
+
process = ChildProcess.build(
|
45
|
+
'ffmpeg',
|
46
|
+
'-an',
|
47
|
+
'-y',
|
48
|
+
'-f', 'x11grab',
|
49
|
+
'-r', @options[:frame_rate] || '5',
|
50
|
+
'-s', @options[:frame_size] || '1024x768',
|
51
|
+
'-i', ENV['DISPLAY'],
|
52
|
+
'-vcodec', @options[:codec] || 'vp8',
|
53
|
+
@output
|
54
|
+
)
|
55
|
+
process.io.stdout = process.io.stderr = File.open('/dev/null', 'w')
|
56
|
+
process
|
57
|
+
end
|
58
|
+
|
59
|
+
end # RecordingVncListener
|
60
|
+
end # CukeForker
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module JCukeForker
|
2
|
+
|
3
|
+
#
|
4
|
+
# Runner.run(features, opts)
|
5
|
+
#
|
6
|
+
# where 'features' is an Array of file:line
|
7
|
+
# and 'opts' is a Hash of options:
|
8
|
+
#
|
9
|
+
# :max => Fixnum number of workers (default: 2, pass 0 for unlimited)
|
10
|
+
# :vnc => true/false,Class,Array children are launched with DISPLAY set from a VNC server pool,
|
11
|
+
# where the size of the pool is equal to :max. If passed a Class instance,
|
12
|
+
# this will be passed as the second argument to VncTools::ServerPool.
|
13
|
+
# :record => true/false,Hash whether to record a video of failed tests (requires ffmpeg)
|
14
|
+
# this will be ignored if if :vnc is not true. If passed a Hash,
|
15
|
+
# this will be passed as options to RecordingVncListener
|
16
|
+
# :notify => object (or array of objects) implementing the AbstractListener API
|
17
|
+
# :out => path directory to dump output to (default: current working dir)
|
18
|
+
# :log => true/false wether or not to log to stdout (default: true)
|
19
|
+
# :format => Symbol format passed to `cucumber --format` (default: html)
|
20
|
+
# :extra_args => Array extra arguments passed to cucumber
|
21
|
+
# :delay => Numeric seconds to sleep between each worker is started (default: 0)
|
22
|
+
#
|
23
|
+
|
24
|
+
class Runner
|
25
|
+
include Observable
|
26
|
+
|
27
|
+
DEFAULT_OPTIONS = {
|
28
|
+
:max => 2,
|
29
|
+
:vnc => false,
|
30
|
+
:record => false,
|
31
|
+
:notify => nil,
|
32
|
+
:out => Dir.pwd,
|
33
|
+
:log => true,
|
34
|
+
:format => :html,
|
35
|
+
:delay => 0
|
36
|
+
}
|
37
|
+
|
38
|
+
def self.run(features, opts = {})
|
39
|
+
create(features, opts).run
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.create(features, opts = {})
|
43
|
+
opts = DEFAULT_OPTIONS.dup.merge(opts)
|
44
|
+
|
45
|
+
max = opts[:max]
|
46
|
+
format = opts[:format]
|
47
|
+
out = File.join opts[:out]
|
48
|
+
listeners = Array(opts[:notify])
|
49
|
+
extra_args = Array(opts[:extra_args])
|
50
|
+
delay = opts[:delay]
|
51
|
+
|
52
|
+
if opts[:log]
|
53
|
+
listeners << LoggingListener.new
|
54
|
+
end
|
55
|
+
|
56
|
+
task_manager = TaskManager.new
|
57
|
+
features.each do |feature|
|
58
|
+
task_manager.add({feature: feature, format: format,out: out,extra_args: extra_args})
|
59
|
+
end
|
60
|
+
|
61
|
+
listeners << task_manager
|
62
|
+
status_server = StatusServer.new '6333'
|
63
|
+
worker_dir = "/tmp/jcukeforker-#{SecureRandom.hex 4}"
|
64
|
+
FileUtils.mkdir_p worker_dir
|
65
|
+
|
66
|
+
vnc_pool = nil
|
67
|
+
if vnc = opts[:vnc]
|
68
|
+
if vnc.kind_of?(Array)
|
69
|
+
vnc_pool = VncTools::ServerPool.new(max, ConfigurableVncServer.create_class(vnc))
|
70
|
+
elsif vnc.kind_of?(Class)
|
71
|
+
vnc_pool = VncTools::ServerPool.new(max, vnc)
|
72
|
+
else
|
73
|
+
vnc_pool = VncTools::ServerPool.new(max)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
processes = create_processes(max, '6333', worker_dir, vnc_pool, opts[:record])
|
78
|
+
|
79
|
+
runner = Runner.new status_server, processes, worker_dir, vnc_pool, delay
|
80
|
+
|
81
|
+
listeners.each { |l|
|
82
|
+
status_server.add_observer l
|
83
|
+
runner.add_observer l
|
84
|
+
}
|
85
|
+
|
86
|
+
runner
|
87
|
+
end
|
88
|
+
|
89
|
+
def initialize(status_server, processes, worker_dir, vnc_pool, delay)
|
90
|
+
@status_server = status_server
|
91
|
+
@processes = processes
|
92
|
+
@worker_dir = worker_dir
|
93
|
+
@vnc_pool = vnc_pool
|
94
|
+
@delay = delay
|
95
|
+
end
|
96
|
+
|
97
|
+
def run
|
98
|
+
start
|
99
|
+
process
|
100
|
+
stop
|
101
|
+
rescue Interrupt
|
102
|
+
fire :on_run_interrupted
|
103
|
+
stop
|
104
|
+
rescue StandardError
|
105
|
+
fire :on_run_interrupted
|
106
|
+
stop
|
107
|
+
raise
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def self.create_processes(max, status_path, worker_dir, vnc_pool = nil, record = false)
|
113
|
+
worker_file = "#{File.expand_path File.dirname(__FILE__)}/worker_script.rb"
|
114
|
+
|
115
|
+
(1..max).inject([]) do |l, i|
|
116
|
+
process_args = %W[ruby #{worker_file} #{status_path} #{worker_dir}/worker-#{i}]
|
117
|
+
if vnc_pool && record
|
118
|
+
record = {} unless record.kind_of? Hash
|
119
|
+
process_args << record.to_json
|
120
|
+
end
|
121
|
+
process = ChildProcess.build(*process_args)
|
122
|
+
process.environment['DISPLAY'] = vnc_pool.get.display if vnc_pool
|
123
|
+
l << process
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def start
|
128
|
+
@status_server.async.run
|
129
|
+
fire :on_run_starting
|
130
|
+
|
131
|
+
@processes.each do |process|
|
132
|
+
process.start
|
133
|
+
sleep @delay
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def process
|
138
|
+
@processes.each &:wait
|
139
|
+
end
|
140
|
+
|
141
|
+
def stop
|
142
|
+
@status_server.shutdown
|
143
|
+
ensure # catch potential second Interrupt
|
144
|
+
@vnc_pool.stop if @vnc_pool
|
145
|
+
FileUtils.rm_r @worker_dir
|
146
|
+
#fire :on_run_finished, @queue.has_failures?
|
147
|
+
end
|
148
|
+
|
149
|
+
def fire(*args)
|
150
|
+
changed
|
151
|
+
notify_observers(*args)
|
152
|
+
end
|
153
|
+
|
154
|
+
end # Runner
|
155
|
+
end # CukeForker
|