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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +132 -0
  4. data/bin/flatware +6 -0
  5. data/lib/flatware.rb +10 -0
  6. data/lib/flatware/checkpoint_handler.rb +34 -0
  7. data/lib/flatware/cli.rb +89 -0
  8. data/lib/flatware/cucumber.rb +34 -0
  9. data/lib/flatware/cucumber/checkpoint.rb +28 -0
  10. data/lib/flatware/cucumber/formatter.rb +112 -0
  11. data/lib/flatware/cucumber/result.rb +27 -0
  12. data/lib/flatware/cucumber/runtime.rb +36 -0
  13. data/lib/flatware/cucumber/scenario_decorator.rb +24 -0
  14. data/lib/flatware/cucumber/scenario_result.rb +38 -0
  15. data/lib/flatware/cucumber/step_result.rb +29 -0
  16. data/lib/flatware/formatters.rb +26 -0
  17. data/lib/flatware/formatters/cucumber/console.rb +48 -0
  18. data/lib/flatware/formatters/cucumber/console/summary.rb +66 -0
  19. data/lib/flatware/formatters/cucumber/http.rb +83 -0
  20. data/lib/flatware/formatters/rspec/console.rb +33 -0
  21. data/lib/flatware/pids.rb +25 -0
  22. data/lib/flatware/poller.rb +35 -0
  23. data/lib/flatware/processor_info.rb +24 -0
  24. data/lib/flatware/rspec.rb +28 -0
  25. data/lib/flatware/rspec/checkpoint.rb +29 -0
  26. data/lib/flatware/rspec/example_notification.rb +21 -0
  27. data/lib/flatware/rspec/examples_notification.rb +24 -0
  28. data/lib/flatware/rspec/formatter.rb +50 -0
  29. data/lib/flatware/rspec/summary.rb +40 -0
  30. data/lib/flatware/serialized_exception.rb +24 -0
  31. data/lib/flatware/sink.rb +105 -0
  32. data/lib/flatware/sink/client.rb +25 -0
  33. data/lib/flatware/socket.rb +181 -0
  34. data/lib/flatware/version.rb +3 -0
  35. data/lib/flatware/worker.rb +57 -0
  36. 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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ lib = File.expand_path('../../lib', __FILE__)
3
+ $:.unshift lib unless $:.include? lib
4
+
5
+ require 'flatware'
6
+ Flatware::CLI.start
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
@@ -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