flatware 0.3.2 → 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 +4 -4
- data/README.md +83 -39
- data/lib/flatware-cucumber.rb +1 -0
- data/lib/flatware-rspec.rb +1 -0
- data/lib/flatware.rb +2 -10
- data/lib/flatware/broadcaster.rb +15 -0
- data/lib/flatware/cli.rb +10 -34
- data/lib/flatware/cucumber.rb +37 -12
- data/lib/flatware/cucumber/checkpoint.rb +28 -0
- data/lib/flatware/cucumber/cli.rb +22 -0
- data/lib/flatware/cucumber/formatter.rb +36 -84
- data/lib/flatware/{formatters/cucumber → cucumber/formatters}/console.rb +5 -3
- data/lib/flatware/{formatters/cucumber → cucumber/formatters}/console/summary.rb +3 -3
- data/lib/flatware/cucumber/result.rb +27 -0
- data/lib/flatware/cucumber/scenario_result.rb +38 -0
- data/lib/flatware/cucumber/step_result.rb +30 -0
- data/lib/flatware/poller.rb +2 -2
- data/lib/flatware/rspec.rb +28 -0
- data/lib/flatware/rspec/checkpoint.rb +29 -0
- data/lib/flatware/rspec/cli.rb +16 -0
- data/lib/flatware/rspec/example_notification.rb +21 -0
- data/lib/flatware/rspec/examples_notification.rb +24 -0
- data/lib/flatware/rspec/formatter.rb +50 -0
- data/lib/flatware/rspec/formatters/console.rb +33 -0
- data/lib/flatware/rspec/summary.rb +40 -0
- data/lib/flatware/serialized_exception.rb +12 -8
- data/lib/flatware/sink.rb +43 -33
- data/lib/flatware/socket.rb +89 -25
- data/lib/flatware/version.rb +1 -1
- data/lib/flatware/worker.rb +16 -9
- metadata +27 -55
- data/lib/flatware/checkpoint.rb +0 -28
- data/lib/flatware/checkpoint_handler.rb +0 -45
- data/lib/flatware/dispatcher.rb +0 -31
- data/lib/flatware/fireable.rb +0 -34
- data/lib/flatware/formatters.rb +0 -27
- data/lib/flatware/formatters/cucumber/http.rb +0 -83
- data/lib/flatware/result.rb +0 -25
- data/lib/flatware/scenario_decorator.rb +0 -22
- data/lib/flatware/scenario_result.rb +0 -36
- data/lib/flatware/step_result.rb +0 -27
@@ -0,0 +1,33 @@
|
|
1
|
+
module Flatware::RSpec::Formatters
|
2
|
+
class Console
|
3
|
+
attr_reader :formatter
|
4
|
+
|
5
|
+
def initialize(out, err)
|
6
|
+
::RSpec::configuration.tty = true
|
7
|
+
::RSpec::configuration.color = true
|
8
|
+
@formatter = ::RSpec::Core::Formatters::ProgressFormatter.new(out)
|
9
|
+
end
|
10
|
+
|
11
|
+
def progress(result)
|
12
|
+
formatter.send(message_for(result),nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def summarize(checkpoints)
|
16
|
+
result = checkpoints.reduce :+
|
17
|
+
if result
|
18
|
+
formatter.dump_failures result
|
19
|
+
formatter.dump_summary result.summary
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def message_for(result)
|
26
|
+
{
|
27
|
+
passed: :example_passed,
|
28
|
+
failed: :example_failed,
|
29
|
+
pending: :example_pending
|
30
|
+
}.fetch result.progress
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rspec/core/notifications'
|
2
|
+
module Flatware
|
3
|
+
module RSpec
|
4
|
+
Summary = Struct.new(:duration, :examples, :failed_examples, :pending_examples, :load_time)
|
5
|
+
|
6
|
+
class Example
|
7
|
+
attr_reader :location_rerun_argument, :full_description
|
8
|
+
def initialize(rspec_example)
|
9
|
+
@full_description = rspec_example.full_description
|
10
|
+
@location_rerun_argument = rspec_example.location_rerun_argument
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Summary
|
15
|
+
def +(other)
|
16
|
+
self.class.new duration + other.duration,
|
17
|
+
examples + other.examples,
|
18
|
+
failed_examples + other.failed_examples,
|
19
|
+
pending_examples + other.pending_examples,
|
20
|
+
load_time + other.load_time
|
21
|
+
end
|
22
|
+
|
23
|
+
def fully_formatted
|
24
|
+
::RSpec::Core::Notifications::SummaryNotification.new(duration, examples, failed_examples, pending_examples, load_time).fully_formatted
|
25
|
+
end
|
26
|
+
|
27
|
+
def failure_count
|
28
|
+
failed_examples.size
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.from_notification(summary)
|
32
|
+
serialized_examples = [summary.examples, summary.failed_examples, summary.pending_examples].map do |examples|
|
33
|
+
examples.map(&Example.method(:new))
|
34
|
+
end
|
35
|
+
|
36
|
+
new summary.duration, *serialized_examples, summary.load_time
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,20 +1,24 @@
|
|
1
1
|
module Flatware
|
2
2
|
class SerializedException
|
3
|
-
attr_reader :class, :message
|
3
|
+
attr_reader :class, :message, :cause
|
4
4
|
attr_accessor :backtrace
|
5
|
-
def initialize(klass, message, backtrace)
|
6
|
-
@class, @message, @backtrace = serialized(klass), message, backtrace
|
5
|
+
def initialize(klass, message, backtrace, cause='')
|
6
|
+
@class, @message, @backtrace, @cause = serialized(klass), message, backtrace, cause
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from(exception)
|
10
|
+
new exception.class, exception.message, exception.backtrace, exception.cause
|
7
11
|
end
|
8
12
|
|
9
13
|
private
|
10
14
|
def serialized(klass)
|
11
15
|
SerializedClass.new(klass.to_s)
|
12
16
|
end
|
17
|
+
end
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
+
class SerializedClass
|
20
|
+
attr_reader :name
|
21
|
+
alias to_s name
|
22
|
+
def initialize(name); @name = name end
|
19
23
|
end
|
20
24
|
end
|
data/lib/flatware/sink.rb
CHANGED
@@ -8,65 +8,70 @@ module Flatware
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class Server
|
11
|
-
attr_reader :
|
12
|
-
|
13
|
-
def initialize(jobs
|
14
|
-
@
|
15
|
-
|
16
|
-
@
|
17
|
-
@
|
11
|
+
attr_reader :sink, :dispatch, :poller, :workers, :checkpoints, :jobs, :formatter
|
12
|
+
|
13
|
+
def initialize(jobs:, formatter:, dispatch:, sink:, worker_count: 0)
|
14
|
+
@formatter = formatter
|
15
|
+
@jobs = group_jobs(jobs, worker_count)
|
16
|
+
@sink = Flatware.socket(ZMQ::PULL, bind: sink)
|
17
|
+
@dispatch = Flatware.socket(ZMQ::REP, bind: dispatch)
|
18
|
+
@poller = Poller.new(@sink, @dispatch)
|
19
|
+
@workers = Set.new(worker_count.times.to_a)
|
20
|
+
@checkpoints = []
|
18
21
|
end
|
19
22
|
|
20
23
|
def start
|
21
24
|
trap 'INT' do
|
22
|
-
|
25
|
+
puts "Interrupted!"
|
26
|
+
formatter.summarize checkpoints
|
23
27
|
summarize_remaining
|
28
|
+
puts "\n\nCleaning up. Please wait...\n"
|
29
|
+
Flatware.close!
|
30
|
+
Process.waitall
|
31
|
+
puts "thanks."
|
24
32
|
exit 1
|
25
33
|
end
|
26
|
-
|
27
|
-
Flatware::Fireable::bind
|
28
34
|
formatter.jobs jobs
|
29
|
-
listen
|
30
|
-
|
31
|
-
|
32
|
-
Flatware.close
|
33
|
-
end
|
34
|
-
|
35
|
-
def checkpoint_handler
|
36
|
-
@checkpoint_handler ||= CheckpointHandler.new(formatter, fail_fast?)
|
35
|
+
listen.tap do
|
36
|
+
Flatware.close
|
37
|
+
end
|
37
38
|
end
|
38
39
|
|
39
40
|
def listen
|
40
|
-
|
41
|
+
que = jobs.dup
|
42
|
+
poller.each do |socket|
|
41
43
|
message, content = socket.recv
|
44
|
+
|
42
45
|
case message
|
46
|
+
when :ready
|
47
|
+
workers << content
|
48
|
+
job = que.shift
|
49
|
+
if job and not done?
|
50
|
+
dispatch.send job
|
51
|
+
else
|
52
|
+
workers.delete content
|
53
|
+
dispatch.send 'seppuku'
|
54
|
+
end
|
43
55
|
when :checkpoint
|
44
|
-
|
56
|
+
checkpoints << content
|
45
57
|
when :finished
|
46
58
|
completed_jobs << content
|
47
59
|
formatter.finished content
|
48
60
|
else
|
49
61
|
formatter.send message, content
|
50
62
|
end
|
63
|
+
break if workers.empty? and done?
|
51
64
|
end
|
52
|
-
|
65
|
+
formatter.summarize(checkpoints)
|
53
66
|
!failures?
|
54
|
-
rescue Error => e
|
55
|
-
raise unless e.message == "Interrupted system call"
|
56
67
|
end
|
57
68
|
|
58
69
|
private
|
59
70
|
|
60
71
|
def failures?
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
def fail_fast?
|
65
|
-
@fail_fast
|
72
|
+
checkpoints.any?(&:failures?) || completed_jobs.any?(&:failed?)
|
66
73
|
end
|
67
74
|
|
68
|
-
attr_reader :jobs, :formatter
|
69
|
-
|
70
75
|
def summarize_remaining
|
71
76
|
return if remaining_work.empty?
|
72
77
|
formatter.summarize_remaining remaining_work
|
@@ -81,15 +86,20 @@ module Flatware
|
|
81
86
|
end
|
82
87
|
|
83
88
|
def done?
|
84
|
-
remaining_work.empty?
|
89
|
+
remaining_work.empty?
|
85
90
|
end
|
86
91
|
|
87
92
|
def remaining_work
|
88
93
|
jobs - completed_jobs
|
89
94
|
end
|
90
95
|
|
91
|
-
def
|
92
|
-
|
96
|
+
def group_jobs(jobs, worker_count)
|
97
|
+
return jobs unless worker_count > 1
|
98
|
+
jobs.group_by.with_index do |_,i|
|
99
|
+
i % worker_count
|
100
|
+
end.values.map do |jobs|
|
101
|
+
Job.new(jobs.map(&:id), jobs.first.args)
|
102
|
+
end
|
93
103
|
end
|
94
104
|
end
|
95
105
|
end
|
data/lib/flatware/socket.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'ffi-rzmq'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'logger'
|
2
4
|
|
3
5
|
module Flatware
|
4
6
|
Error = Class.new StandardError
|
@@ -14,20 +16,37 @@ module Flatware
|
|
14
16
|
|
15
17
|
extend self
|
16
18
|
|
19
|
+
def logger
|
20
|
+
@logger ||= Logger.new($stderr)
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger=(logger)
|
24
|
+
@logger = logger
|
25
|
+
end
|
26
|
+
|
17
27
|
def socket(*args)
|
18
28
|
context.socket(*args)
|
19
29
|
end
|
20
30
|
|
21
|
-
def close
|
22
|
-
|
31
|
+
def close(force: false)
|
32
|
+
@ignore_errors = true if force
|
33
|
+
context.close(force: force)
|
23
34
|
@context = nil
|
24
35
|
end
|
25
36
|
|
37
|
+
def close!
|
38
|
+
close force: true
|
39
|
+
end
|
40
|
+
|
41
|
+
def socket_error
|
42
|
+
raise(Error, ZMQ::Util.error_string, caller) unless @ignore_errors
|
43
|
+
end
|
44
|
+
|
26
45
|
def log(*message)
|
27
|
-
if
|
28
|
-
|
29
|
-
|
30
|
-
$
|
46
|
+
if Exception === message.first
|
47
|
+
logger.error message.first
|
48
|
+
elsif verbose?
|
49
|
+
logger.info ([$0] + message).join(' ')
|
31
50
|
end
|
32
51
|
message
|
33
52
|
end
|
@@ -38,7 +57,10 @@ module Flatware
|
|
38
57
|
end
|
39
58
|
|
40
59
|
def context
|
41
|
-
@context ||=
|
60
|
+
@context ||= begin
|
61
|
+
@ignore_errors = nil
|
62
|
+
Context.new
|
63
|
+
end
|
42
64
|
end
|
43
65
|
|
44
66
|
class Context
|
@@ -49,8 +71,8 @@ module Flatware
|
|
49
71
|
@sockets = []
|
50
72
|
end
|
51
73
|
|
52
|
-
def socket(
|
53
|
-
Socket.new(c.socket(
|
74
|
+
def socket(zmq_type, options={})
|
75
|
+
Socket.new(c.socket(zmq_type)).tap do |socket|
|
54
76
|
sockets.push socket
|
55
77
|
if port = options[:connect]
|
56
78
|
socket.connect port
|
@@ -61,26 +83,34 @@ module Flatware
|
|
61
83
|
end
|
62
84
|
end
|
63
85
|
|
64
|
-
def close
|
65
|
-
sockets.each
|
66
|
-
|
86
|
+
def close(force: false)
|
87
|
+
sockets.each do |socket|
|
88
|
+
socket.setsockopt ZMQ::LINGER, 0
|
89
|
+
end if force
|
90
|
+
sockets.each(&:close)
|
91
|
+
Flatware::socket_error unless c.terminate == 0
|
67
92
|
Flatware.log "terminated context"
|
68
93
|
end
|
69
94
|
end
|
70
95
|
|
71
96
|
class Socket
|
72
|
-
attr_reader :
|
97
|
+
attr_reader :socket
|
98
|
+
|
73
99
|
def initialize(socket)
|
74
|
-
@
|
100
|
+
@socket = socket
|
75
101
|
end
|
76
102
|
|
77
103
|
def setsockopt(*args)
|
78
|
-
|
104
|
+
socket.setsockopt(*args)
|
105
|
+
end
|
106
|
+
|
107
|
+
def name
|
108
|
+
socket.name
|
79
109
|
end
|
80
110
|
|
81
111
|
def send(message)
|
82
|
-
result =
|
83
|
-
|
112
|
+
result = socket.send_string(Marshal.dump(message))
|
113
|
+
Flatware::socket_error if result == -1
|
84
114
|
Flatware.log "#@type #@port send #{message}"
|
85
115
|
message
|
86
116
|
end
|
@@ -88,28 +118,62 @@ module Flatware
|
|
88
118
|
def connect(port)
|
89
119
|
@type = 'connected'
|
90
120
|
@port = port
|
91
|
-
|
121
|
+
Flatware::socket_error unless socket.connect(port) == 0
|
92
122
|
Flatware.log "connect #@port"
|
93
123
|
end
|
94
124
|
|
125
|
+
def monitor
|
126
|
+
name = "inproc://monitor#{SecureRandom.hex(10)}"
|
127
|
+
LibZMQ.zmq_socket_monitor(socket.socket, name, ZMQ::EVENT_ALL)
|
128
|
+
Monitor.new(name)
|
129
|
+
end
|
130
|
+
|
131
|
+
class Monitor
|
132
|
+
def initialize(port)
|
133
|
+
@socket = Flatware.socket ZMQ::PAIR
|
134
|
+
@socket.connect port
|
135
|
+
end
|
136
|
+
|
137
|
+
def recv
|
138
|
+
bytes = @socket.recv marshal: false
|
139
|
+
data = LibZMQ::EventData.new FFI::MemoryPointer.from_string bytes
|
140
|
+
event[data.event]
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def event
|
146
|
+
ZMQ.constants.select do |c|
|
147
|
+
c.to_s =~ /^EVENT/
|
148
|
+
end.map do |s|
|
149
|
+
{s => ZMQ.const_get(s)}
|
150
|
+
end.reduce(:merge).invert
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
95
154
|
def bind(port)
|
96
155
|
@type = 'bound'
|
97
156
|
@port = port
|
98
|
-
|
157
|
+
Flatware::socket_error unless socket.bind(port) == 0
|
99
158
|
Flatware.log "bind #@port"
|
100
159
|
end
|
101
160
|
|
102
161
|
def close
|
103
|
-
|
104
|
-
raise(Error, ZMQ::Util.error_string, caller) unless s.close == 0
|
162
|
+
Flatware::socket_error unless socket.close == 0
|
105
163
|
Flatware.log "close #@type #@port"
|
106
164
|
end
|
107
165
|
|
108
|
-
def recv
|
166
|
+
def recv(block: true, marshal: true)
|
109
167
|
message = ''
|
110
|
-
|
111
|
-
|
112
|
-
|
168
|
+
if block
|
169
|
+
result = socket.recv_string(message)
|
170
|
+
Flatware::socket_error if result == -1
|
171
|
+
else
|
172
|
+
socket.recv_string(message, ZMQ::NOBLOCK)
|
173
|
+
end
|
174
|
+
if message != '' and marshal
|
175
|
+
message = Marshal.load(message)
|
176
|
+
end
|
113
177
|
Flatware.log "#@type #@port recv #{message}"
|
114
178
|
message
|
115
179
|
end
|
data/lib/flatware/version.rb
CHANGED
data/lib/flatware/worker.rb
CHANGED
@@ -6,37 +6,44 @@ module Flatware
|
|
6
6
|
def initialize(id, runner, dispatch_endpoint, sink_endpoint)
|
7
7
|
@id = id
|
8
8
|
@runner = runner
|
9
|
-
@fireable = Fireable.new
|
10
9
|
@sink = Sink::Client.new sink_endpoint
|
11
10
|
@task = Flatware.socket ZMQ::REQ, connect: dispatch_endpoint
|
12
11
|
end
|
13
12
|
|
14
|
-
def self.spawn(
|
15
|
-
|
13
|
+
def self.spawn(count:, runner:, dispatch:, sink:)
|
14
|
+
count.times do |i|
|
16
15
|
fork do
|
17
16
|
$0 = "flatware worker #{i}"
|
18
17
|
ENV['TEST_ENV_NUMBER'] = i.to_s
|
19
|
-
new(i, runner,
|
18
|
+
new(i, runner, dispatch, sink).listen
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
23
|
def listen
|
24
|
+
trap 'INT' do
|
25
|
+
Flatware.close!
|
26
|
+
@want_to_quit = true
|
27
|
+
exit(1)
|
28
|
+
end
|
29
|
+
|
25
30
|
Sink.client = sink
|
26
31
|
report_for_duty
|
27
|
-
|
32
|
+
loop do
|
33
|
+
job = task.recv
|
34
|
+
break if job == 'seppuku' or @want_to_quit
|
28
35
|
job.worker = id
|
29
36
|
sink.started job
|
30
37
|
begin
|
31
38
|
runner.run job.id, job.args
|
32
|
-
rescue
|
39
|
+
rescue => e
|
40
|
+
Flatware.log e
|
33
41
|
job.failed = true
|
34
|
-
sink.finished job
|
35
|
-
raise
|
36
42
|
end
|
37
43
|
sink.finished job
|
38
44
|
report_for_duty
|
39
45
|
end
|
46
|
+
Flatware.close unless @want_to_quit
|
40
47
|
end
|
41
48
|
|
42
49
|
private
|
@@ -44,7 +51,7 @@ module Flatware
|
|
44
51
|
attr_reader :fireable, :task, :sink, :runner
|
45
52
|
|
46
53
|
def report_for_duty
|
47
|
-
task.send
|
54
|
+
task.send [:ready, id]
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|