flatware-rc 1.0.0.rc
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 +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
|