flatware 0.0.4 → 0.1.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.
data/README.rdoc CHANGED
@@ -1,6 +1,9 @@
1
1
  = Flatware
2
2
 
3
- Flatware is a a distributed cucumber runner.
3
+ {<img src="https://travis-ci.org/briandunn/flatware.png" />}[https://travis-ci.org/briandunn/flatware]
4
+ {<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/briandunn/flatware]
5
+
6
+ Flatware is a distributed cucumber runner.
4
7
 
5
8
  == Requirements
6
9
 
data/lib/flatware.rb CHANGED
@@ -1,29 +1,42 @@
1
- require 'zmq'
1
+ require 'forwardable'
2
+ require 'ffi-rzmq'
2
3
 
3
4
  module Flatware
4
5
  autoload :CLI, 'flatware/cli'
5
6
  autoload :Cucumber, 'flatware/cucumber'
6
7
  autoload :Dispatcher, 'flatware/dispatcher'
7
8
  autoload :Fireable, 'flatware/fireable'
9
+ autoload :ProcessorInfo, 'flatware/processor_info'
8
10
  autoload :Result, 'flatware/result'
9
11
  autoload :ScenarioResult, 'flatware/scenario_result'
10
12
  autoload :Sink, 'flatware/sink'
11
13
  autoload :StepResult, 'flatware/step_result'
12
14
  autoload :Summary, 'flatware/summary'
13
15
  autoload :Worker, 'flatware/worker'
16
+ autoload :ScenarioDecorator, 'flatware/scenario_decorator'
17
+
18
+ Error = Class.new StandardError
14
19
 
15
20
  Job = Struct.new :id, :args
16
21
 
17
22
  extend self
18
- def socket(*args)
19
- context.socket(*args).tap do |socket|
23
+ def socket(type, options={})
24
+ Socket.new(context.socket(type)).tap do |socket|
20
25
  sockets.push socket
26
+ if port = options[:connect]
27
+ socket.connect port
28
+ end
29
+ if port = options[:bind]
30
+ socket.bind port
31
+ end
32
+ #FIXME: figure out how to do this without waiting
33
+ sleep 0.05
21
34
  end
22
35
  end
23
36
 
24
37
  def close
25
38
  sockets.each &:close
26
- context.close
39
+ context.terminate
27
40
  @context = nil
28
41
  end
29
42
 
@@ -47,4 +60,26 @@ module Flatware
47
60
  def sockets
48
61
  @sockets ||= []
49
62
  end
63
+
64
+ Socket = Struct.new :s do
65
+ extend Forwardable
66
+ def_delegators :s, :bind, :connect, :setsockopt
67
+ def send(message)
68
+ result = s.send_string(Marshal.dump(message))
69
+ raise Error, ZMQ::Util.error_string, caller unless result == 0
70
+ message
71
+ end
72
+
73
+ def close
74
+ s.setsockopt(ZMQ::LINGER, 1)
75
+ s.close
76
+ end
77
+
78
+ def recv
79
+ message = ''
80
+ result = s.recv_string(message)
81
+ raise Error, ZMQ::Util.error_string, caller unless result == 0
82
+ Marshal.load message
83
+ end
84
+ end
50
85
  end
@@ -15,8 +15,9 @@ module Flatware
15
15
 
16
16
  def serialize_scenarios(scenarios)
17
17
  scenarios.map do |scenario|
18
- ScenarioResult.new scenario.status
18
+ ScenarioResult.new scenario.status, scenario.file_colon_line, scenario.name
19
19
  end
20
20
  end
21
+
21
22
  end
22
23
  end
data/lib/flatware/cli.rb CHANGED
@@ -3,7 +3,7 @@ module Flatware
3
3
  class CLI < Thor
4
4
 
5
5
  def self.processors
6
- @processors ||= `hostinfo`.match(/^(?<processors>\d+) processors are logically available\.$/)[:processors].to_i
6
+ @processors ||= ProcessorInfo.count
7
7
  end
8
8
 
9
9
  def self.worker_option
@@ -1,5 +1,6 @@
1
1
  require 'cucumber/formatter/console'
2
2
  require 'flatware/checkpoint'
3
+ require 'flatware/scenario_decorator'
3
4
  module Flatware
4
5
  module Cucumber
5
6
  class Formatter
@@ -34,7 +35,7 @@ module Flatware
34
35
  end
35
36
 
36
37
  def checkpoint
37
- Checkpoint.new(step_mother.steps - @ran_steps, step_mother.scenarios - @ran_scenarios).tap do
38
+ Checkpoint.new(step_mother.steps - @ran_steps, decorate_scenarios(step_mother.scenarios - @ran_scenarios)).tap do
38
39
  snapshot
39
40
  end
40
41
  end
@@ -45,6 +46,10 @@ module Flatware
45
46
  @ran_steps = step_mother.steps.dup
46
47
  @ran_scenarios = step_mother.scenarios.dup
47
48
  end
49
+
50
+ def decorate_scenarios(scenarios)
51
+ scenarios.map { |scenario| ScenarioDecorator.new(scenario) }
52
+ end
48
53
  end
49
54
  end
50
55
 
@@ -1,6 +1,6 @@
1
1
  module Flatware
2
2
  class Dispatcher
3
- DISPATCH_PORT = 'ipc://dispatch'
3
+ PORT = 'ipc://dispatch'
4
4
 
5
5
  def self.start(jobs=Cucumber.jobs)
6
6
  new(jobs).dispatch!
@@ -11,10 +11,10 @@ module Flatware
11
11
  end
12
12
 
13
13
  def dispatch!
14
- return Flatware.close if jobs.empty?
14
+ return if jobs.empty?
15
15
  fireable.until_fired dispatch do |request|
16
16
  if job = jobs.pop
17
- dispatch.send Marshal.dump job
17
+ dispatch.send job
18
18
  else
19
19
  dispatch.send 'seppuku'
20
20
  end
@@ -30,9 +30,7 @@ module Flatware
30
30
  end
31
31
 
32
32
  def dispatch
33
- @dispatch ||= Flatware.socket(ZMQ::REP).tap do |socket|
34
- socket.bind DISPATCH_PORT
35
- end
33
+ @dispatch ||= Flatware.socket ZMQ::REP, bind: PORT
36
34
  end
37
35
  end
38
36
  end
@@ -1,22 +1,50 @@
1
1
  module Flatware
2
2
  class Fireable
3
+ PORT = 'ipc://die'
4
+
5
+ def self.bind
6
+ @kill = Flatware.socket(ZMQ::PUB, bind: PORT)
7
+ end
8
+
9
+ def self.kill
10
+ @kill.send 'seppuku'
11
+ end
12
+
3
13
  def initialize
4
- @die = Flatware.socket(ZMQ::SUB).tap do |die|
5
- die.connect 'ipc://die'
14
+ @die = Flatware.socket(ZMQ::SUB, connect: PORT).tap do |die|
6
15
  die.setsockopt ZMQ::SUBSCRIBE, ''
7
16
  end
8
17
  end
9
18
 
10
19
  attr_reader :die
11
20
 
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
21
+ def until_fired(socket, &block)
22
+ poller = Poller.new socket, die
23
+ poller.each do |message|
24
+ break if message == 'seppuku'
25
+ block.call message
17
26
  end
18
27
  ensure
19
28
  Flatware.close
20
29
  end
21
30
  end
31
+
32
+ class Poller
33
+ attr_reader :sockets
34
+ def initialize(*sockets)
35
+ @sockets = sockets
36
+ end
37
+
38
+ def each(&block)
39
+ poller = ZMQ::Poller.new
40
+ for socket in sockets
41
+ poller.register_readable socket.s
42
+ end
43
+ while poller.poll > 0
44
+ poller.readables.each do |s|
45
+ block.call Socket.new(s).recv
46
+ end
47
+ end
48
+ end
49
+ end
22
50
  end
@@ -0,0 +1,20 @@
1
+ module Flatware
2
+ class ProcessorInfo
3
+ def count
4
+ case operating_system
5
+ when 'Darwin'
6
+ `hostinfo`.match(/^(?<processors>\d+) processors are logically available\.$/)[:processors].to_i
7
+ when 'Linux'
8
+ `grep --count '^processor' /proc/cpuinfo`.to_i
9
+ end
10
+ end
11
+
12
+ def operating_system
13
+ `uname`.chomp
14
+ end
15
+
16
+ def self.count
17
+ new.count
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Flatware
2
+ class ScenarioDecorator
3
+ attr_reader :status
4
+
5
+ def initialize(scenario)
6
+ @scenario, @status = scenario, scenario.status
7
+ @scenario = scenario.scenario_outline if example_row?
8
+ end
9
+
10
+ def name
11
+ @scenario.name
12
+ end
13
+
14
+ def file_colon_line
15
+ @scenario.file_colon_line
16
+ end
17
+
18
+ private
19
+
20
+ def example_row?
21
+ @scenario.respond_to? :scenario_outline
22
+ end
23
+ end
24
+ end
@@ -1,8 +1,10 @@
1
1
  module Flatware
2
2
  class ScenarioResult
3
- attr_reader :status
4
- def initialize(status)
3
+ attr_reader :status, :file_colon_line, :name
4
+ def initialize(status, file_colon_line, name)
5
5
  @status = status
6
+ @file_colon_line = file_colon_line
7
+ @name = name
6
8
  end
7
9
 
8
10
  def passed?
data/lib/flatware/sink.rb CHANGED
@@ -2,9 +2,10 @@ require 'flatware'
2
2
  require 'flatware/cucumber/formatter'
3
3
  module Flatware
4
4
  class Sink
5
+ PORT = 'ipc://sink'
5
6
  class << self
6
7
  def push(message)
7
- client.push Marshal.dump message
8
+ client.push message
8
9
  end
9
10
 
10
11
  def finished(job)
@@ -29,6 +30,7 @@ module Flatware
29
30
  trap 'INT' do
30
31
  summarize
31
32
  summarize_remaining
33
+ exit 1
32
34
  end
33
35
 
34
36
  before_firing { listen }
@@ -38,7 +40,7 @@ module Flatware
38
40
  def listen
39
41
  until done?
40
42
  message = socket.recv
41
- case (result = Marshal.load message)
43
+ case (result = message)
42
44
  when Result
43
45
  print result.progress
44
46
  when Checkpoint
@@ -47,11 +49,11 @@ module Flatware
47
49
  completed_jobs << result
48
50
  log "COMPLETED SCENARIO"
49
51
  else
50
- log "i don't know that message, bro."
52
+ log "i don't know that message, bro.", message
51
53
  end
52
54
  end
53
55
  summarize
54
- rescue ZMQ::Error => e
56
+ rescue Error => e
55
57
  raise unless e.message == "Interrupted system call"
56
58
  end
57
59
 
@@ -87,11 +89,9 @@ module Flatware
87
89
  end
88
90
 
89
91
  def before_firing(&block)
90
- die = Flatware.socket(ZMQ::PUB).tap do |socket|
91
- socket.bind 'ipc://die'
92
- end
92
+ Flatware::Fireable::bind
93
93
  block.call
94
- die.send 'seppuku'
94
+ Flatware::Fireable::kill
95
95
  end
96
96
 
97
97
  def checkpoints
@@ -116,9 +116,7 @@ module Flatware
116
116
  end
117
117
 
118
118
  def socket
119
- @socket ||= Flatware.socket(ZMQ::PULL).tap do |socket|
120
- socket.bind 'ipc://sink'
121
- end
119
+ @socket ||= Flatware.socket(ZMQ::PULL, bind: PORT)
122
120
  end
123
121
  end
124
122
 
@@ -130,9 +128,7 @@ module Flatware
130
128
  private
131
129
 
132
130
  def socket
133
- @socket ||= Flatware.socket(ZMQ::PUSH).tap do |socket|
134
- socket.connect 'ipc://sink'
135
- end
131
+ @socket ||= Flatware.socket(ZMQ::PUSH, connect: PORT)
136
132
  end
137
133
  end
138
134
  end
@@ -14,12 +14,23 @@ module Flatware
14
14
  def summarize
15
15
  2.times { io.puts }
16
16
  print_steps :failed
17
+ print_failed_scenarios scenarios
17
18
  print_counts 'scenario', scenarios
18
19
  print_counts 'step', steps
19
20
  end
20
21
 
21
22
  private
22
23
 
24
+ def print_failed_scenarios(scenarios)
25
+ return unless scenarios.any? &with_status(:failed)
26
+
27
+ io.puts format_string "Failing Scenarios:", :failed
28
+ scenarios.select(&with_status(:failed)).sort_by(&:file_colon_line).each do |scenario|
29
+ io.puts format_string(scenario.file_colon_line, :failed) + format_string(" # Scenario: " + scenario.name, :comment)
30
+ end
31
+ io.puts
32
+ end
33
+
23
34
  def print_steps(status)
24
35
  print_elements steps.select(&with_status(status)), status, 'steps'
25
36
  end
@@ -1,3 +1,3 @@
1
1
  module Flatware
2
- VERSION = '0.0.4'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -20,8 +20,7 @@ module Flatware
20
20
  time = Benchmark.realtime do
21
21
  fireable
22
22
  report_for_duty
23
- fireable.until_fired task do |work|
24
- job = Marshal.load work
23
+ fireable.until_fired task do |job|
25
24
  log 'working!'
26
25
  Cucumber.run job.id, job.args
27
26
  Sink.finished job
@@ -43,9 +42,7 @@ module Flatware
43
42
  end
44
43
 
45
44
  def task
46
- @task ||= Flatware.socket(ZMQ::REQ).tap do |task|
47
- task.connect Dispatcher::DISPATCH_PORT
48
- end
45
+ @task ||= Flatware.socket ZMQ::REQ, connect: Dispatcher::PORT
49
46
  end
50
47
 
51
48
  def report_for_duty
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flatware
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-24 00:00:00.000000000 Z
12
+ date: 2013-03-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: zmq
15
+ name: ffi-rzmq
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 1.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ! '>='
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: '0'
29
+ version: 1.0.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: thor
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -64,49 +64,49 @@ dependencies:
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  none: false
66
66
  requirements:
67
- - - ! '>='
67
+ - - ~>
68
68
  - !ruby/object:Gem::Version
69
- version: '0'
69
+ version: 0.5.1
70
70
  type: :development
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
- - - ! '>='
75
+ - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: '0'
77
+ version: 0.5.1
78
78
  - !ruby/object:Gem::Dependency
79
79
  name: rake
80
80
  requirement: !ruby/object:Gem::Requirement
81
81
  none: false
82
82
  requirements:
83
- - - ! '>='
83
+ - - ~>
84
84
  - !ruby/object:Gem::Version
85
- version: '0'
85
+ version: 10.0.3
86
86
  type: :development
87
87
  prerelease: false
88
88
  version_requirements: !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
91
- - - ! '>='
91
+ - - ~>
92
92
  - !ruby/object:Gem::Version
93
- version: '0'
93
+ version: 10.0.3
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: rspec
96
96
  requirement: !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
- - - ! '>='
99
+ - - ~>
100
100
  - !ruby/object:Gem::Version
101
- version: '0'
101
+ version: 2.13.0
102
102
  type: :development
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
- - - ! '>='
107
+ - - ~>
108
108
  - !ruby/object:Gem::Version
109
- version: '0'
109
+ version: 2.13.0
110
110
  description: A distributed cucumber runner
111
111
  email: brian@hashrocket.com
112
112
  executables:
@@ -124,7 +124,9 @@ files:
124
124
  - lib/flatware/cucumber/runtime.rb
125
125
  - lib/flatware/dispatcher.rb
126
126
  - lib/flatware/fireable.rb
127
+ - lib/flatware/processor_info.rb
127
128
  - lib/flatware/result.rb
129
+ - lib/flatware/scenario_decorator.rb
128
130
  - lib/flatware/scenario_result.rb
129
131
  - lib/flatware/sink.rb
130
132
  - lib/flatware/step_result.rb
@@ -155,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
157
  version: '0'
156
158
  requirements: []
157
159
  rubyforge_project:
158
- rubygems_version: 1.8.24
160
+ rubygems_version: 1.8.25
159
161
  signing_key:
160
162
  specification_version: 3
161
163
  summary: A distributed cucumber runner