flatware 0.3.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +18 -17
- data/README.md +132 -0
- data/lib/flatware/cli.rb +15 -13
- data/lib/flatware/cucumber/formatter.rb +2 -2
- data/lib/flatware/dispatcher.rb +7 -9
- data/lib/flatware/formatters.rb +4 -3
- data/lib/flatware/formatters/cucumber/console.rb +46 -0
- data/lib/flatware/formatters/cucumber/console/summary.rb +66 -0
- data/lib/flatware/formatters/{http.rb → cucumber/http.rb} +0 -0
- data/lib/flatware/sink.rb +13 -39
- data/lib/flatware/sink/client.rb +25 -0
- data/lib/flatware/socket.rb +5 -4
- data/lib/flatware/version.rb +1 -1
- data/lib/flatware/worker.rb +18 -12
- metadata +37 -30
- data/README.rdoc +0 -102
- data/lib/flatware/formatters/console.rb +0 -48
- data/lib/flatware/formatters/console/summary.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfb0b2fec0892b096fda85d7117ecb063ead5624
|
4
|
+
data.tar.gz: be3acf00d8248975adb052c4004bd2017b66c8f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8154c907e8c51bb3a83771cbfba07e4751b9b284965cfcd5ea634f8dd9563c28ee5da2f6c44ddbd6d326bff5ce440b42e3c30981e3f2ef3131c494037d5086a1
|
7
|
+
data.tar.gz: 4d1d8adb80cb44a973427cb1af117f321c597279246bb804d13c8978e22d33f2b2f367ebc0df7b6247b9423aae49dc1e42f487d5776c3eeafb87d71c4669abf0
|
data/LICENSE.txt
CHANGED
@@ -1,20 +1,21 @@
|
|
1
|
-
|
1
|
+
The MIT License (MIT)
|
2
2
|
|
3
|
-
|
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:
|
3
|
+
Copyright (c) 2014 Brian Dunn
|
10
4
|
|
11
|
-
|
12
|
-
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# Flatware [![Build Status][travis-badge]][travis] [![Code Climate][code-climate-badge]][code-climate]
|
2
|
+
|
3
|
+
[travis-badge]: https://travis-ci.org/briandunn/flatware.png
|
4
|
+
[travis]: http://travis-ci.org/briandunn/flatware
|
5
|
+
[code-climate-badge]: https://codeclimate.com/github/briandunn/flatware.png
|
6
|
+
[code-climate]: https://codeclimate.com/github/briandunn/flatware
|
7
|
+
|
8
|
+
Flatware is a distributed cucumber runner.
|
9
|
+
|
10
|
+
## Requirements
|
11
|
+
|
12
|
+
* ZeroMQ > 2.1
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this to your Gemfile:
|
17
|
+
|
18
|
+
```
|
19
|
+
gem 'flatware'
|
20
|
+
```
|
21
|
+
|
22
|
+
and `bundle install`.
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
To run your entire suite with the default cucumber options, just:
|
27
|
+
|
28
|
+
```
|
29
|
+
$ flatware
|
30
|
+
```
|
31
|
+
|
32
|
+
If you'd like to limit the number of forked workers, you can pass the 'w' flag:
|
33
|
+
|
34
|
+
```
|
35
|
+
$ flatware -w 3
|
36
|
+
```
|
37
|
+
|
38
|
+
You can also pass most cucumber options to Flatware. For example, to run only
|
39
|
+
features that are not tagged 'javascript', you can:
|
40
|
+
|
41
|
+
```
|
42
|
+
$ flatware cucumber -t ~@javascript
|
43
|
+
```
|
44
|
+
|
45
|
+
## Typical Usage in a Rails App
|
46
|
+
|
47
|
+
Add the following to your config/database.yml:
|
48
|
+
|
49
|
+
```
|
50
|
+
test:
|
51
|
+
database: foo_test
|
52
|
+
```
|
53
|
+
|
54
|
+
becomes:
|
55
|
+
|
56
|
+
```
|
57
|
+
test:
|
58
|
+
database: foo_test<%=ENV['TEST_ENV_NUMBER']%>
|
59
|
+
```
|
60
|
+
|
61
|
+
Run the following:
|
62
|
+
|
63
|
+
```
|
64
|
+
$ rake db:setup # if not already done
|
65
|
+
$ flatware fan rake db:test:prepare
|
66
|
+
```
|
67
|
+
|
68
|
+
Now you are ready to rock:
|
69
|
+
|
70
|
+
```
|
71
|
+
$ flatware
|
72
|
+
```
|
73
|
+
|
74
|
+
## Planned Features
|
75
|
+
|
76
|
+
* Reliable enough to use as part of your Continuous Integration system
|
77
|
+
* Always accounts for every feature you ask it to run
|
78
|
+
* Use heuristics to run your slowest tests first
|
79
|
+
* speak Cucumber's DRB protocol; if you know how to use Spork you know how to
|
80
|
+
use Flatware
|
81
|
+
|
82
|
+
## Design Goals
|
83
|
+
|
84
|
+
### Maintainable
|
85
|
+
|
86
|
+
* Fully test at an integration level. Don't be afraid to change the code. If you
|
87
|
+
break it you'll know.
|
88
|
+
* Couple as loosely as possible, and only to the most stable/public bits of
|
89
|
+
Cucumber.
|
90
|
+
|
91
|
+
### Minimal
|
92
|
+
|
93
|
+
* Projects define their own preperation scripts
|
94
|
+
* Only distribute to local cores (for now)
|
95
|
+
* Only handle cucumber
|
96
|
+
|
97
|
+
### Robust
|
98
|
+
|
99
|
+
* Depend on a dedicated messaging library
|
100
|
+
* Be acountable for completed work; provide progress report regardless of
|
101
|
+
completing the suite.
|
102
|
+
|
103
|
+
## Tinkering
|
104
|
+
|
105
|
+
Flatware is tested with [aruba][]. In order to get a demo cucumber project you
|
106
|
+
can add the `@no-clobber` tag to `features/flatware.feature` and run the test
|
107
|
+
with `cucumber features/flatware.feature`. Now you should have a `./tmp/aruba`
|
108
|
+
directory. CD there and `flatware` will be in your path so you can tinker away.
|
109
|
+
|
110
|
+
[aruba]: https://github.com/cucumber/aruba
|
111
|
+
|
112
|
+
## Resources
|
113
|
+
|
114
|
+
To learn more about the messaging system that Flatware uses, take a look at the
|
115
|
+
[excellent ZeroMQ guide][z].
|
116
|
+
|
117
|
+
[z]: http://zguide.zeromq.org/page:all
|
118
|
+
|
119
|
+
## Contributing to Flatware
|
120
|
+
|
121
|
+
* Check out the latest master to make sure the feature hasn't been implemented
|
122
|
+
or the bug hasn't been fixed yet
|
123
|
+
* Check out the issue tracker to make sure someone already hasn't requested it
|
124
|
+
and/or contributed it
|
125
|
+
* Fork the project
|
126
|
+
* Start a feature/bugfix branch
|
127
|
+
* Commit and push until you are happy with your contribution
|
128
|
+
* Make sure to add tests for it. This is important so I don't break it in a
|
129
|
+
future version unintentionally.
|
130
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to
|
131
|
+
have your own version, or is otherwise necessary, that is fine, but please
|
132
|
+
isolate to its own commit so I can cherry-pick around it.
|
data/lib/flatware/cli.rb
CHANGED
@@ -23,22 +23,14 @@ module Flatware
|
|
23
23
|
worker_option
|
24
24
|
method_option 'fail-fast', type: :boolean, default: false, desc: "Abort the run on first failure"
|
25
25
|
method_option 'formatters', aliases: "-f", type: :array, default: %w[console], desc: "The formatters to use for output"
|
26
|
+
method_option 'dispatch-endpoint', type: :string, default: 'ipc://dispatch'
|
27
|
+
method_option 'sink-endpoint', type: :string, default: 'ipc://task'
|
26
28
|
desc "[FLATWARE_OPTS] cucumber [CUCUMBER_ARGS]", "parallelizes cucumber with custom arguments"
|
27
29
|
def cucumber(*)
|
28
|
-
Process.setpgrp
|
29
|
-
Flatware.verbose = options[:log]
|
30
|
-
log "flatware options:", options
|
31
|
-
log "cucumber options:", cucumber_args
|
32
30
|
jobs = Cucumber.extract_jobs_from_args cucumber_args
|
33
|
-
Worker.spawn workers
|
34
|
-
|
35
|
-
|
36
|
-
Dispatcher.start jobs
|
37
|
-
end
|
38
|
-
$0 = 'flatware sink'
|
39
|
-
passed = Sink.start_server jobs, Formatters.load_by_name(options['formatters']), fail_fast: options['fail-fast']
|
40
|
-
Process.waitall
|
41
|
-
exit passed ? 0 : 1
|
31
|
+
Worker.spawn workers, Cucumber, options['dispatch-endpoint'], options['sink-endpoint']
|
32
|
+
formatter = Formatters.load_by_name(:cucumber, options['formatters'])
|
33
|
+
start_sink jobs, formatter
|
42
34
|
end
|
43
35
|
|
44
36
|
worker_option
|
@@ -67,6 +59,16 @@ module Flatware
|
|
67
59
|
|
68
60
|
private
|
69
61
|
|
62
|
+
def start_sink(jobs, formatter)
|
63
|
+
$0 = 'flatware sink'
|
64
|
+
Process.setpgrp
|
65
|
+
Flatware.verbose = options[:log]
|
66
|
+
Dispatcher.spawn jobs, options['dispatch-endpoint']
|
67
|
+
passed = Sink.start_server jobs, formatter, options['sink-endpoint'], fail_fast: options['fail-fast']
|
68
|
+
Process.waitall
|
69
|
+
exit passed ? 0 : 1
|
70
|
+
end
|
71
|
+
|
70
72
|
def cucumber_args
|
71
73
|
if index = ARGV.index('cucumber')
|
72
74
|
ARGV[index + 1..-1]
|
@@ -20,7 +20,7 @@ module Flatware
|
|
20
20
|
end
|
21
21
|
scenario.failed_outside_step!(file_colon_line) if scenario
|
22
22
|
end
|
23
|
-
Sink.checkpoint checkpoint
|
23
|
+
Sink::client.checkpoint checkpoint
|
24
24
|
end
|
25
25
|
|
26
26
|
def after_step_result(_, _, _, status, *)
|
@@ -82,7 +82,7 @@ module Flatware
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def send_progress(status)
|
85
|
-
Sink.progress Result.new status
|
85
|
+
Sink::client.progress Result.new status
|
86
86
|
end
|
87
87
|
|
88
88
|
class Collector
|
data/lib/flatware/dispatcher.rb
CHANGED
@@ -1,19 +1,17 @@
|
|
1
1
|
module Flatware
|
2
2
|
class Dispatcher
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
exit 1
|
3
|
+
def self.spawn(jobs, endpoint)
|
4
|
+
fork do
|
5
|
+
$0 = 'flatware dispatcher'
|
6
|
+
trap('INT') { exit 1 }
|
7
|
+
new(jobs, endpoint).dispatch!
|
9
8
|
end
|
10
|
-
new(jobs).dispatch!
|
11
9
|
end
|
12
10
|
|
13
|
-
def initialize(jobs)
|
11
|
+
def initialize(jobs, endpoint)
|
14
12
|
@jobs = jobs
|
15
13
|
@fireable = Fireable.new
|
16
|
-
@dispatch = Flatware.socket ZMQ::REP, bind:
|
14
|
+
@dispatch = Flatware.socket ZMQ::REP, bind: endpoint
|
17
15
|
end
|
18
16
|
|
19
17
|
def dispatch!
|
data/lib/flatware/formatters.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Flatware
|
2
2
|
module Formatters
|
3
|
-
def self.load_by_name(names)
|
3
|
+
def self.load_by_name(runner, names)
|
4
4
|
formatters = names.map do |name|
|
5
|
-
require "flatware/formatters/#{name}"
|
6
|
-
|
5
|
+
require "flatware/formatters/#{runner}/#{name}"
|
6
|
+
namespace = const_get({cucumber: 'Cucumber'}.fetch(runner))
|
7
|
+
klass = namespace.const_get name.capitalize
|
7
8
|
klass.new $stdout, $stderr
|
8
9
|
end
|
9
10
|
Broadcaster.new formatters
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'flatware/formatters/cucumber/console/summary'
|
2
|
+
require 'cucumber/formatter/console'
|
3
|
+
|
4
|
+
module Flatware::Formatters::Cucumber
|
5
|
+
class Console
|
6
|
+
#for format_string
|
7
|
+
include ::Cucumber::Formatter::Console
|
8
|
+
|
9
|
+
FORMATS = {
|
10
|
+
passed: '.',
|
11
|
+
failed: 'F',
|
12
|
+
undefined: 'U',
|
13
|
+
pending: 'P',
|
14
|
+
skipped: '-'
|
15
|
+
}
|
16
|
+
|
17
|
+
STATUSES = FORMATS.keys
|
18
|
+
|
19
|
+
attr_reader :out, :err
|
20
|
+
|
21
|
+
def initialize(stdout, stderr)
|
22
|
+
@out, @err = stdout, stderr
|
23
|
+
end
|
24
|
+
|
25
|
+
def progress(result)
|
26
|
+
out.print format result.progress
|
27
|
+
end
|
28
|
+
|
29
|
+
def summarize(steps, scenarios)
|
30
|
+
Summary.new(steps, scenarios, out).summarize
|
31
|
+
end
|
32
|
+
|
33
|
+
def summarize_remaining(remaining_jobs)
|
34
|
+
out.puts
|
35
|
+
out.puts "The following features have not been run:"
|
36
|
+
for job in remaining_jobs
|
37
|
+
out.puts job.id
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def format(status)
|
43
|
+
format_string FORMATS[status], status
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'cucumber/formatter/console'
|
2
|
+
require 'flatware/formatters/cucumber/console'
|
3
|
+
require 'flatware/checkpoint'
|
4
|
+
|
5
|
+
module Flatware::Formatters::Cucumber
|
6
|
+
class Console
|
7
|
+
class Summary
|
8
|
+
include ::Cucumber::Formatter::Console
|
9
|
+
attr_reader :io, :steps, :scenarios
|
10
|
+
|
11
|
+
def initialize(steps, scenarios=[], io=StringIO.new)
|
12
|
+
@io = io
|
13
|
+
@steps = steps
|
14
|
+
@scenarios = scenarios
|
15
|
+
end
|
16
|
+
|
17
|
+
def summarize
|
18
|
+
2.times { io.puts }
|
19
|
+
print_failures(steps, 'step')
|
20
|
+
print_failures(scenarios.select(&:failed_outside_step?), 'scenario')
|
21
|
+
print_failed_scenarios scenarios
|
22
|
+
print_counts 'scenario', scenarios
|
23
|
+
print_counts 'step', steps
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def print_failed_scenarios(scenarios)
|
29
|
+
return unless scenarios.any? &with_status(:failed)
|
30
|
+
|
31
|
+
io.puts format_string "Failing Scenarios:", :failed
|
32
|
+
scenarios.select(&with_status(:failed)).sort_by(&:file_colon_line).each do |scenario|
|
33
|
+
io.puts format_string(scenario.file_colon_line, :failed) + format_string(" # Scenario: " + scenario.name, :comment)
|
34
|
+
end
|
35
|
+
io.puts
|
36
|
+
end
|
37
|
+
|
38
|
+
def print_failures(collection, label)
|
39
|
+
failures = collection.select(&with_status(:failed))
|
40
|
+
print_elements failures, :failed, pluralize(label, failures.size)
|
41
|
+
end
|
42
|
+
|
43
|
+
def print_counts(label, collection)
|
44
|
+
io.puts pluralize(label, collection.size) + count_summary(collection)
|
45
|
+
end
|
46
|
+
|
47
|
+
def pluralize(word, number)
|
48
|
+
"#{number} #{number == 1 ? word : word + 's'}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def with_status(status)
|
52
|
+
proc {|r| r.status == status}
|
53
|
+
end
|
54
|
+
|
55
|
+
def count_summary(results)
|
56
|
+
return "" unless results.any?
|
57
|
+
status_counts = STATUSES.map do |status|
|
58
|
+
count = results.select(&with_status(status)).size
|
59
|
+
format_string "#{count} #{status}", status if count > 0
|
60
|
+
end.compact.join ", "
|
61
|
+
|
62
|
+
" (#{status_counts})"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
File without changes
|
data/lib/flatware/sink.rb
CHANGED
@@ -1,34 +1,20 @@
|
|
1
1
|
require 'flatware'
|
2
2
|
module Flatware
|
3
|
-
|
4
|
-
|
5
|
-
class << self
|
6
|
-
def push(message)
|
7
|
-
client.push message
|
8
|
-
end
|
9
|
-
|
10
|
-
def start_server(*args)
|
11
|
-
Server.new(*args).start
|
12
|
-
end
|
3
|
+
module Sink
|
4
|
+
extend self
|
13
5
|
|
14
|
-
|
15
|
-
|
16
|
-
def client
|
17
|
-
@client ||= Client.new
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
%w[finished started progress checkpoint].each do |message|
|
22
|
-
define_singleton_method message do |content|
|
23
|
-
push [message.to_sym, content]
|
24
|
-
end
|
6
|
+
def start_server(*args)
|
7
|
+
Server.new(*args).start
|
25
8
|
end
|
26
9
|
|
27
10
|
class Server
|
28
|
-
|
11
|
+
attr_reader :socket
|
12
|
+
|
13
|
+
def initialize(jobs, formatter, endpoint, options={})
|
29
14
|
@jobs, @formatter = jobs, formatter
|
30
15
|
options = {fail_fast: false}.merge options
|
31
16
|
@fail_fast = options[:fail_fast]
|
17
|
+
@socket = Flatware.socket(ZMQ::PULL, bind: endpoint)
|
32
18
|
end
|
33
19
|
|
34
20
|
def start
|
@@ -64,13 +50,17 @@ module Flatware
|
|
64
50
|
end
|
65
51
|
end
|
66
52
|
checkpoint_handler.summarize
|
67
|
-
!
|
53
|
+
!failures?
|
68
54
|
rescue Error => e
|
69
55
|
raise unless e.message == "Interrupted system call"
|
70
56
|
end
|
71
57
|
|
72
58
|
private
|
73
59
|
|
60
|
+
def failures?
|
61
|
+
checkpoint_handler.had_failures? || completed_jobs.any?(&:failed?)
|
62
|
+
end
|
63
|
+
|
74
64
|
def fail_fast?
|
75
65
|
@fail_fast
|
76
66
|
end
|
@@ -101,22 +91,6 @@ module Flatware
|
|
101
91
|
def fireable
|
102
92
|
@fireable ||= Fireable.new
|
103
93
|
end
|
104
|
-
|
105
|
-
def socket
|
106
|
-
@socket ||= Flatware.socket(ZMQ::PULL, bind: PORT)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
class Client
|
111
|
-
def push(message)
|
112
|
-
socket.send message
|
113
|
-
end
|
114
|
-
|
115
|
-
private
|
116
|
-
|
117
|
-
def socket
|
118
|
-
@socket ||= Flatware.socket(ZMQ::PUSH, connect: PORT)
|
119
|
-
end
|
120
94
|
end
|
121
95
|
end
|
122
96
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'flatware/socket'
|
2
|
+
module Flatware
|
3
|
+
module Sink
|
4
|
+
extend self
|
5
|
+
attr_accessor :client
|
6
|
+
|
7
|
+
class Client
|
8
|
+
def initialize(sink_endpoint)
|
9
|
+
@socket = Flatware.socket ZMQ::PUSH, connect: sink_endpoint
|
10
|
+
end
|
11
|
+
|
12
|
+
%w[finished started progress checkpoint].each do |message|
|
13
|
+
define_method message do |content|
|
14
|
+
push [message.to_sym, content]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def push(message)
|
21
|
+
@socket.send message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/flatware/socket.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'ffi-rzmq'
|
2
2
|
|
3
3
|
module Flatware
|
4
|
-
SINK_PORT = 'ipc://sink'
|
5
|
-
DISPATCH_PORT = 'ipc://dispatch'
|
6
|
-
|
7
4
|
Error = Class.new StandardError
|
8
5
|
|
9
6
|
Job = Struct.new :id, :args do
|
10
7
|
attr_accessor :worker
|
8
|
+
attr_writer :failed
|
9
|
+
|
10
|
+
def failed?
|
11
|
+
!!@failed
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
13
15
|
extend self
|
@@ -52,7 +54,6 @@ module Flatware
|
|
52
54
|
sockets.push socket
|
53
55
|
if port = options[:connect]
|
54
56
|
socket.connect port
|
55
|
-
sleep 0.05
|
56
57
|
end
|
57
58
|
if port = options[:bind]
|
58
59
|
socket.bind port
|
data/lib/flatware/version.rb
CHANGED
data/lib/flatware/worker.rb
CHANGED
@@ -1,41 +1,47 @@
|
|
1
|
+
require 'flatware/sink/client'
|
1
2
|
module Flatware
|
2
3
|
class Worker
|
3
4
|
attr_reader :id
|
4
5
|
|
5
|
-
def
|
6
|
-
new(id).listen
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(id)
|
6
|
+
def initialize(id, runner, dispatch_endpoint, sink_endpoint)
|
10
7
|
@id = id
|
8
|
+
@runner = runner
|
11
9
|
@fireable = Fireable.new
|
12
|
-
@
|
10
|
+
@sink = Sink::Client.new sink_endpoint
|
11
|
+
@task = Flatware.socket ZMQ::REQ, connect: dispatch_endpoint
|
13
12
|
end
|
14
13
|
|
15
|
-
def self.spawn(worker_count)
|
14
|
+
def self.spawn(worker_count, runner, dispatch_endpoint, sink_endpoint)
|
16
15
|
worker_count.times do |i|
|
17
16
|
fork do
|
18
17
|
$0 = "flatware worker #{i}"
|
19
18
|
ENV['TEST_ENV_NUMBER'] = i.to_s
|
20
|
-
|
19
|
+
new(i, runner, dispatch_endpoint, sink_endpoint).listen
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
25
24
|
def listen
|
25
|
+
Sink.client = sink
|
26
26
|
report_for_duty
|
27
27
|
fireable.until_fired task do |job|
|
28
28
|
job.worker = id
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
sink.started job
|
30
|
+
begin
|
31
|
+
runner.run job.id, job.args
|
32
|
+
rescue Errno::ENOENT
|
33
|
+
job.failed = true
|
34
|
+
sink.finished job
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
sink.finished job
|
32
38
|
report_for_duty
|
33
39
|
end
|
34
40
|
end
|
35
41
|
|
36
42
|
private
|
37
43
|
|
38
|
-
attr_reader :fireable, :task
|
44
|
+
attr_reader :fireable, :task, :sink, :runner
|
39
45
|
|
40
46
|
def report_for_duty
|
41
47
|
task.send 'ready'
|
metadata
CHANGED
@@ -1,99 +1,105 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flatware
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Dunn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi-rzmq
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: thor
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0.13'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.13'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: cucumber
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 1.3.0
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '2.0'
|
48
51
|
type: :runtime
|
49
52
|
prerelease: false
|
50
53
|
version_requirements: !ruby/object:Gem::Requirement
|
51
54
|
requirements:
|
52
|
-
- -
|
55
|
+
- - ">="
|
53
56
|
- !ruby/object:Gem::Version
|
54
57
|
version: 1.3.0
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.0'
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
62
|
name: aruba
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
58
64
|
requirements:
|
59
|
-
- - ~>
|
65
|
+
- - "~>"
|
60
66
|
- !ruby/object:Gem::Version
|
61
67
|
version: 0.5.1
|
62
68
|
type: :development
|
63
69
|
prerelease: false
|
64
70
|
version_requirements: !ruby/object:Gem::Requirement
|
65
71
|
requirements:
|
66
|
-
- - ~>
|
72
|
+
- - "~>"
|
67
73
|
- !ruby/object:Gem::Version
|
68
74
|
version: 0.5.1
|
69
75
|
- !ruby/object:Gem::Dependency
|
70
76
|
name: rake
|
71
77
|
requirement: !ruby/object:Gem::Requirement
|
72
78
|
requirements:
|
73
|
-
- - ~>
|
79
|
+
- - "~>"
|
74
80
|
- !ruby/object:Gem::Version
|
75
|
-
version: 10.0
|
81
|
+
version: 10.1.0
|
76
82
|
type: :development
|
77
83
|
prerelease: false
|
78
84
|
version_requirements: !ruby/object:Gem::Requirement
|
79
85
|
requirements:
|
80
|
-
- - ~>
|
86
|
+
- - "~>"
|
81
87
|
- !ruby/object:Gem::Version
|
82
|
-
version: 10.0
|
88
|
+
version: 10.1.0
|
83
89
|
- !ruby/object:Gem::Dependency
|
84
90
|
name: rspec
|
85
91
|
requirement: !ruby/object:Gem::Requirement
|
86
92
|
requirements:
|
87
|
-
- - ~>
|
93
|
+
- - "~>"
|
88
94
|
- !ruby/object:Gem::Version
|
89
|
-
version: 2.
|
95
|
+
version: 2.14.0
|
90
96
|
type: :development
|
91
97
|
prerelease: false
|
92
98
|
version_requirements: !ruby/object:Gem::Requirement
|
93
99
|
requirements:
|
94
|
-
- - ~>
|
100
|
+
- - "~>"
|
95
101
|
- !ruby/object:Gem::Version
|
96
|
-
version: 2.
|
102
|
+
version: 2.14.0
|
97
103
|
description: A distributed cucumber runner
|
98
104
|
email: brian@hashrocket.com
|
99
105
|
executables:
|
@@ -101,8 +107,11 @@ executables:
|
|
101
107
|
extensions: []
|
102
108
|
extra_rdoc_files:
|
103
109
|
- LICENSE.txt
|
104
|
-
- README.
|
110
|
+
- README.md
|
105
111
|
files:
|
112
|
+
- LICENSE.txt
|
113
|
+
- README.md
|
114
|
+
- bin/flatware
|
106
115
|
- lib/flatware.rb
|
107
116
|
- lib/flatware/checkpoint.rb
|
108
117
|
- lib/flatware/checkpoint_handler.rb
|
@@ -113,9 +122,9 @@ files:
|
|
113
122
|
- lib/flatware/dispatcher.rb
|
114
123
|
- lib/flatware/fireable.rb
|
115
124
|
- lib/flatware/formatters.rb
|
116
|
-
- lib/flatware/formatters/console.rb
|
117
|
-
- lib/flatware/formatters/console/summary.rb
|
118
|
-
- lib/flatware/formatters/http.rb
|
125
|
+
- lib/flatware/formatters/cucumber/console.rb
|
126
|
+
- lib/flatware/formatters/cucumber/console/summary.rb
|
127
|
+
- lib/flatware/formatters/cucumber/http.rb
|
119
128
|
- lib/flatware/pids.rb
|
120
129
|
- lib/flatware/poller.rb
|
121
130
|
- lib/flatware/processor_info.rb
|
@@ -124,13 +133,11 @@ files:
|
|
124
133
|
- lib/flatware/scenario_result.rb
|
125
134
|
- lib/flatware/serialized_exception.rb
|
126
135
|
- lib/flatware/sink.rb
|
136
|
+
- lib/flatware/sink/client.rb
|
127
137
|
- lib/flatware/socket.rb
|
128
138
|
- lib/flatware/step_result.rb
|
129
139
|
- lib/flatware/version.rb
|
130
140
|
- lib/flatware/worker.rb
|
131
|
-
- LICENSE.txt
|
132
|
-
- README.rdoc
|
133
|
-
- bin/flatware
|
134
141
|
homepage: http://github.com/briandunn/flatware
|
135
142
|
licenses:
|
136
143
|
- MIT
|
@@ -141,17 +148,17 @@ require_paths:
|
|
141
148
|
- lib
|
142
149
|
required_ruby_version: !ruby/object:Gem::Requirement
|
143
150
|
requirements:
|
144
|
-
- -
|
151
|
+
- - ">="
|
145
152
|
- !ruby/object:Gem::Version
|
146
153
|
version: '0'
|
147
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
155
|
requirements:
|
149
|
-
- -
|
156
|
+
- - ">="
|
150
157
|
- !ruby/object:Gem::Version
|
151
158
|
version: '0'
|
152
159
|
requirements: []
|
153
160
|
rubyforge_project:
|
154
|
-
rubygems_version: 2.
|
161
|
+
rubygems_version: 2.4.6
|
155
162
|
signing_key:
|
156
163
|
specification_version: 4
|
157
164
|
summary: A distributed cucumber runner
|
data/README.rdoc
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
= Flatware
|
2
|
-
|
3
|
-
{<img src="https://travis-ci.org/briandunn/flatware.png" />}[https://travis-ci.org/briandunn/flatware]
|
4
|
-
{<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/briandunn/flatware]
|
5
|
-
|
6
|
-
Flatware is a distributed cucumber runner.
|
7
|
-
|
8
|
-
== Requirements
|
9
|
-
|
10
|
-
* ZeroMQ > 2.1
|
11
|
-
|
12
|
-
== Installation
|
13
|
-
|
14
|
-
Add this to your Gemfile:
|
15
|
-
|
16
|
-
gem 'flatware'
|
17
|
-
|
18
|
-
and `bundle install`.
|
19
|
-
|
20
|
-
== Usage
|
21
|
-
|
22
|
-
To run your entire suite with the default cucumber options, just:
|
23
|
-
|
24
|
-
flatware
|
25
|
-
|
26
|
-
If you'd like to limit the number of forked workers, you can pass the 'w' flag:
|
27
|
-
|
28
|
-
flatware -w 3
|
29
|
-
|
30
|
-
You can also pass most cucumber options to flatware. For example, to run only features that are not tagged 'javascript', you can:
|
31
|
-
|
32
|
-
flatware cucumber -t ~@javascript
|
33
|
-
|
34
|
-
== Typical Usage in a Rails App
|
35
|
-
|
36
|
-
Add the following to your config/database.yml:
|
37
|
-
|
38
|
-
test:
|
39
|
-
database: foo_test
|
40
|
-
|
41
|
-
becomes:
|
42
|
-
|
43
|
-
test:
|
44
|
-
database: foo_test<%=ENV['TEST_ENV_NUMBER']%>
|
45
|
-
|
46
|
-
Run the following:
|
47
|
-
|
48
|
-
rake db:setup # if not already done
|
49
|
-
flatware fan rake db:test:prepare
|
50
|
-
|
51
|
-
Now you are ready to rock:
|
52
|
-
|
53
|
-
flatware
|
54
|
-
|
55
|
-
== Planned Features
|
56
|
-
|
57
|
-
* Reliable enough to use as part of your Continuous Integration system
|
58
|
-
* Always accounts for every feature you ask it to run
|
59
|
-
* Use heuristics to run your slowest tests first
|
60
|
-
* speak Cucumber's DRB protocol; if you know how to use Spork you know how to use Flatware
|
61
|
-
|
62
|
-
== Design Goals
|
63
|
-
|
64
|
-
=== Maintainable
|
65
|
-
|
66
|
-
* Fully test at an integration level. Don't be afraid to change the code. If you break it you'll know.
|
67
|
-
* Couple as loosely as possible, and only to the most stable/public bits of Cucumber.
|
68
|
-
|
69
|
-
=== Minimal
|
70
|
-
|
71
|
-
* Projects define their own preperation scripts
|
72
|
-
* Only distribute to local cores (for now)
|
73
|
-
* Only handle cucumber
|
74
|
-
|
75
|
-
=== Robust
|
76
|
-
|
77
|
-
* Depend on a dedicated messaging library
|
78
|
-
* Be acountable for completed work; provide progress report regardless of completing the suite.
|
79
|
-
|
80
|
-
== Tinkering
|
81
|
-
|
82
|
-
Flatware is tested with aruba. In order to get a demo cucumber project you can add the `@no-clobber` tag to `features/flatware.feature` and run the test with `cucumber features/flatware.feature`. Now you should have a `./tmp/aruba` directory. CD there and `flatware` will be in your path so you can tinker away.
|
83
|
-
|
84
|
-
== Resources
|
85
|
-
|
86
|
-
The excellent ZeroMQ guide: http://zguide.zeromq.org/page:all
|
87
|
-
|
88
|
-
== Contributing to Flatware
|
89
|
-
|
90
|
-
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
91
|
-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
92
|
-
* Fork the project
|
93
|
-
* Start a feature/bugfix branch
|
94
|
-
* Commit and push until you are happy with your contribution
|
95
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
96
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
97
|
-
|
98
|
-
== Copyright
|
99
|
-
|
100
|
-
Copyright (c) 2011-2012 Brian Dunn. See LICENSE.txt for
|
101
|
-
further details.
|
102
|
-
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'flatware/formatters/console/summary'
|
2
|
-
require 'cucumber/formatter/console'
|
3
|
-
|
4
|
-
module Flatware
|
5
|
-
module Formatters
|
6
|
-
class Console
|
7
|
-
#for format_string
|
8
|
-
include ::Cucumber::Formatter::Console
|
9
|
-
|
10
|
-
FORMATS = {
|
11
|
-
passed: '.',
|
12
|
-
failed: 'F',
|
13
|
-
undefined: 'U',
|
14
|
-
pending: 'P',
|
15
|
-
skipped: '-'
|
16
|
-
}
|
17
|
-
|
18
|
-
STATUSES = FORMATS.keys
|
19
|
-
|
20
|
-
attr_reader :out, :err
|
21
|
-
|
22
|
-
def initialize(stdout, stderr)
|
23
|
-
@out, @err = stdout, stderr
|
24
|
-
end
|
25
|
-
|
26
|
-
def progress(result)
|
27
|
-
out.print format result.progress
|
28
|
-
end
|
29
|
-
|
30
|
-
def summarize(steps, scenarios)
|
31
|
-
Summary.new(steps, scenarios, out).summarize
|
32
|
-
end
|
33
|
-
|
34
|
-
def summarize_remaining(remaining_jobs)
|
35
|
-
out.puts
|
36
|
-
out.puts "The following features have not been run:"
|
37
|
-
for job in remaining_jobs
|
38
|
-
out.puts job.id
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
def format(status)
|
44
|
-
format_string FORMATS[status], status
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'cucumber/formatter/console'
|
2
|
-
require 'flatware/formatters/console'
|
3
|
-
require 'flatware/checkpoint'
|
4
|
-
module Flatware
|
5
|
-
module Formatters
|
6
|
-
class Console
|
7
|
-
class Summary
|
8
|
-
include ::Cucumber::Formatter::Console
|
9
|
-
attr_reader :io, :steps, :scenarios
|
10
|
-
|
11
|
-
def initialize(steps, scenarios=[], io=StringIO.new)
|
12
|
-
@io = io
|
13
|
-
@steps = steps
|
14
|
-
@scenarios = scenarios
|
15
|
-
end
|
16
|
-
|
17
|
-
def summarize
|
18
|
-
2.times { io.puts }
|
19
|
-
print_failures(steps, 'step')
|
20
|
-
print_failures(scenarios.select(&:failed_outside_step?), 'scenario')
|
21
|
-
print_failed_scenarios scenarios
|
22
|
-
print_counts 'scenario', scenarios
|
23
|
-
print_counts 'step', steps
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def print_failed_scenarios(scenarios)
|
29
|
-
return unless scenarios.any? &with_status(:failed)
|
30
|
-
|
31
|
-
io.puts format_string "Failing Scenarios:", :failed
|
32
|
-
scenarios.select(&with_status(:failed)).sort_by(&:file_colon_line).each do |scenario|
|
33
|
-
io.puts format_string(scenario.file_colon_line, :failed) + format_string(" # Scenario: " + scenario.name, :comment)
|
34
|
-
end
|
35
|
-
io.puts
|
36
|
-
end
|
37
|
-
|
38
|
-
def print_failures(collection, label)
|
39
|
-
failures = collection.select(&with_status(:failed))
|
40
|
-
print_elements failures, :failed, pluralize(label, failures.size)
|
41
|
-
end
|
42
|
-
|
43
|
-
def print_counts(label, collection)
|
44
|
-
io.puts pluralize(label, collection.size) + count_summary(collection)
|
45
|
-
end
|
46
|
-
|
47
|
-
def pluralize(word, number)
|
48
|
-
"#{number} #{number == 1 ? word : word + 's'}"
|
49
|
-
end
|
50
|
-
|
51
|
-
def with_status(status)
|
52
|
-
proc {|r| r.status == status}
|
53
|
-
end
|
54
|
-
|
55
|
-
def count_summary(results)
|
56
|
-
return "" unless results.any?
|
57
|
-
status_counts = STATUSES.map do |status|
|
58
|
-
count = results.select(&with_status(status)).size
|
59
|
-
format_string "#{count} #{status}", status if count > 0
|
60
|
-
end.compact.join ", "
|
61
|
-
|
62
|
-
" (#{status_counts})"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|