flatware 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|