flatware 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Brian Dunn
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,79 @@
1
+ = Flatware
2
+
3
+ Flatware is a a distributed cucumber runner.
4
+
5
+ == Requirements
6
+
7
+ * ZeroMQ ~> 2.1
8
+
9
+ == Installation
10
+
11
+ Add this to your Gemfile:
12
+
13
+ gem 'flatware'
14
+
15
+ and `bundle install`.
16
+
17
+ == Usage
18
+
19
+ To run your entire suite with the default cucumber options, just:
20
+
21
+ flatware
22
+
23
+ If you'd like to limit the number of forked workers, you can pass the 'w' flag:
24
+
25
+ flatware -w 3
26
+
27
+ You can also pass most cucumber options to flatware. For example, to run only features that are not tagged 'javascript', you can:
28
+
29
+ flatware cucumber -t ~@javascript
30
+
31
+
32
+ == Planned Features
33
+
34
+ * Reliable enough to use as part of your Continuous Integration system
35
+ * Always accounts for every feature you ask it to run
36
+ * Use heuristics to run your slowest tests first
37
+ * speak Cucumber's DRB protocol; if you know how to use Spork you know how to use Flatware
38
+
39
+ == Design Goals
40
+
41
+ === Maintainable
42
+
43
+ * Fully test at an integration level. Don't be afraid to change the code. If you break it you'll know.
44
+ * Couple as loosely as possible, and only to the most stable/public bits of Cucumber.
45
+
46
+ === Minimal
47
+
48
+ * Projects define their own preperation scripts
49
+ * Only distribute to local cores (for now)
50
+ * Only handle cucumber
51
+
52
+ === Robust
53
+
54
+ * Depend on a dedicated messaging library
55
+ * Be acountable for completed work; provide progress report regardless of completing the suite.
56
+
57
+ == Tinkering
58
+
59
+ Flatware is tested with aruba. In order to get a demo cucumber project you can add the `@no-clobber` tag to `features/flatware.feature` and run the test with `cucumber features/flatware.feature`. Now you should have a `./tmp/aruba` directory. CD there and you can run the `worker` and `dispatcher` binaries, like `../../bin/worker` and `../../bin/dispatcher`
60
+
61
+ == Resources
62
+
63
+ The excellent ZeroMQ guide: http://zguide.zeromq.org/page:all
64
+
65
+ == Contributing to Flatware
66
+
67
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
68
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
69
+ * Fork the project
70
+ * Start a feature/bugfix branch
71
+ * Commit and push until you are happy with your contribution
72
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
73
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
74
+
75
+ == Copyright
76
+
77
+ Copyright (c) 2011-2012 Brian Dunn. See LICENSE.txt for
78
+ further details.
79
+
@@ -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
@@ -0,0 +1,50 @@
1
+ require 'zmq'
2
+
3
+ module Flatware
4
+ autoload :CLI, 'flatware/cli'
5
+ autoload :Cucumber, 'flatware/cucumber'
6
+ autoload :Dispatcher, 'flatware/dispatcher'
7
+ autoload :Fireable, 'flatware/fireable'
8
+ autoload :Result, 'flatware/result'
9
+ autoload :ScenarioResult, 'flatware/scenario_result'
10
+ autoload :Sink, 'flatware/sink'
11
+ autoload :StepResult, 'flatware/step_result'
12
+ autoload :Summary, 'flatware/summary'
13
+ autoload :Worker, 'flatware/worker'
14
+
15
+ Job = Struct.new :id, :args
16
+
17
+ extend self
18
+ def socket(*args)
19
+ context.socket(*args).tap do |socket|
20
+ sockets.push socket
21
+ end
22
+ end
23
+
24
+ def close
25
+ sockets.each &:close
26
+ context.close
27
+ @context = nil
28
+ end
29
+
30
+ def log(*message)
31
+ if verbose?
32
+ $stderr.print "#{$$} "
33
+ $stderr.puts *message
34
+ end
35
+ end
36
+
37
+ attr_writer :verbose
38
+ def verbose?
39
+ !!@verbose
40
+ end
41
+
42
+ private
43
+ def context
44
+ @context ||= ZMQ::Context.new
45
+ end
46
+
47
+ def sockets
48
+ @sockets ||= []
49
+ end
50
+ end
@@ -0,0 +1,71 @@
1
+ require 'thor'
2
+ module Flatware
3
+ class CLI < Thor
4
+
5
+ def self.processors
6
+ @processors ||= `hostinfo`.match(/^(?<processors>\d+) processors are logically available\.$/)[:processors].to_i
7
+ end
8
+
9
+ def self.worker_option
10
+ method_option :workers, aliases: "-w", type: :numeric, default: processors, desc: "Number of concurent processes to run"
11
+ end
12
+
13
+ class_option :log, aliases: "-l", type: :boolean, desc: "Print debug messages to $stderr"
14
+
15
+ default_task :default
16
+ worker_option
17
+ desc "default", "parallelizes cucumber with default arguments"
18
+ def default
19
+ cucumber
20
+ end
21
+
22
+ worker_option
23
+ desc "cucumber [CUCUMBER_ARGS]", "parallelizes cucumber with custom arguments"
24
+ def cucumber(*)
25
+ Flatware.verbose = options[:log]
26
+ Worker.spawn workers
27
+ jobs = Cucumber.extract_jobs_from_args args
28
+ fork do
29
+ log "dispatch"
30
+ $0 = 'flatware dispatcher'
31
+ Dispatcher.start jobs
32
+ end
33
+ log "bossman"
34
+ $0 = 'flatware sink'
35
+ Sink.start_server jobs
36
+ Process.waitall
37
+ end
38
+
39
+ worker_option
40
+ desc "fan [COMMAND]", "executes the given job on all of the workers"
41
+ def fan(*command)
42
+ Flatware.verbose = options[:log]
43
+
44
+ command = command.join(" ")
45
+ puts "Running '#{command}' on #{workers} workers"
46
+
47
+ workers.times do |i|
48
+ fork do
49
+ exec({"TEST_ENV_NUMBER" => i.to_s}, command)
50
+ end
51
+ end
52
+ Process.waitall
53
+ end
54
+
55
+
56
+ desc "clear", "kills all flatware processes"
57
+ def clear
58
+ `ps -c -opid,command | grep flatware | cut -f 1 -d ' ' | xargs kill -6`
59
+ end
60
+
61
+ private
62
+
63
+ def log(*args)
64
+ Flatware.log(*args)
65
+ end
66
+
67
+ def workers
68
+ options[:workers]
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,38 @@
1
+ require 'cucumber'
2
+ require_relative 'cucumber/runtime'
3
+ module Flatware
4
+ module Cucumber
5
+ autoload :Formatter, 'flatware/cucumber/formatter'
6
+ autoload :ProgressString, 'flatware/cucumber/formatter'
7
+
8
+ FORMATS = {
9
+ :passed => '.',
10
+ :failed => 'F',
11
+ :undefined => 'U',
12
+ :pending => 'P',
13
+ :skipped => '-'
14
+ }
15
+
16
+ STATUSES = FORMATS.keys
17
+
18
+ extend self
19
+
20
+ attr_reader :jobs
21
+
22
+ def extract_jobs_from_args(args=[], out_stream=$stdout, error_stream=$stderr)
23
+ raw_args = args.dup
24
+ config = ::Cucumber::Cli::Configuration.new(out_stream, error_stream)
25
+ config.parse! args
26
+ options = raw_args - args
27
+ @jobs = config.feature_files.map { |file| Job.new file, options }
28
+ end
29
+
30
+ def run(feature_files=[], options=[])
31
+ runtime.run feature_files, options
32
+ end
33
+
34
+ def runtime
35
+ @runtime ||= Runtime.new
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,151 @@
1
+ require 'cucumber/formatter/console'
2
+ module Flatware
3
+ module Cucumber
4
+ class Formatter
5
+ def initialize(step_mother, *)
6
+ @step_mother = step_mother
7
+ end
8
+
9
+ def scenario_name(keyword, name, file_colon_line, source_indent)
10
+ @current_scenario = file_colon_line
11
+ end
12
+
13
+ def before_feature_element(feature_element)
14
+ case feature_element
15
+ when ::Cucumber::Ast::ScenarioOutline
16
+ @outline_steps = feature_element
17
+ end
18
+ end
19
+
20
+ def after_feature_element(feature_element)
21
+ @outline_steps = nil
22
+ end
23
+
24
+ def after_feature(*)
25
+ background_steps.each do |step|
26
+ Sink.push step
27
+ end unless current_scenario
28
+ end
29
+
30
+ def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
31
+ result = if status_only? background
32
+ background_steps << Result.background(status, exception)
33
+ Result.status status
34
+ else
35
+ Result.step status, exception, current_scenario
36
+ end
37
+
38
+ Sink.push result
39
+ end
40
+
41
+ def before_outline_table(outline_table)
42
+ @outline_table = outline_table
43
+ end
44
+
45
+ def after_outline_table(outline_table)
46
+ @outline_table = nil
47
+ end
48
+
49
+ def before_table_row(table_row)
50
+ if example_row? table_row
51
+ @step_collector = StepCollector.new(step_mother)
52
+ end
53
+ end
54
+
55
+ def after_table_row(table_row)
56
+ if example_row? table_row
57
+ step_collector.stop table_row
58
+ Sink.push Result.new step_collector.progress, step_collector.steps
59
+ end
60
+ end
61
+
62
+ def table_cell_value(_, status)
63
+ Sink.push Result.status status if example_cell? status
64
+ end
65
+
66
+ private
67
+
68
+ attr_reader :step_mother, :step_collector, :current_scenario
69
+
70
+ def background_steps
71
+ @background_steps ||= []
72
+ end
73
+
74
+ def status_only?(background)
75
+ scenario_outline? or (background and not current_scenario)
76
+ end
77
+
78
+ def scenario_outline?
79
+ !!@outline_steps
80
+ end
81
+
82
+ def example_row?(table_row)
83
+ outline_table? and not table_header_row? table_row
84
+ end
85
+
86
+ def example_cell?(status)
87
+ outline_table? and not table_header_cell? status
88
+ end
89
+
90
+ def table_header_cell?(status)
91
+ status == :skipped_param
92
+ end
93
+
94
+ def outline_table?
95
+ !!@outline_table
96
+ end
97
+
98
+ def table_header_row?(table_row)
99
+ table_row.failed?
100
+ rescue ::Cucumber::Ast::OutlineTable::ExampleRow::InvalidForHeaderRowError
101
+ true
102
+ else
103
+ false
104
+ end
105
+
106
+ class StepCollector
107
+ attr_reader :step_mother
108
+ def initialize(step_mother)
109
+ @step_mother = step_mother
110
+ snapshot_steps
111
+ end
112
+
113
+ def stop(table_row)
114
+ @scenario_id = extract_scenario(table_row)
115
+ @example_row_steps = step_mother.steps - ran_steps
116
+ end
117
+
118
+ def steps
119
+ example_row_steps or raise('stop collecting first')
120
+ example_row_steps.map do |step|
121
+ StepResult.new(step.status, step.exception, scenario_id)
122
+ end
123
+ end
124
+
125
+ def progress
126
+ ''
127
+ end
128
+
129
+ private
130
+
131
+ attr_reader :example_row_steps, :scenario_id, :ran_steps
132
+
133
+ def extract_scenario(table_row)
134
+ table_row.scenario_outline.file_colon_line(table_row.line)
135
+ end
136
+
137
+ def snapshot_steps
138
+ @ran_steps = step_mother.steps.dup
139
+ end
140
+ end
141
+ end
142
+
143
+ class ProgressString
144
+ extend ::Cucumber::Formatter::Console
145
+ def self.format(status)
146
+ return '' unless status
147
+ format_string FORMATS[status], status
148
+ end
149
+ end
150
+ end
151
+ 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
+ module Flatware
2
+ class Dispatcher
3
+ DISPATCH_PORT = 'ipc://dispatch'
4
+
5
+ def self.start(jobs=Cucumber.jobs)
6
+ new(jobs).dispatch!
7
+ end
8
+
9
+ def initialize(jobs)
10
+ @jobs = jobs
11
+ end
12
+
13
+ def dispatch!
14
+ return Flatware.close if jobs.empty?
15
+ fireable.until_fired dispatch do |request|
16
+ if job = jobs.pop
17
+ dispatch.send Marshal.dump job
18
+ else
19
+ dispatch.send 'seppuku'
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :jobs
27
+
28
+ def fireable
29
+ @fireable ||= Fireable.new
30
+ end
31
+
32
+ def dispatch
33
+ @dispatch ||= Flatware.socket(ZMQ::REP).tap do |socket|
34
+ socket.bind DISPATCH_PORT
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ module Flatware
2
+ class Fireable
3
+ def initialize
4
+ @die = Flatware.socket(ZMQ::SUB).tap do |die|
5
+ die.connect 'ipc://die'
6
+ die.setsockopt ZMQ::SUBSCRIBE, ''
7
+ end
8
+ end
9
+
10
+ attr_reader :die
11
+
12
+ def until_fired(sockets=[], &block)
13
+ while ready = ZMQ.select(Array(sockets) + [die])
14
+ messages = ready.flatten.compact.map(&:recv)
15
+ break if messages.include? 'seppuku'
16
+ messages.each &block
17
+ end
18
+ ensure
19
+ Flatware.close
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module Flatware
2
+ class Result
3
+ attr_reader :progress, :steps
4
+
5
+ def initialize(progress, steps=nil)
6
+ @progress, @steps = progress, steps || []
7
+ end
8
+
9
+ class << self
10
+ def step(*args)
11
+ step = StepResult.new *args
12
+ new step.progress, [step]
13
+ end
14
+
15
+ def status(status)
16
+ new Cucumber::ProgressString.format status
17
+ end
18
+
19
+ def background(status, exception)
20
+ new '', [StepResult.new(status, exception)]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Flatware
2
+ class ScenarioResult
3
+ attr_reader :id, :steps
4
+
5
+ def initialize(id, steps=[])
6
+ @id = id
7
+ @steps = steps
8
+ end
9
+
10
+ def status
11
+ first(:failed) || first(:undefined) || :passed
12
+ end
13
+
14
+ private
15
+
16
+ def first(status)
17
+ statuses.detect {|s| s == status}
18
+ end
19
+
20
+ def statuses
21
+ steps.map &:status
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,136 @@
1
+ require 'flatware'
2
+ require 'flatware/cucumber/formatter'
3
+ module Flatware
4
+ class Sink
5
+ class << self
6
+ def push(message)
7
+ client.push Marshal.dump message
8
+ end
9
+
10
+ def finished(job)
11
+ push job
12
+ end
13
+
14
+ def start_server(jobs=Cucumber.jobs, out_stream=$stdout, error_stream=$stderr)
15
+ Server.new(jobs, out_stream, error_stream).start
16
+ end
17
+
18
+ def client
19
+ @client ||= Client.new
20
+ end
21
+ end
22
+
23
+ class Server
24
+ def initialize(jobs, out, error)
25
+ @jobs, @out, @error = jobs, out, error
26
+ end
27
+
28
+ def start
29
+ trap 'INT' do
30
+ summarize
31
+ summarize_remaining
32
+ end
33
+
34
+ before_firing { listen }
35
+ Flatware.close
36
+ end
37
+
38
+ def listen
39
+ until done?
40
+ message = socket.recv
41
+ case (result = Marshal.load message)
42
+ when Result
43
+ print result.progress
44
+ steps.push *result.steps
45
+ when Job
46
+ completed_jobs << result
47
+ log "COMPLETED SCENARIO"
48
+ else
49
+ log "i don't know that message, bro."
50
+ end
51
+ end
52
+ summarize
53
+ rescue ZMQ::Error => e
54
+ raise unless e.message == "Interrupted system call"
55
+ end
56
+
57
+ private
58
+
59
+ attr_reader :out, :jobs
60
+
61
+ def print(*args)
62
+ out.print *args
63
+ end
64
+
65
+ def puts(*args)
66
+ out.puts *args
67
+ end
68
+
69
+ def summarize
70
+ Summary.new(steps, out).summarize
71
+ end
72
+
73
+ def summarize_remaining
74
+ return if remaining_work.empty?
75
+ puts
76
+ puts "The following features have not been run:"
77
+ for job in remaining_work
78
+ puts job.id
79
+ end
80
+ end
81
+
82
+ def log(*args)
83
+ Flatware.log *args
84
+ end
85
+
86
+ def before_firing(&block)
87
+ die = Flatware.socket(ZMQ::PUB).tap do |socket|
88
+ socket.bind 'ipc://die'
89
+ end
90
+ block.call
91
+ die.send 'seppuku'
92
+ end
93
+
94
+ def steps
95
+ @steps ||= []
96
+ end
97
+
98
+ def completed_jobs
99
+ @completed_jobs ||= []
100
+ end
101
+
102
+ def done?
103
+ log remaining_work
104
+ remaining_work.empty?
105
+ end
106
+
107
+ def remaining_work
108
+ jobs - completed_jobs
109
+ end
110
+
111
+ def fireable
112
+ @fireable ||= Fireable.new
113
+ end
114
+
115
+ def socket
116
+ @socket ||= Flatware.socket(ZMQ::PULL).tap do |socket|
117
+ socket.bind 'ipc://sink'
118
+ end
119
+ end
120
+ end
121
+
122
+ class Client
123
+ def push(message)
124
+ socket.send message
125
+ end
126
+
127
+ private
128
+
129
+ def socket
130
+ @socket ||= Flatware.socket(ZMQ::PUSH).tap do |socket|
131
+ socket.connect 'ipc://sink'
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,44 @@
1
+ module Flatware
2
+ class StepResult
3
+ attr_reader :status, :exception, :scenario_id
4
+
5
+ def initialize(status, exception, scenario_id=nil)
6
+ @status, @exception, @scenario_id = status, serialized(exception), scenario_id
7
+ end
8
+
9
+ def passed?
10
+ status == :passed
11
+ end
12
+
13
+ def failed?
14
+ status == :failed
15
+ end
16
+
17
+ def progress
18
+ Cucumber::ProgressString.format(status)
19
+ end
20
+
21
+ private
22
+ def serialized(e)
23
+ SerializedException.new(e.class, e.message, e.backtrace) if e
24
+ end
25
+
26
+ class SerializedException
27
+ attr_reader :class, :message, :backtrace
28
+ def initialize(klass, message, backtrace)
29
+ @class, @message, @backtrace = serialized(klass), message, backtrace
30
+ end
31
+
32
+ private
33
+ def serialized(klass)
34
+ SerializedClass.new(klass.to_s)
35
+ end
36
+ end
37
+
38
+ class SerializedClass
39
+ attr_reader :name
40
+ alias to_s name
41
+ def initialize(name); @name = name end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,61 @@
1
+ require 'cucumber/formatter/console'
2
+ module Flatware
3
+ class Summary
4
+ include ::Cucumber::Formatter::Console
5
+ attr_reader :io, :steps
6
+
7
+ def initialize(steps, io=StringIO.new)
8
+ @io = io
9
+ @steps = steps
10
+ end
11
+
12
+ def scenarios
13
+ @scenarios ||= scenario_steps.group_by(&:scenario_id).map do |scenario, steps|
14
+ ScenarioResult.new(scenario, steps)
15
+ end
16
+ end
17
+
18
+ def summarize
19
+ 2.times { io.puts }
20
+ print_steps :failed
21
+ print_counts 'scenario', scenarios
22
+ print_counts 'step', steps
23
+ end
24
+
25
+ private
26
+
27
+ def scenario_steps
28
+ steps.select &:scenario_id
29
+ end
30
+
31
+ def print_steps(status)
32
+ print_elements steps.select(&with_status(status)), status, 'steps'
33
+ end
34
+
35
+ def print_counts(label, collection)
36
+ io.puts pluralize(label, collection.size) + count_summary(collection)
37
+ end
38
+
39
+ def pluralize(word, number)
40
+ "#{number} #{number == 1 ? word : word + 's'}"
41
+ end
42
+
43
+ def with_status(status)
44
+ proc {|r| r.status == status}
45
+ end
46
+
47
+ def count_summary(results)
48
+ return "" unless results.any?
49
+ status_counts = Cucumber::STATUSES.map do |status|
50
+ count = results.select(&with_status(status)).size
51
+ format_string "#{count} #{status}", status if count > 0
52
+ end.compact.join ", "
53
+
54
+ " (#{status_counts})"
55
+ end
56
+
57
+ def count(status)
58
+ completed_scenarios.select {|scenario| scenario.status == status}.count
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,55 @@
1
+ require 'benchmark'
2
+ module Flatware
3
+ class Worker
4
+
5
+ def self.listen!
6
+ new.listen
7
+ end
8
+
9
+ def self.spawn(worker_count)
10
+ worker_count.times do |i|
11
+ fork do
12
+ $0 = "flatware worker #{i}"
13
+ ENV['TEST_ENV_NUMBER'] = i.to_s
14
+ listen!
15
+ end
16
+ end
17
+ end
18
+
19
+ def listen
20
+ time = Benchmark.realtime do
21
+ fireable
22
+ report_for_duty
23
+ fireable.until_fired task do |work|
24
+ job = Marshal.load work
25
+ log 'working!'
26
+ Cucumber.run job.id, job.args
27
+ Sink.finished job
28
+ report_for_duty
29
+ log 'waiting'
30
+ end
31
+ end
32
+ log time
33
+ end
34
+
35
+ private
36
+
37
+ def log(*args)
38
+ Flatware.log *args
39
+ end
40
+
41
+ def fireable
42
+ @fireable ||= Fireable.new
43
+ end
44
+
45
+ def task
46
+ @task ||= Flatware.socket(ZMQ::REQ).tap do |task|
47
+ task.connect Dispatcher::DISPATCH_PORT
48
+ end
49
+ end
50
+
51
+ def report_for_duty
52
+ task.send 'ready'
53
+ end
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flatware
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brian Dunn
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: zmq
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: thor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.15.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.15.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: cucumber
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: aruba
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: A distributed cucumber runner
111
+ email: brian@hashrocket.com
112
+ executables:
113
+ - flatware
114
+ extensions: []
115
+ extra_rdoc_files:
116
+ - LICENSE.txt
117
+ - README.rdoc
118
+ files:
119
+ - lib/flatware.rb
120
+ - lib/flatware/cli.rb
121
+ - lib/flatware/cucumber.rb
122
+ - lib/flatware/cucumber/formatter.rb
123
+ - lib/flatware/cucumber/runtime.rb
124
+ - lib/flatware/dispatcher.rb
125
+ - lib/flatware/fireable.rb
126
+ - lib/flatware/result.rb
127
+ - lib/flatware/scenario_result.rb
128
+ - lib/flatware/sink.rb
129
+ - lib/flatware/step_result.rb
130
+ - lib/flatware/summary.rb
131
+ - lib/flatware/worker.rb
132
+ - LICENSE.txt
133
+ - README.rdoc
134
+ - bin/flatware
135
+ homepage: http://github.com/briandunn/flatware
136
+ licenses:
137
+ - MIT
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 1.8.24
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: A distributed cucumber runner
160
+ test_files: []