cukeforker 0.2.1 → 0.2.2
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/README.mdown +11 -4
- data/cukeforker.gemspec +1 -1
- data/lib/cukeforker/runner.rb +11 -9
- data/lib/cukeforker/version.rb +1 -1
- data/lib/cukeforker/worker.rb +15 -6
- data/lib/cukeforker/worker_queue.rb +8 -1
- data/spec/cukeforker/runner_spec.rb +9 -7
- data/spec/cukeforker/worker_queue_spec.rb +47 -14
- data/spec/cukeforker/worker_spec.rb +30 -0
- data/spec/spec_helper.rb +2 -7
- metadata +4 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: becd9202bf711742b3c2641599a388314744fc13
|
4
|
+
data.tar.gz: 8c2897ef60b92f651693ef0591ded68337fffd5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e83d8b780b6fa2540fd13d53e19474e6310916881963e950a77a77af32f2e1603ff3cb6207a2303171b4a8a5725f45026a184e6e245869389acc9adf3267583
|
7
|
+
data.tar.gz: 1845ce7c0b69cafabadd56b36362080cd52ff2e3328a1a523c325d2762ff4885eb6a569d967fb21f4297320d195a23fd01430cdc552d472f15aedd328b51b0fe
|
data/.gitignore
CHANGED
data/README.mdown
CHANGED
@@ -1,12 +1,19 @@
|
|
1
|
-
cukeforker
|
2
|
-
==========
|
1
|
+
# cukeforker
|
3
2
|
|
4
3
|
Forking cukes and VNC displays.
|
5
4
|
|
6
5
|
[](http://travis-ci.org/jarib/cukeforker)
|
6
|
+
[](https://codeclimate.com/github/jarib/cukeforker)
|
7
|
+
[](https://coveralls.io/r/jarib/cukeforker)
|
8
|
+
|
9
|
+
### NB!
|
10
|
+
|
11
|
+
If you're using cukeforker with selenium-webdriver and Firefox, all versions prior to 2.40 has a bug where custom
|
12
|
+
Firefox profiles created in a forked process would not get cleaned up. Please make sure you're using selenium-webdriver >= 2.40
|
13
|
+
to avoid this.
|
14
|
+
|
15
|
+
## Usage
|
7
16
|
|
8
|
-
Usage
|
9
|
-
=============
|
10
17
|
|
11
18
|
```ruby
|
12
19
|
# parallelize per feature
|
data/cukeforker.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.add_dependency "cucumber", ">= 1.1.5"
|
18
18
|
s.add_dependency "vnctools", ">= 0.0.5"
|
19
19
|
s.add_development_dependency "rspec", "~> 2.5"
|
20
|
-
s.add_development_dependency "
|
20
|
+
s.add_development_dependency "coveralls"
|
21
21
|
s.add_development_dependency "rake", "~> 0.9.2"
|
22
22
|
|
23
23
|
s.files = `git ls-files`.split("\n")
|
data/lib/cukeforker/runner.rb
CHANGED
@@ -25,14 +25,15 @@ module CukeForker
|
|
25
25
|
include Observable
|
26
26
|
|
27
27
|
DEFAULT_OPTIONS = {
|
28
|
-
:max
|
29
|
-
:vnc
|
30
|
-
:record
|
31
|
-
:notify
|
32
|
-
:out
|
33
|
-
:log
|
34
|
-
:format
|
35
|
-
:delay
|
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
|
+
:fail_fast => false,
|
36
37
|
}
|
37
38
|
|
38
39
|
def self.run(features, opts = {})
|
@@ -48,6 +49,7 @@ module CukeForker
|
|
48
49
|
listeners = Array(opts[:notify])
|
49
50
|
extra_args = Array(opts[:extra_args])
|
50
51
|
delay = opts[:delay]
|
52
|
+
fail_fast = opts[:fail_fast]
|
51
53
|
|
52
54
|
if opts[:log]
|
53
55
|
listeners << LoggingListener.new
|
@@ -73,7 +75,7 @@ module CukeForker
|
|
73
75
|
end
|
74
76
|
end
|
75
77
|
|
76
|
-
queue = WorkerQueue.new
|
78
|
+
queue = WorkerQueue.new(max, delay, fail_fast)
|
77
79
|
features.each do |feature|
|
78
80
|
queue.add Worker.new(feature, format, out, extra_args)
|
79
81
|
end
|
data/lib/cukeforker/version.rb
CHANGED
data/lib/cukeforker/worker.rb
CHANGED
@@ -3,9 +3,9 @@ module CukeForker
|
|
3
3
|
include Observable
|
4
4
|
|
5
5
|
class << self
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
attr_writer :id
|
7
|
+
def id; @id ||= -1; end
|
8
|
+
end
|
9
9
|
|
10
10
|
attr_reader :status, :feature, :pid, :format, :out, :id, :data
|
11
11
|
|
@@ -40,10 +40,9 @@ module CukeForker
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def args
|
43
|
-
args = %W[--format #{
|
43
|
+
args = Array(format).flat_map { |f| %W[--format #{f} --out #{output(f)}] }
|
44
44
|
args += @extra_args
|
45
45
|
args << feature
|
46
|
-
|
47
46
|
args
|
48
47
|
end
|
49
48
|
|
@@ -57,7 +56,8 @@ module CukeForker
|
|
57
56
|
]"
|
58
57
|
end
|
59
58
|
|
60
|
-
def output
|
59
|
+
def output(format=nil)
|
60
|
+
format = @format if format.nil?
|
61
61
|
File.join out, "#{basename}.#{format}"
|
62
62
|
end
|
63
63
|
|
@@ -73,6 +73,15 @@ module CukeForker
|
|
73
73
|
@basename ||= feature.gsub(/\W/, '_')
|
74
74
|
end
|
75
75
|
|
76
|
+
def kill
|
77
|
+
return unless pid
|
78
|
+
|
79
|
+
Process.kill("TERM", pid)
|
80
|
+
Process.wait(pid)
|
81
|
+
rescue
|
82
|
+
# could not kill worker, ignore
|
83
|
+
end
|
84
|
+
|
76
85
|
private
|
77
86
|
|
78
87
|
def execute_cucumber
|
@@ -2,9 +2,10 @@ module CukeForker
|
|
2
2
|
class WorkerQueue
|
3
3
|
include Observable
|
4
4
|
|
5
|
-
def initialize(max, delay)
|
5
|
+
def initialize(max, delay, fail_fast=false)
|
6
6
|
@max = max
|
7
7
|
@delay = delay
|
8
|
+
@fail_fast = fail_fast
|
8
9
|
|
9
10
|
if @max < 0
|
10
11
|
raise ArgumentError, "max workers cannot be negative, got #{@max.inspect}"
|
@@ -121,6 +122,12 @@ module CukeForker
|
|
121
122
|
@running.delete worker
|
122
123
|
@finished << worker
|
123
124
|
|
125
|
+
if @fail_fast && worker.failed?
|
126
|
+
@pending.clear
|
127
|
+
@running.each { |w| w.kill }
|
128
|
+
@running.clear
|
129
|
+
end
|
130
|
+
|
124
131
|
fire :on_worker_finished, worker
|
125
132
|
end
|
126
133
|
|
@@ -14,13 +14,14 @@ module CukeForker
|
|
14
14
|
log = false
|
15
15
|
features = %w[a b]
|
16
16
|
delay = 1
|
17
|
+
fail_fast = false
|
17
18
|
|
18
19
|
mock_queue = double(WorkerQueue)
|
19
20
|
mock_workers = Array.new(2) { |n| double("Worker-#{n}") }
|
20
21
|
|
21
22
|
Process.stub(:pid => 1234)
|
22
23
|
|
23
|
-
WorkerQueue.should_receive(:new).with(max, 1).and_return mock_queue
|
24
|
+
WorkerQueue.should_receive(:new).with(max, 1, fail_fast).and_return mock_queue
|
24
25
|
Worker.should_receive(:new).with("a", :json, "/tmp", []).and_return mock_workers[0]
|
25
26
|
Worker.should_receive(:new).with("b", :json, "/tmp", []).and_return mock_workers[1]
|
26
27
|
|
@@ -29,12 +30,13 @@ module CukeForker
|
|
29
30
|
mock_queue.should_receive(:add).with mock_workers[1]
|
30
31
|
|
31
32
|
Runner.create(features,
|
32
|
-
:max
|
33
|
-
:notify
|
34
|
-
:format
|
35
|
-
:log
|
36
|
-
:out
|
37
|
-
:delay
|
33
|
+
:max => max,
|
34
|
+
:notify => listeners,
|
35
|
+
:format => format,
|
36
|
+
:log => false,
|
37
|
+
:out => out,
|
38
|
+
:delay => 1,
|
39
|
+
:fail_fast => fail_fast,
|
38
40
|
).should be_kind_of(Runner)
|
39
41
|
end
|
40
42
|
|
@@ -3,7 +3,8 @@ require File.expand_path("../../spec_helper", __FILE__)
|
|
3
3
|
module CukeForker
|
4
4
|
describe WorkerQueue do
|
5
5
|
let(:workers) { Array.new(5) { |n| double("Worker-#{n}") } }
|
6
|
-
let(:queue) { WorkerQueue.new(3, 0) }
|
6
|
+
let(:queue) { WorkerQueue.new(3, 0, false) }
|
7
|
+
|
7
8
|
|
8
9
|
it "adds an item to the queue" do
|
9
10
|
queue.should_not be_backed_up
|
@@ -50,9 +51,9 @@ module CukeForker
|
|
50
51
|
|
51
52
|
queue.fill
|
52
53
|
|
53
|
-
workers[0].stub(:finished? => true)
|
54
|
-
workers[1].stub(:finished? => true)
|
55
|
-
workers[2].stub(:finished? => false)
|
54
|
+
workers[0].stub(:finished? => true, :failed? => false)
|
55
|
+
workers[1].stub(:finished? => true, :failed? => false)
|
56
|
+
workers[2].stub(:finished? => false, :failed? => false)
|
56
57
|
|
57
58
|
queue.poll
|
58
59
|
|
@@ -70,9 +71,9 @@ module CukeForker
|
|
70
71
|
|
71
72
|
workers.each { |w| queue.add w }
|
72
73
|
|
73
|
-
workers[0].stub(:start => nil, :finished? => true)
|
74
|
-
workers[1].stub(:start => nil, :finished? => true)
|
75
|
-
workers[2].stub(:start => nil, :finished? => false)
|
74
|
+
workers[0].stub(:start => nil, :finished? => true, :failed? => false)
|
75
|
+
workers[1].stub(:start => nil, :finished? => true, :failed? => false)
|
76
|
+
workers[2].stub(:start => nil, :finished? => false, :failed? => false)
|
76
77
|
|
77
78
|
listener.should_receive(:on_worker_starting).exactly(3).times
|
78
79
|
queue.fill
|
@@ -124,9 +125,9 @@ module CukeForker
|
|
124
125
|
queue.stub :start_time => Time.now
|
125
126
|
workers[0..2].each { |w| queue.add w }
|
126
127
|
|
127
|
-
workers[0].stub(:start => nil)
|
128
|
-
workers[1].stub(:start => nil)
|
129
|
-
workers[2].stub(:start => nil)
|
128
|
+
workers[0].stub(:start => nil, :failed? => false)
|
129
|
+
workers[1].stub(:start => nil, :failed? => false)
|
130
|
+
workers[2].stub(:start => nil, :failed? => false)
|
130
131
|
|
131
132
|
workers[0].should_receive(:finished?).twice.and_return false, true
|
132
133
|
workers[1].should_receive(:finished?).twice.and_return false, true
|
@@ -148,13 +149,45 @@ module CukeForker
|
|
148
149
|
|
149
150
|
workers[0..2].each { |w| queue.add w }
|
150
151
|
|
151
|
-
workers[0].stub(:start => nil, :finished? => true)
|
152
|
-
workers[1].stub(:start => nil, :finished? => false)
|
153
|
-
workers[2].stub(:start => nil, :finished? => false)
|
152
|
+
workers[0].stub(:start => nil, :finished? => true, :failed? => false)
|
153
|
+
workers[1].stub(:start => nil, :finished? => false, :failed? => false)
|
154
|
+
workers[2].stub(:start => nil, :finished? => false, :failed? => false)
|
154
155
|
|
155
156
|
queue.fill
|
156
157
|
queue.poll
|
157
158
|
queue.eta.should == [Time.now + seconds_per_child*2, 2, 1]
|
158
159
|
end
|
159
|
-
|
160
|
+
|
161
|
+
context "with fail fast enabled" do
|
162
|
+
let(:workers) { Array.new(10) { |n| double("Worker-#{n}") } }
|
163
|
+
let(:queue) { WorkerQueue.new(3, 0, true) }
|
164
|
+
|
165
|
+
it "should exit after the first test failure" do
|
166
|
+
queue.should be_empty
|
167
|
+
|
168
|
+
workers.each { |w| queue.add w }
|
169
|
+
|
170
|
+
workers[0].stub(:start => nil, :finished? => true, :failed? => false)
|
171
|
+
workers[1].stub(:start => nil, :finished? => false, :failed? => false, :kill => nil)
|
172
|
+
workers[2].stub(:start => nil, :finished? => false, :failed? => false, :kill => nil)
|
173
|
+
queue.fill
|
174
|
+
queue.poll
|
175
|
+
|
176
|
+
workers[3].stub(:start => nil, :finished? => false, :failed? => true)
|
177
|
+
queue.fill
|
178
|
+
queue.poll
|
179
|
+
|
180
|
+
workers[3].stub(:finished? => true)
|
181
|
+
queue.fill
|
182
|
+
queue.poll
|
183
|
+
|
184
|
+
queue.should_not be_backed_up
|
185
|
+
queue.should be_empty
|
186
|
+
queue.instance_variable_get(:@finished).should == [ workers[0], workers[3] ]
|
187
|
+
|
188
|
+
queue.should have_failures
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end # WorkerQueue
|
160
193
|
end # CukeForker
|
@@ -25,6 +25,19 @@ module CukeForker
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
context "running a scenario with multiple report formats" do
|
29
|
+
formats = [ :json, :junit ]
|
30
|
+
path = "some/path"
|
31
|
+
let(:worker) { Worker.new("some/feature:51", formats, path) }
|
32
|
+
|
33
|
+
it "has an output file for each format specified" do
|
34
|
+
expected_args = formats.flat_map do |f|
|
35
|
+
%W[--format #{f} --out #{path}/some_feature_51.#{f}]
|
36
|
+
end
|
37
|
+
worker.args.each_cons(expected_args.size).include?(expected_args).should be_true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
28
41
|
it "creates an argument string based on the given parameters" do
|
29
42
|
worker.args.should == %w{--format json --out some/path/some_feature.json --extra args some/feature }
|
30
43
|
end
|
@@ -115,5 +128,22 @@ module CukeForker
|
|
115
128
|
Process.stub(:waitpid2).and_raise(Errno::ESRCH)
|
116
129
|
worker.should be_finished
|
117
130
|
end
|
131
|
+
|
132
|
+
it "can kill the child process" do
|
133
|
+
worker.stub(:pid => $$)
|
134
|
+
|
135
|
+
Process.should_receive(:kill)
|
136
|
+
Process.should_receive(:wait)
|
137
|
+
|
138
|
+
worker.kill
|
139
|
+
end
|
140
|
+
|
141
|
+
it "ignores failures when killing the child" do
|
142
|
+
worker.stub(:pid => $$)
|
143
|
+
|
144
|
+
Process.should_receive(:kill).and_raise(Errno::ECHILD)
|
145
|
+
worker.kill.should be_nil
|
146
|
+
end
|
147
|
+
|
118
148
|
end # Worker
|
119
149
|
end # CukeForker
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'simplecov'
|
4
|
-
SimpleCov.start {
|
5
|
-
add_filter "spec/"
|
6
|
-
}
|
7
|
-
end
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
8
3
|
|
9
4
|
$LOAD_PATH.unshift File.expand_path("../lib")
|
10
5
|
require 'cukeforker'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cukeforker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jari Bakken
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cucumber
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: coveralls
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - '>='
|
@@ -138,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
138
|
version: '0'
|
139
139
|
requirements: []
|
140
140
|
rubyforge_project: cukeforker
|
141
|
-
rubygems_version: 2.
|
141
|
+
rubygems_version: 2.0.14
|
142
142
|
signing_key:
|
143
143
|
specification_version: 4
|
144
144
|
summary: Library to maintain a forking queue of Cucumber processes
|
@@ -152,4 +152,3 @@ test_files:
|
|
152
152
|
- spec/cukeforker/worker_queue_spec.rb
|
153
153
|
- spec/cukeforker/worker_spec.rb
|
154
154
|
- spec/spec_helper.rb
|
155
|
-
has_rdoc:
|