flatware-cucumber 0.4.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af10de976bd3f5ab9978612d0eca4303fce90326
4
+ data.tar.gz: 06c0102f0d981ac87c918a77a346b35abd9dbd8f
5
+ SHA512:
6
+ metadata.gz: f54ab4f38b3b837151349f8d453ef39f27a3855fada7cffd042c07c0afc7e54c60cf2f806f4725896d7c0a8311d87fd0f6ad96eee5103e212ed91e54f53304be
7
+ data.tar.gz: 1f3b63d49b7ffb911bfc49fec675f9e8b54e87b9756716d48a85dd64e862f185dce981955c6d8de32d774daa2e103f18d5a579edc92c0083a2a690835343bbfc
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,176 @@
1
+ # Flatware [![Build Status][travis-badge]][travis] [![Code Climate][code-climate-badge]][code-climate]
2
+
3
+ [travis-badge]: https://travis-ci.org/briandunn/flatware.svg?branch=master
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 parallelizes your test suite to significantly reduce test time.
9
+
10
+ ## Requirements
11
+
12
+ * ZeroMQ > 4.0
13
+
14
+ ## Installation
15
+
16
+ ### ZeroMQ
17
+
18
+ #### Linux Ubuntu
19
+
20
+ ```sh
21
+ sudo apt-get install -qq libzmq3-dev
22
+ ```
23
+
24
+ (Never you mind the 3. This package contains ZMQ version 4.)
25
+
26
+ #### Mac OSX
27
+
28
+ Ruby FFI isn't getting along with the latest ZMQ formula. A tweaked verson is available in the Hashrocket tap.
29
+
30
+ ```sh
31
+ brew tap hashrocket/formulas
32
+ brew install hashrocket/formulas/zeromq
33
+ brew install zeromq
34
+ ```
35
+
36
+ ### Flatware
37
+
38
+ Add the runners you need to your Gemfile:
39
+
40
+ ```ruby
41
+ gem 'flatware-rspec' # one
42
+ gem 'flatware-cucumber' # or both
43
+ ```
44
+
45
+ then run
46
+
47
+ ```sh
48
+ bundle install
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ### Cucumber
54
+
55
+ To run your entire suite with the default cucumber options, add the `flatware-cucumber` gem and just:
56
+
57
+ ```sh
58
+ $ flatware cucumber
59
+ ```
60
+
61
+ ### RSpec
62
+
63
+ To run your entire suite with the default rspec options add the `flatware-rspec` gem and just:
64
+
65
+ ```sh
66
+ $ flatware rspec
67
+ ```
68
+
69
+ ### Options
70
+
71
+ If you'd like to limit the number of forked workers, you can pass the 'w' flag:
72
+
73
+ ```sh
74
+ $ flatware -w 3
75
+ ```
76
+
77
+ You can also pass most cucumber/rspec options to Flatware. For example, to run only
78
+ features that are not tagged 'javascript', you can:
79
+
80
+ ```sh
81
+ $ flatware cucumber -t ~@javascript
82
+ ```
83
+
84
+ Additionally, for either cucumber or rspec you can specify a directory:
85
+
86
+ ```sh
87
+ $ flatware rspec spec/features
88
+ ```
89
+
90
+ ## Typical Usage in a Rails App
91
+
92
+ Add the following to your `config/database.yml`:
93
+
94
+ ```yml
95
+ test:
96
+ database: foo_test
97
+ ```
98
+
99
+ becomes:
100
+
101
+ ```yml
102
+ test:
103
+ database: foo_test<%=ENV['TEST_ENV_NUMBER']%>
104
+ ```
105
+
106
+ Run the following:
107
+
108
+ ```sh
109
+ $ rake db:setup # if not already done
110
+ $ flatware fan rake db:test:prepare
111
+ ```
112
+
113
+ Now you are ready to rock:
114
+
115
+ ```sh
116
+ $ flatware rspec && flatware cucumber
117
+ ```
118
+
119
+ ## Planned Features
120
+
121
+ * Use heuristics to run your slowest tests first
122
+
123
+ ## Design Goals
124
+
125
+ ### Maintainable
126
+
127
+ * Fully test at an integration level. Don't be afraid to change the code. If you
128
+ break it you'll know.
129
+ * Couple as loosely as possible, and only to the most stable/public bits of
130
+ Cucumber and RSpec.
131
+
132
+ ### Minimal
133
+
134
+ * Projects define their own preparation scripts
135
+ * Only distribute to local cores (for now)
136
+
137
+ ### Robust
138
+
139
+ * Depend on a dedicated messaging library
140
+ * Be accountable for completed work; provide progress report regardless of
141
+ completing the suite.
142
+
143
+ ## Tinkering
144
+
145
+ Flatware integration tests use [aruba][a]. In order to get a demo cucumber project you
146
+ can add the `@no-clobber` tag to `features/flatware.feature` and run the test
147
+ with `cucumber features/flatware.feature`. Now you should have a `./tmp/aruba`
148
+ directory. CD there and `flatware` will be in your path so you can tinker away.
149
+
150
+ ## How it works
151
+
152
+ Flatware relies on a message passing system to enable concurrency.
153
+ The main process declares a worker for each cpu in the computer. Each
154
+ worker forks from the main process and is then assigned a portion of the
155
+ test suite. As the worker runs the test suite it sends progress
156
+ messages to the main process. These messages are collected and when
157
+ the last worker is finished the main process provides a report on the
158
+ collected progress messages.
159
+
160
+ ## Resources
161
+
162
+ To learn more about the messaging system that Flatware uses, take a look at the
163
+ [excellent ZeroMQ guide][z].
164
+
165
+ [z]: http://zguide.zeromq.org/page:all
166
+ [a]: https://github.com/cucumber/aruba
167
+
168
+ ## Contributing to Flatware
169
+
170
+ Do whatever you want. I'd love to help make sure Flatware meets your needs.
171
+
172
+ ## About
173
+
174
+ [![Hashrocket logo](https://hashrocket.com/hashrocket_logo.svg)](https://hashrocket.com)
175
+
176
+ Flatware is supported by the team at [Hashrocket](https://hashrocket.com), a multidisciplinary design & development consultancy. If you'd like to [work with us](https://hashrocket.com/contact-us/hire-us) or [join our team](https://hashrocket.com/contact-us/jobs), don't hesitate to get in touch.
@@ -0,0 +1 @@
1
+ require 'flatware/cucumber'
@@ -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,22 @@
1
+ require 'flatware/cli'
2
+
3
+ module Flatware
4
+ class CLI
5
+ worker_option
6
+ method_option 'dispatch-endpoint', type: :string, default: 'ipc://dispatch'
7
+ method_option 'sink-endpoint', type: :string, default: 'ipc://task'
8
+ desc "cucumber [FLATWARE_OPTS] [CUCUMBER_ARGS]", "parallelizes cucumber with custom arguments"
9
+ def cucumber(*args)
10
+ config = Cucumber.configure args
11
+
12
+ unless config.jobs.any?
13
+ puts "Please create some feature files in the #{config.feature_dir} directory."
14
+ exit 1
15
+ end
16
+
17
+ Flatware.verbose = options[:log]
18
+ Worker.spawn count: workers, runner: Cucumber, dispatch: options['dispatch-endpoint'], sink: options['sink-endpoint']
19
+ start_sink jobs: config.jobs, workers: workers, formatter: Flatware::Cucumber::Formatters::Console.new($stdout, $stderr)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,66 @@
1
+ require 'cucumber'
2
+ require 'flatware/sink'
3
+ require 'ostruct'
4
+ module Flatware
5
+ module Cucumber
6
+ class Formatter
7
+ Checkpoint = Struct.new :steps, :scenarios do
8
+ def failures?
9
+ scenarios.any? &:failed?
10
+ end
11
+ end
12
+
13
+ Scenario = Struct.new :name, :file_colon_line, :status do
14
+ def failed?
15
+ status == :failed
16
+ end
17
+
18
+ def failed_outside_step?
19
+ false
20
+ end
21
+ end
22
+
23
+ def initialize(config)
24
+ config.on_event :after_test_case, &method(:on_after_test_case)
25
+ config.on_event :after_test_step, &method(:on_after_test_step)
26
+ config.on_event :finished_testing, &method(:on_finished_testing)
27
+ config.on_event :step_match, &method(:on_step_match)
28
+ reset
29
+ end
30
+
31
+ private
32
+
33
+ def reset
34
+ @steps = []
35
+ @scenarios = []
36
+ @matched_steps = Set.new
37
+ end
38
+
39
+ attr_reader :steps, :scenarios, :matched_steps
40
+
41
+ def on_after_test_case(event)
42
+ scenarios << Scenario.new(event.test_case.name, event.test_case.location.to_s, event.result.to_sym)
43
+ end
44
+
45
+ def on_step_match(event)
46
+ @matched_steps << event.step_match.location.to_s
47
+ end
48
+
49
+ def on_after_test_step(event)
50
+ if really_a_step?(event.test_step) or event.result.undefined?
51
+ steps << StepResult.new(event.result.to_sym, event.result.failed? && event.result.exception)
52
+ Sink::client.progress Result.new event.result.to_sym
53
+ end
54
+ end
55
+
56
+ def on_finished_testing(*)
57
+ Sink::client.checkpoint Checkpoint.new steps, scenarios
58
+ reset
59
+ end
60
+
61
+ def really_a_step?(step)
62
+ matched_steps.include?(step.action_location.to_s)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,48 @@
1
+ require 'flatware/cucumber/formatters/console/summary'
2
+ require 'cucumber/formatter/console'
3
+
4
+ module Flatware::Cucumber::Formatters
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(checkpoints)
30
+ steps = checkpoints.flat_map(&:steps)
31
+ scenarios = checkpoints.flat_map(&:scenarios)
32
+ Summary.new(steps, scenarios, out).summarize
33
+ end
34
+
35
+ def summarize_remaining(remaining_jobs)
36
+ out.puts
37
+ out.puts "The following features have not been run:"
38
+ for job in remaining_jobs
39
+ out.puts job.id
40
+ end
41
+ end
42
+
43
+ private
44
+ def format(status)
45
+ format_string FORMATS[status], status
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,66 @@
1
+ require 'cucumber/formatter/console'
2
+ require 'flatware/cucumber/formatters/console'
3
+ require 'flatware/cucumber/checkpoint'
4
+
5
+ module Flatware::Cucumber::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
@@ -0,0 +1,27 @@
1
+ module Flatware
2
+ module Cucumber
3
+ class Result
4
+ attr_reader :progress, :worker
5
+
6
+ def initialize(progress)
7
+ @progress = progress
8
+ @worker = ENV['TEST_ENV_NUMBER'].to_i
9
+ end
10
+
11
+ class << self
12
+ def step(*args)
13
+ step = StepResult.new *args
14
+ new step.progress, [step]
15
+ end
16
+
17
+ def status(status)
18
+ new status
19
+ end
20
+
21
+ def background(status, exception)
22
+ new '', [StepResult.new(status, exception)]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'cucumber'
2
+
3
+ module Flatware
4
+ module Cucumber
5
+ class Runtime < ::Cucumber::Runtime
6
+
7
+ attr_accessor :configuration, :loader
8
+ attr_reader :out, :err
9
+ attr_reader :visitor
10
+
11
+ def initialize(out=StringIO.new, err=out)
12
+ @out, @err = out, err
13
+ super(default_configuration)
14
+ load_step_definitions
15
+ @results = Results.new(configuration)
16
+ end
17
+
18
+ def default_configuration
19
+ config = ::Cucumber::Cli::Configuration.new
20
+ config.parse! []
21
+ config
22
+ end
23
+
24
+ def run(feature_files=[], options=[])
25
+ @loader = nil
26
+ options = Array(feature_files) + %w[--format Flatware::Cucumber::Formatter] + options
27
+
28
+ configure(::Cucumber::Cli::Main.new(options, out, err).configuration)
29
+
30
+ self.visitor = configuration.build_tree_walker(self)
31
+ visitor.visit_features(features)
32
+ results
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ require 'flatware/serialized_exception'
2
+ module Flatware
3
+ module Cucumber
4
+ class ScenarioResult
5
+ attr_reader :status, :file_colon_line, :name
6
+ def initialize(status, file_colon_line, name, e)
7
+ @status = status
8
+ @file_colon_line = file_colon_line
9
+ @name = name
10
+ @exception = SerializedException.new(e.class, e.message, e.backtrace) if e
11
+ @failed_outside_step = false
12
+ end
13
+
14
+ def passed?
15
+ status == :passed
16
+ end
17
+
18
+ def failed?
19
+ status == :failed
20
+ end
21
+
22
+ def failed_outside_step!(file_colon_line)
23
+ @failed_outside_step = file_colon_line
24
+ end
25
+
26
+ def failed_outside_step?
27
+ !!@failed_outside_step
28
+ end
29
+
30
+ def exception
31
+ @exception.tap do |e|
32
+ e.backtrace = e.backtrace.grep(Regexp.new(Dir.pwd)).map { |line| line[Dir.pwd.size..-1] }
33
+ e.backtrace = e.backtrace + [@failed_outside_step] if failed_outside_step?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ require 'flatware/serialized_exception'
2
+ module Flatware
3
+ module Cucumber
4
+ class StepResult
5
+ attr_reader :status, :exception
6
+
7
+ def initialize(status, exception)
8
+ @status, @exception = status, (serialized(exception) if exception)
9
+ end
10
+
11
+ def passed?
12
+ status == :passed
13
+ end
14
+
15
+ def failed?
16
+ status == :failed
17
+ end
18
+
19
+ def progress
20
+ Cucumber::ProgressString.format(status)
21
+ end
22
+
23
+ private
24
+ def serialized(e)
25
+ e.backtrace and e.backtrace.unshift e.backtrace.shift.sub(Dir.pwd, '.')
26
+ SerializedException.new(e.class, e.message, e.backtrace)
27
+ end
28
+ end
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flatware-cucumber
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Dunn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: flatware
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: cucumber
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ description: A distributed cucumber runner
42
+ email: brian@hashrocket.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files:
46
+ - LICENSE.txt
47
+ - README.md
48
+ files:
49
+ - LICENSE.txt
50
+ - README.md
51
+ - lib/flatware-cucumber.rb
52
+ - lib/flatware/cucumber/checkpoint.rb
53
+ - lib/flatware/cucumber/cli.rb
54
+ - lib/flatware/cucumber/formatter.rb
55
+ - lib/flatware/cucumber/formatters/console.rb
56
+ - lib/flatware/cucumber/formatters/console/summary.rb
57
+ - lib/flatware/cucumber/result.rb
58
+ - lib/flatware/cucumber/runtime.rb
59
+ - lib/flatware/cucumber/scenario_result.rb
60
+ - lib/flatware/cucumber/step_result.rb
61
+ homepage: http://github.com/briandunn/flatware
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.1'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.6.8
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: A distributed cucumber runner
85
+ test_files: []