flatware-rc 1.0.0.rc

Sign up to get free protection for your applications and to get access to all the features.
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