flatware 0.2.0 → 0.3.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 +7 -0
- data/lib/flatware.rb +4 -73
- data/lib/flatware/checkpoint.rb +1 -1
- data/lib/flatware/checkpoint_handler.rb +25 -7
- data/lib/flatware/cli.rb +7 -7
- data/lib/flatware/cucumber.rb +1 -13
- data/lib/flatware/cucumber/formatter.rb +62 -12
- data/lib/flatware/dispatcher.rb +10 -13
- data/lib/flatware/fireable.rb +4 -20
- data/lib/flatware/formatters.rb +26 -0
- data/lib/flatware/formatters/console.rb +48 -0
- data/lib/flatware/formatters/console/summary.rb +67 -0
- data/lib/flatware/formatters/http.rb +83 -0
- data/lib/flatware/pids.rb +25 -0
- data/lib/flatware/poller.rb +35 -0
- data/lib/flatware/processor_info.rb +4 -0
- data/lib/flatware/result.rb +5 -4
- data/lib/flatware/scenario_decorator.rb +10 -12
- data/lib/flatware/scenario_result.rb +19 -1
- data/lib/flatware/serialized_exception.rb +20 -0
- data/lib/flatware/sink.rb +29 -40
- data/lib/flatware/socket.rb +116 -0
- data/lib/flatware/step_result.rb +1 -18
- data/lib/flatware/version.rb +1 -1
- data/lib/flatware/worker.rb +17 -25
- metadata +17 -24
- data/lib/flatware/summary.rb +0 -60
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'cucumber/formatter/console'
|
2
|
+
require 'flatware/formatters/console'
|
3
|
+
require 'flatware/checkpoint'
|
4
|
+
module Flatware
|
5
|
+
module 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
|
67
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Flatware
|
5
|
+
module Formatters
|
6
|
+
class Http
|
7
|
+
attr_reader :out, :client, :server_pid
|
8
|
+
def initialize(out, err)
|
9
|
+
@out = out
|
10
|
+
@client = Client.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def jobs(jobs)
|
14
|
+
client.send_message [:jobs, jobs.map {|job| {id: job.id}}]
|
15
|
+
end
|
16
|
+
|
17
|
+
def started(job)
|
18
|
+
client.send_message [:started, job: job.id, worker: job.worker]
|
19
|
+
end
|
20
|
+
|
21
|
+
def finished(job)
|
22
|
+
client.send_message [:finished, job: job.id, worker: job.worker]
|
23
|
+
end
|
24
|
+
|
25
|
+
def progress(result)
|
26
|
+
client.send_message [:progress, status: result.progress, worker: result.worker]
|
27
|
+
end
|
28
|
+
|
29
|
+
def summarize(steps, scenarios)
|
30
|
+
client.send_message [
|
31
|
+
:summarize, {
|
32
|
+
steps: steps.map(&method(:step_as_json)),
|
33
|
+
scenarios: scenarios.map(&method(:scenario_as_json))
|
34
|
+
}
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def step_as_json(step)
|
41
|
+
{ status: step.status }.tap do |h|
|
42
|
+
h.merge(exception: {
|
43
|
+
class: step.exception.class,
|
44
|
+
message: step.exception.message,
|
45
|
+
backtrace: step.exception.backtrace
|
46
|
+
}) if step.exception
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def scenario_as_json(scenario)
|
51
|
+
{
|
52
|
+
status: scenario.status,
|
53
|
+
file_colon_line: scenario.file_colon_line,
|
54
|
+
name: scenario.name
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
class Client
|
59
|
+
attr_reader :uri
|
60
|
+
include Net
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
@uri = URI ENV['FLATWARE_URL']
|
64
|
+
end
|
65
|
+
|
66
|
+
def send_message(message)
|
67
|
+
send_request uri.path, JSON.dump(message)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def send_request(path, body=nil)
|
73
|
+
req = HTTP::Post.new path
|
74
|
+
req.body = body
|
75
|
+
res = HTTP.start uri.hostname, uri.port do |http|
|
76
|
+
http.request req
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'flatware/processor_info'
|
2
|
+
module Flatware
|
3
|
+
extend self
|
4
|
+
# All the pids of all the processes called flatware on this machine
|
5
|
+
def pids
|
6
|
+
pids_command.map do |row|
|
7
|
+
row =~ /(\d+).*flatware/ and $1.to_i
|
8
|
+
end.compact
|
9
|
+
end
|
10
|
+
|
11
|
+
def pids_command
|
12
|
+
case ProcessorInfo.operating_system
|
13
|
+
when 'Darwin'
|
14
|
+
`ps -c -opid,pgid,command`
|
15
|
+
when 'Linux'
|
16
|
+
`ps -opid,pgid,command`
|
17
|
+
end.split("\n")[1..-1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def pids_of_group(group_pid)
|
21
|
+
pids_command.map(&:split).map do |pid, pgid, _|
|
22
|
+
pid.to_i if pgid.to_i == group_pid
|
23
|
+
end.compact
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Flatware
|
2
|
+
class Poller
|
3
|
+
attr_reader :sockets, :zmq_poller
|
4
|
+
def initialize(*sockets)
|
5
|
+
@sockets = sockets
|
6
|
+
@zmq_poller = ZMQ::Poller.new
|
7
|
+
register_sockets
|
8
|
+
end
|
9
|
+
|
10
|
+
def each(&block)
|
11
|
+
while (result = zmq_poller.poll) != 0
|
12
|
+
raise Error, ZMQ::Util.error_string, caller if result == -1
|
13
|
+
for socket in zmq_poller.readables.map &find_wrapped_socket
|
14
|
+
yield socket
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def find_wrapped_socket
|
22
|
+
->(s) do
|
23
|
+
sockets.find do |socket|
|
24
|
+
socket.s == s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def register_sockets
|
30
|
+
sockets.each do |socket|
|
31
|
+
zmq_poller.register_readable socket.s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/flatware/result.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Flatware
|
2
2
|
class Result
|
3
|
-
attr_reader :progress, :
|
3
|
+
attr_reader :progress, :worker
|
4
4
|
|
5
|
-
def initialize(progress
|
6
|
-
@progress
|
5
|
+
def initialize(progress)
|
6
|
+
@progress = progress
|
7
|
+
@worker = ENV['TEST_ENV_NUMBER'].to_i
|
7
8
|
end
|
8
9
|
|
9
10
|
class << self
|
@@ -13,7 +14,7 @@ module Flatware
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def status(status)
|
16
|
-
new
|
17
|
+
new status
|
17
18
|
end
|
18
19
|
|
19
20
|
def background(status, exception)
|
@@ -1,24 +1,22 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
module Flatware
|
2
3
|
class ScenarioDecorator
|
3
|
-
|
4
|
+
extend Forwardable
|
5
|
+
def_delegators :scenario, :name, :file_colon_line
|
4
6
|
|
5
|
-
|
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
|
7
|
+
attr_reader :status, :exception
|
13
8
|
|
14
|
-
def
|
15
|
-
@scenario.
|
9
|
+
def initialize(scenario)
|
10
|
+
@scenario, @status, @exception = scenario, scenario.status, scenario.exception
|
11
|
+
@scenario, @exception = scenario.scenario_outline, scenario.exception if example_row?
|
16
12
|
end
|
17
13
|
|
18
14
|
private
|
19
15
|
|
16
|
+
attr_reader :scenario
|
17
|
+
|
20
18
|
def example_row?
|
21
|
-
|
19
|
+
scenario.respond_to? :scenario_outline
|
22
20
|
end
|
23
21
|
end
|
24
22
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
+
require 'flatware/serialized_exception'
|
1
2
|
module Flatware
|
2
3
|
class ScenarioResult
|
3
4
|
attr_reader :status, :file_colon_line, :name
|
4
|
-
def initialize(status, file_colon_line, name)
|
5
|
+
def initialize(status, file_colon_line, name, e)
|
5
6
|
@status = status
|
6
7
|
@file_colon_line = file_colon_line
|
7
8
|
@name = name
|
9
|
+
@exception = SerializedException.new(e.class, e.message, e.backtrace) if e
|
10
|
+
@failed_outside_step = false
|
8
11
|
end
|
9
12
|
|
10
13
|
def passed?
|
@@ -14,5 +17,20 @@ module Flatware
|
|
14
17
|
def failed?
|
15
18
|
status == :failed
|
16
19
|
end
|
20
|
+
|
21
|
+
def failed_outside_step!(file_colon_line)
|
22
|
+
@failed_outside_step = file_colon_line
|
23
|
+
end
|
24
|
+
|
25
|
+
def failed_outside_step?
|
26
|
+
!!@failed_outside_step
|
27
|
+
end
|
28
|
+
|
29
|
+
def exception
|
30
|
+
@exception.tap do |e|
|
31
|
+
e.backtrace = e.backtrace.grep(Regexp.new(Dir.pwd)).map { |line| line[Dir.pwd.size..-1] }
|
32
|
+
e.backtrace = e.backtrace + [@failed_outside_step] if failed_outside_step?
|
33
|
+
end
|
34
|
+
end
|
17
35
|
end
|
18
36
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Flatware
|
2
|
+
class SerializedException
|
3
|
+
attr_reader :class, :message
|
4
|
+
attr_accessor :backtrace
|
5
|
+
def initialize(klass, message, backtrace)
|
6
|
+
@class, @message, @backtrace = serialized(klass), message, backtrace
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def serialized(klass)
|
11
|
+
SerializedClass.new(klass.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
class SerializedClass
|
15
|
+
attr_reader :name
|
16
|
+
alias to_s name
|
17
|
+
def initialize(name); @name = name end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/flatware/sink.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'flatware'
|
2
|
-
require 'flatware/cucumber/formatter'
|
3
2
|
module Flatware
|
4
3
|
class Sink
|
5
4
|
PORT = 'ipc://sink'
|
@@ -8,22 +7,28 @@ module Flatware
|
|
8
7
|
client.push message
|
9
8
|
end
|
10
9
|
|
11
|
-
def finished(job)
|
12
|
-
push job
|
13
|
-
end
|
14
|
-
|
15
10
|
def start_server(*args)
|
16
11
|
Server.new(*args).start
|
17
12
|
end
|
18
13
|
|
14
|
+
private
|
15
|
+
|
19
16
|
def client
|
20
17
|
@client ||= Client.new
|
21
18
|
end
|
22
19
|
end
|
23
20
|
|
21
|
+
%w[finished started progress checkpoint].each do |message|
|
22
|
+
define_singleton_method message do |content|
|
23
|
+
push [message.to_sym, content]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
24
27
|
class Server
|
25
|
-
def initialize(jobs,
|
26
|
-
@jobs, @
|
28
|
+
def initialize(jobs, formatter, options={})
|
29
|
+
@jobs, @formatter = jobs, formatter
|
30
|
+
options = {fail_fast: false}.merge options
|
31
|
+
@fail_fast = options[:fail_fast]
|
27
32
|
end
|
28
33
|
|
29
34
|
def start
|
@@ -33,30 +38,33 @@ module Flatware
|
|
33
38
|
exit 1
|
34
39
|
end
|
35
40
|
|
36
|
-
|
41
|
+
Flatware::Fireable::bind
|
42
|
+
formatter.jobs jobs
|
43
|
+
listen
|
44
|
+
ensure
|
45
|
+
Flatware::Fireable::kill
|
37
46
|
Flatware.close
|
38
47
|
end
|
39
48
|
|
40
49
|
def checkpoint_handler
|
41
|
-
@checkpoint_handler ||= CheckpointHandler.new(
|
50
|
+
@checkpoint_handler ||= CheckpointHandler.new(formatter, fail_fast?)
|
42
51
|
end
|
43
52
|
|
44
53
|
def listen
|
45
54
|
until done?
|
46
|
-
|
47
|
-
case
|
48
|
-
when
|
49
|
-
|
50
|
-
when
|
51
|
-
|
52
|
-
|
53
|
-
completed_jobs << result
|
54
|
-
log "COMPLETED SCENARIO"
|
55
|
+
message, content = socket.recv
|
56
|
+
case message
|
57
|
+
when :checkpoint
|
58
|
+
checkpoint_handler.handle! content
|
59
|
+
when :finished
|
60
|
+
completed_jobs << content
|
61
|
+
formatter.finished content
|
55
62
|
else
|
56
|
-
|
63
|
+
formatter.send message, content
|
57
64
|
end
|
58
65
|
end
|
59
66
|
checkpoint_handler.summarize
|
67
|
+
!checkpoint_handler.had_failures?
|
60
68
|
rescue Error => e
|
61
69
|
raise unless e.message == "Interrupted system call"
|
62
70
|
end
|
@@ -67,41 +75,22 @@ module Flatware
|
|
67
75
|
@fail_fast
|
68
76
|
end
|
69
77
|
|
70
|
-
attr_reader :
|
71
|
-
|
72
|
-
def print(*args)
|
73
|
-
out.print *args
|
74
|
-
end
|
75
|
-
|
76
|
-
def puts(*args)
|
77
|
-
out.puts *args
|
78
|
-
end
|
78
|
+
attr_reader :jobs, :formatter
|
79
79
|
|
80
80
|
def summarize_remaining
|
81
81
|
return if remaining_work.empty?
|
82
|
-
|
83
|
-
puts "The following features have not been run:"
|
84
|
-
for job in remaining_work
|
85
|
-
puts job.id
|
86
|
-
end
|
82
|
+
formatter.summarize_remaining remaining_work
|
87
83
|
end
|
88
84
|
|
89
85
|
def log(*args)
|
90
86
|
Flatware.log *args
|
91
87
|
end
|
92
88
|
|
93
|
-
def before_firing(&block)
|
94
|
-
Flatware::Fireable::bind
|
95
|
-
block.call
|
96
|
-
Flatware::Fireable::kill
|
97
|
-
end
|
98
|
-
|
99
89
|
def completed_jobs
|
100
90
|
@completed_jobs ||= []
|
101
91
|
end
|
102
92
|
|
103
93
|
def done?
|
104
|
-
log remaining_work
|
105
94
|
remaining_work.empty? || checkpoint_handler.done?
|
106
95
|
end
|
107
96
|
|