flatware-rc 1.0.0.rc
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +132 -0
- data/bin/flatware +6 -0
- data/lib/flatware.rb +10 -0
- data/lib/flatware/checkpoint_handler.rb +34 -0
- data/lib/flatware/cli.rb +89 -0
- data/lib/flatware/cucumber.rb +34 -0
- data/lib/flatware/cucumber/checkpoint.rb +28 -0
- data/lib/flatware/cucumber/formatter.rb +112 -0
- data/lib/flatware/cucumber/result.rb +27 -0
- data/lib/flatware/cucumber/runtime.rb +36 -0
- data/lib/flatware/cucumber/scenario_decorator.rb +24 -0
- data/lib/flatware/cucumber/scenario_result.rb +38 -0
- data/lib/flatware/cucumber/step_result.rb +29 -0
- data/lib/flatware/formatters.rb +26 -0
- data/lib/flatware/formatters/cucumber/console.rb +48 -0
- data/lib/flatware/formatters/cucumber/console/summary.rb +66 -0
- data/lib/flatware/formatters/cucumber/http.rb +83 -0
- data/lib/flatware/formatters/rspec/console.rb +33 -0
- data/lib/flatware/pids.rb +25 -0
- data/lib/flatware/poller.rb +35 -0
- data/lib/flatware/processor_info.rb +24 -0
- data/lib/flatware/rspec.rb +28 -0
- data/lib/flatware/rspec/checkpoint.rb +29 -0
- data/lib/flatware/rspec/example_notification.rb +21 -0
- data/lib/flatware/rspec/examples_notification.rb +24 -0
- data/lib/flatware/rspec/formatter.rb +50 -0
- data/lib/flatware/rspec/summary.rb +40 -0
- data/lib/flatware/serialized_exception.rb +24 -0
- data/lib/flatware/sink.rb +105 -0
- data/lib/flatware/sink/client.rb +25 -0
- data/lib/flatware/socket.rb +181 -0
- data/lib/flatware/version.rb +3 -0
- data/lib/flatware/worker.rb +57 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 29c812dd64d84ce34e23384de23597028e1d2f3e
|
4
|
+
data.tar.gz: d9c9b115efee14671c92f00d7a260e3689baf57f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 61bc760e517826f5961a65ef6fc5461038e02038d251af85326665ee36592ba14d213bd5185a6ce4056c2a019c9546407d332be8edb9a6698ac0748da4106c0b
|
7
|
+
data.tar.gz: 0671c7421a9668fc9fb59751b13336170b8ca00d3239f81a7ae3df92d82c9c532977577845f900c0b22fac0ab31449e44cca582554f079d135865f8691c6cca7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Brian Dunn
|
4
|
+
|
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:
|
11
|
+
|
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/bin/flatware
ADDED
data/lib/flatware.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Flatware
|
2
|
+
require 'flatware/checkpoint_handler'
|
3
|
+
require 'flatware/processor_info'
|
4
|
+
require 'flatware/cli'
|
5
|
+
require 'flatware/poller'
|
6
|
+
require 'flatware/formatters'
|
7
|
+
require 'flatware/sink'
|
8
|
+
require 'flatware/socket'
|
9
|
+
require 'flatware/worker'
|
10
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Flatware
|
2
|
+
class CheckpointHandler
|
3
|
+
attr_reader :formatter, :checkpoints
|
4
|
+
|
5
|
+
def initialize(formatter, fails_fast)
|
6
|
+
@fail_fast = fails_fast
|
7
|
+
@formatter = formatter
|
8
|
+
@checkpoints = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def handle!(checkpoint)
|
12
|
+
checkpoints << checkpoint
|
13
|
+
if checkpoint.failures? && fail_fast?
|
14
|
+
@done = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def done?
|
19
|
+
@done
|
20
|
+
end
|
21
|
+
|
22
|
+
def fail_fast?
|
23
|
+
@fail_fast
|
24
|
+
end
|
25
|
+
|
26
|
+
def summarize
|
27
|
+
formatter.summarize(checkpoints)
|
28
|
+
end
|
29
|
+
|
30
|
+
def had_failures?
|
31
|
+
checkpoints.any? &:failures?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/flatware/cli.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'flatware/pids'
|
3
|
+
module Flatware
|
4
|
+
class CLI < Thor
|
5
|
+
|
6
|
+
default_command :cucumber
|
7
|
+
|
8
|
+
def self.processors
|
9
|
+
@processors ||= ProcessorInfo.count
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.worker_option
|
13
|
+
method_option :workers, aliases: "-w", type: :numeric, default: processors, desc: "Number of concurent processes to run"
|
14
|
+
end
|
15
|
+
|
16
|
+
class_option :log, aliases: "-l", type: :boolean, desc: "Print debug messages to $stderr"
|
17
|
+
|
18
|
+
worker_option
|
19
|
+
method_option 'fail-fast', type: :boolean, default: false, desc: "Abort the run on first failure"
|
20
|
+
method_option 'formatters', aliases: "-f", type: :array, default: %w[console], desc: "The formatters to use for output"
|
21
|
+
method_option 'dispatch-endpoint', type: :string, default: 'ipc://dispatch'
|
22
|
+
method_option 'sink-endpoint', type: :string, default: 'ipc://task'
|
23
|
+
desc "cucumber [FLATWARE_OPTS] [CUCUMBER_ARGS]", "parallelizes cucumber with custom arguments"
|
24
|
+
def cucumber(*args)
|
25
|
+
require 'flatware/cucumber'
|
26
|
+
jobs = Cucumber.extract_jobs_from_args args
|
27
|
+
Flatware.verbose = options[:log]
|
28
|
+
worker_count = [workers, jobs.size].min
|
29
|
+
Worker.spawn worker_count, Cucumber, options['dispatch-endpoint'], options['sink-endpoint']
|
30
|
+
start_sink jobs: jobs, workers: worker_count
|
31
|
+
end
|
32
|
+
|
33
|
+
worker_option
|
34
|
+
method_option 'fail-fast', type: :boolean, default: false, desc: "Abort the run on first failure"
|
35
|
+
method_option 'formatters', aliases: "-f", type: :array, default: %w[console], desc: "The formatters to use for output"
|
36
|
+
method_option 'dispatch-endpoint', type: :string, default: 'ipc://dispatch'
|
37
|
+
method_option 'sink-endpoint', type: :string, default: 'ipc://task'
|
38
|
+
desc "rspec [FLATWARE_OPTS]", "parallelizes rspec"
|
39
|
+
def rspec(*rspec_args)
|
40
|
+
require 'flatware/rspec'
|
41
|
+
jobs = RSpec.extract_jobs_from_args rspec_args, workers: workers
|
42
|
+
Flatware.verbose = options[:log]
|
43
|
+
Worker.spawn workers, RSpec, options['dispatch-endpoint'], options['sink-endpoint']
|
44
|
+
start_sink jobs: jobs, workers: workers
|
45
|
+
end
|
46
|
+
|
47
|
+
worker_option
|
48
|
+
desc "fan [COMMAND]", "executes the given job on all of the workers"
|
49
|
+
def fan(*command)
|
50
|
+
Flatware.verbose = options[:log]
|
51
|
+
|
52
|
+
command = command.join(" ")
|
53
|
+
puts "Running '#{command}' on #{workers} workers"
|
54
|
+
|
55
|
+
workers.times do |i|
|
56
|
+
fork do
|
57
|
+
exec({"TEST_ENV_NUMBER" => i.to_s}, command)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
Process.waitall
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
desc "clear", "kills all flatware processes"
|
65
|
+
def clear
|
66
|
+
(Flatware.pids - [$$]).each do |pid|
|
67
|
+
Process.kill 6, pid
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def start_sink(jobs:, workers:, runner: current_command_chain.first)
|
74
|
+
$0 = 'flatware sink'
|
75
|
+
Process.setpgrp
|
76
|
+
formatter = Formatters.load_by_name(runner, options['formatters'])
|
77
|
+
passed = Sink.start_server jobs: jobs, formatter: formatter, sink: options['sink-endpoint'], dispatch: options['dispatch-endpoint'], fail_fast: options['fail-fast'], worker_count: workers
|
78
|
+
exit passed ? 0 : 1
|
79
|
+
end
|
80
|
+
|
81
|
+
def log(*args)
|
82
|
+
Flatware.log(*args)
|
83
|
+
end
|
84
|
+
|
85
|
+
def workers
|
86
|
+
options[:workers]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'cucumber'
|
2
|
+
require 'flatware/cucumber/checkpoint'
|
3
|
+
require 'flatware/cucumber/formatter'
|
4
|
+
require 'flatware/cucumber/result'
|
5
|
+
require 'flatware/cucumber/runtime'
|
6
|
+
require 'flatware/cucumber/scenario_decorator'
|
7
|
+
require 'flatware/cucumber/scenario_result'
|
8
|
+
require 'flatware/cucumber/step_result'
|
9
|
+
require 'flatware/formatters/cucumber/console'
|
10
|
+
|
11
|
+
module Flatware
|
12
|
+
module Cucumber
|
13
|
+
|
14
|
+
extend self
|
15
|
+
|
16
|
+
attr_reader :jobs
|
17
|
+
|
18
|
+
def extract_jobs_from_args(args=[], out_stream=$stdout, error_stream=$stderr)
|
19
|
+
raw_args = args.dup
|
20
|
+
config = ::Cucumber::Cli::Configuration.new(out_stream, error_stream)
|
21
|
+
config.parse! args
|
22
|
+
options = raw_args - args
|
23
|
+
@jobs = config.feature_files.map { |file| Job.new file, options }.to_a
|
24
|
+
end
|
25
|
+
|
26
|
+
def run(feature_files=[], options=[])
|
27
|
+
runtime.run feature_files, options
|
28
|
+
end
|
29
|
+
|
30
|
+
def runtime
|
31
|
+
@runtime ||= Runtime.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Flatware
|
2
|
+
module Cucumber
|
3
|
+
class Checkpoint
|
4
|
+
attr_reader :steps, :scenarios
|
5
|
+
def initialize(steps, scenarios)
|
6
|
+
@steps, @scenarios = serialize_steps(steps), serialize_scenarios(scenarios)
|
7
|
+
end
|
8
|
+
|
9
|
+
def failures?
|
10
|
+
scenarios.any? &:failed?
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def serialize_steps(steps)
|
16
|
+
steps.map do |step|
|
17
|
+
StepResult.new step.status, step.exception
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize_scenarios(scenarios)
|
22
|
+
scenarios.map do |scenario|
|
23
|
+
ScenarioResult.new scenario.status, scenario.file_colon_line, scenario.name, scenario.exception
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'flatware/sink'
|
2
|
+
require 'ostruct'
|
3
|
+
module Flatware
|
4
|
+
module Cucumber
|
5
|
+
class Formatter
|
6
|
+
|
7
|
+
def initialize(step_mother, *)
|
8
|
+
@collector = Collector.new step_mother
|
9
|
+
@scenarios = []
|
10
|
+
@in_a_step = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_features(*)
|
14
|
+
checkpoint = collector.checkpoint
|
15
|
+
scenarios.select(&:exception).map(&:file_colon_line).each do |file_colon_line|
|
16
|
+
scenario = checkpoint.scenarios.detect do |scenario|
|
17
|
+
scenario.file_colon_line == file_colon_line
|
18
|
+
end
|
19
|
+
scenario.failed_outside_step!(file_colon_line) if scenario
|
20
|
+
end
|
21
|
+
Sink::client.checkpoint checkpoint
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_step_result(_, _, _, status, *)
|
25
|
+
send_progress(status)
|
26
|
+
end
|
27
|
+
|
28
|
+
def table_cell_value(_, status)
|
29
|
+
send_progress(status) if status
|
30
|
+
end
|
31
|
+
|
32
|
+
def exception(exception, status)
|
33
|
+
unless @in_a_step
|
34
|
+
current_scenario.exception = exception
|
35
|
+
send_progress(status)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def respond_to?(x)
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def before_outline_table(*)
|
44
|
+
@in_examples = true
|
45
|
+
end
|
46
|
+
|
47
|
+
def after_outline_table(*)
|
48
|
+
@in_examples = false
|
49
|
+
end
|
50
|
+
|
51
|
+
def before_table_cell(*)
|
52
|
+
@in_a_step = @in_examples
|
53
|
+
end
|
54
|
+
|
55
|
+
def after_table_cell(*)
|
56
|
+
@in_a_step = ! @in_examples
|
57
|
+
end
|
58
|
+
|
59
|
+
def after_table_row(table_row)
|
60
|
+
exception(table_row.exception, :failed) if table_row.exception
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_step(*)
|
64
|
+
@in_a_step = true
|
65
|
+
end
|
66
|
+
|
67
|
+
def after_step(*)
|
68
|
+
@in_a_step = false
|
69
|
+
end
|
70
|
+
|
71
|
+
def scenario_name(_, name, file_colon_line, *)
|
72
|
+
scenarios.push OpenStruct.new file_colon_line: file_colon_line, name: name
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
attr_reader :collector, :scenarios
|
77
|
+
|
78
|
+
def current_scenario
|
79
|
+
scenarios.last
|
80
|
+
end
|
81
|
+
|
82
|
+
def send_progress(status)
|
83
|
+
Sink::client.progress Result.new status
|
84
|
+
end
|
85
|
+
|
86
|
+
class Collector
|
87
|
+
attr_reader :step_mother
|
88
|
+
def initialize(step_mother)
|
89
|
+
@step_mother = step_mother
|
90
|
+
snapshot
|
91
|
+
end
|
92
|
+
|
93
|
+
def checkpoint
|
94
|
+
Checkpoint.new(step_mother.steps - @ran_steps, decorate_scenarios(step_mother.scenarios - @ran_scenarios)).tap do
|
95
|
+
snapshot
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def snapshot
|
102
|
+
@ran_steps = step_mother.steps.dup
|
103
|
+
@ran_scenarios = step_mother.scenarios.dup
|
104
|
+
end
|
105
|
+
|
106
|
+
def decorate_scenarios(scenarios)
|
107
|
+
scenarios.map { |scenario| ScenarioDecorator.new(scenario) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|