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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 004764c1a65d325a871a4e5dc10959436dbe7b11
|
4
|
+
data.tar.gz: 6fde0c535fa7868237c65bc066d449db2fe506ed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ccdef8b34f2425936014ad0fbb9a53bad94c29df3c72f770e46aae3d75cafcd9659e8c1533624783667c4238f1a1a0fedaaf82ce2595f3d0bed409ec7c542ad2
|
7
|
+
data.tar.gz: 55b5196a2d5a08231d5440bc683c249b3508356666f719bfa566302b558978e5c3c5fdeae35e6c801800913de0c8d084818ceffe7db29c43dae2a8aa97519f19
|
data/lib/flatware.rb
CHANGED
@@ -1,86 +1,17 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'ffi-rzmq'
|
3
|
-
|
4
1
|
module Flatware
|
2
|
+
require 'flatware/checkpoint'
|
5
3
|
require 'flatware/checkpoint_handler'
|
6
4
|
require 'flatware/processor_info'
|
7
5
|
require 'flatware/cli'
|
8
6
|
require 'flatware/cucumber'
|
9
7
|
require 'flatware/dispatcher'
|
10
8
|
require 'flatware/fireable'
|
9
|
+
require 'flatware/formatters'
|
11
10
|
require 'flatware/result'
|
11
|
+
require 'flatware/scenario_decorator'
|
12
12
|
require 'flatware/scenario_result'
|
13
13
|
require 'flatware/sink'
|
14
|
+
require 'flatware/socket'
|
14
15
|
require 'flatware/step_result'
|
15
|
-
require 'flatware/summary'
|
16
16
|
require 'flatware/worker'
|
17
|
-
require 'flatware/scenario_decorator'
|
18
|
-
|
19
|
-
Error = Class.new StandardError
|
20
|
-
|
21
|
-
Job = Struct.new :id, :args
|
22
|
-
|
23
|
-
extend self
|
24
|
-
def socket(type, options={})
|
25
|
-
Socket.new(context.socket(type)).tap do |socket|
|
26
|
-
sockets.push socket
|
27
|
-
if port = options[:connect]
|
28
|
-
socket.connect port
|
29
|
-
end
|
30
|
-
if port = options[:bind]
|
31
|
-
socket.bind port
|
32
|
-
end
|
33
|
-
#FIXME: figure out how to do this without waiting
|
34
|
-
sleep 0.05
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def close
|
39
|
-
sockets.each &:close
|
40
|
-
context.terminate
|
41
|
-
@context = nil
|
42
|
-
end
|
43
|
-
|
44
|
-
def log(*message)
|
45
|
-
if verbose?
|
46
|
-
$stderr.print "#{$$} "
|
47
|
-
$stderr.puts *message
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
attr_writer :verbose
|
52
|
-
def verbose?
|
53
|
-
!!@verbose
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
def context
|
58
|
-
@context ||= ZMQ::Context.new
|
59
|
-
end
|
60
|
-
|
61
|
-
def sockets
|
62
|
-
@sockets ||= []
|
63
|
-
end
|
64
|
-
|
65
|
-
Socket = Struct.new :s do
|
66
|
-
extend Forwardable
|
67
|
-
def_delegators :s, :bind, :connect, :setsockopt
|
68
|
-
def send(message)
|
69
|
-
result = s.send_string(Marshal.dump(message))
|
70
|
-
raise Error, ZMQ::Util.error_string, caller if result == -1
|
71
|
-
message
|
72
|
-
end
|
73
|
-
|
74
|
-
def close
|
75
|
-
s.setsockopt(ZMQ::LINGER, 1)
|
76
|
-
s.close
|
77
|
-
end
|
78
|
-
|
79
|
-
def recv
|
80
|
-
message = ''
|
81
|
-
result = s.recv_string(message)
|
82
|
-
raise Error, ZMQ::Util.error_string, caller if result == -1
|
83
|
-
Marshal.load message
|
84
|
-
end
|
85
|
-
end
|
86
17
|
end
|
data/lib/flatware/checkpoint.rb
CHANGED
@@ -20,7 +20,7 @@ module Flatware
|
|
20
20
|
|
21
21
|
def serialize_scenarios(scenarios)
|
22
22
|
scenarios.map do |scenario|
|
23
|
-
ScenarioResult.new scenario.status, scenario.file_colon_line, scenario.name
|
23
|
+
ScenarioResult.new scenario.status, scenario.file_colon_line, scenario.name, scenario.exception
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -1,14 +1,16 @@
|
|
1
1
|
module Flatware
|
2
2
|
class CheckpointHandler
|
3
|
-
|
3
|
+
attr_reader :formatter, :checkpoints
|
4
|
+
|
5
|
+
def initialize(formatter, fails_fast)
|
4
6
|
@fail_fast = fails_fast
|
5
|
-
@
|
7
|
+
@formatter = formatter
|
6
8
|
@checkpoints = []
|
7
9
|
end
|
8
10
|
|
9
11
|
def handle!(checkpoint)
|
10
|
-
|
11
|
-
if checkpoint.failures? &&
|
12
|
+
checkpoints << checkpoint
|
13
|
+
if checkpoint.failures? && fail_fast?
|
12
14
|
Fireable::kill # Killing everybody
|
13
15
|
@done = true
|
14
16
|
end
|
@@ -18,10 +20,26 @@ module Flatware
|
|
18
20
|
@done
|
19
21
|
end
|
20
22
|
|
23
|
+
def fail_fast?
|
24
|
+
@fail_fast
|
25
|
+
end
|
26
|
+
|
21
27
|
def summarize
|
22
|
-
|
23
|
-
|
24
|
-
|
28
|
+
formatter.summarize(steps, scenarios)
|
29
|
+
end
|
30
|
+
|
31
|
+
def had_failures?
|
32
|
+
checkpoints.any? &:failures?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def steps
|
38
|
+
checkpoints.map(&:steps).flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
def scenarios
|
42
|
+
checkpoints.map(&:scenarios).flatten
|
25
43
|
end
|
26
44
|
end
|
27
45
|
end
|
data/lib/flatware/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'thor'
|
2
|
+
require 'flatware/pids'
|
2
3
|
module Flatware
|
3
4
|
class CLI < Thor
|
4
5
|
|
@@ -21,22 +22,23 @@ module Flatware
|
|
21
22
|
|
22
23
|
worker_option
|
23
24
|
method_option 'fail-fast', type: :boolean, default: false, desc: "Abort the run on first failure"
|
25
|
+
method_option 'formatters', aliases: "-f", type: :array, default: %w[console], desc: "The formatters to use for output"
|
24
26
|
desc "[FLATWARE_OPTS] cucumber [CUCUMBER_ARGS]", "parallelizes cucumber with custom arguments"
|
25
27
|
def cucumber(*)
|
28
|
+
Process.setpgrp
|
26
29
|
Flatware.verbose = options[:log]
|
27
|
-
Worker.spawn workers
|
28
30
|
log "flatware options:", options
|
29
31
|
log "cucumber options:", cucumber_args
|
30
32
|
jobs = Cucumber.extract_jobs_from_args cucumber_args
|
33
|
+
Worker.spawn workers
|
31
34
|
fork do
|
32
|
-
log "dispatch"
|
33
35
|
$0 = 'flatware dispatcher'
|
34
36
|
Dispatcher.start jobs
|
35
37
|
end
|
36
|
-
log "bossman"
|
37
38
|
$0 = 'flatware sink'
|
38
|
-
Sink.start_server jobs,
|
39
|
+
passed = Sink.start_server jobs, Formatters.load_by_name(options['formatters']), fail_fast: options['fail-fast']
|
39
40
|
Process.waitall
|
41
|
+
exit passed ? 0 : 1
|
40
42
|
end
|
41
43
|
|
42
44
|
worker_option
|
@@ -58,9 +60,7 @@ module Flatware
|
|
58
60
|
|
59
61
|
desc "clear", "kills all flatware processes"
|
60
62
|
def clear
|
61
|
-
|
62
|
-
row =~ /(\d+).*flatware/ and $1.to_i
|
63
|
-
end.compact.each do |pid|
|
63
|
+
(Flatware.pids - [$$]).each do |pid|
|
64
64
|
Process.kill 6, pid
|
65
65
|
end
|
66
66
|
end
|
data/lib/flatware/cucumber.rb
CHANGED
@@ -1,19 +1,7 @@
|
|
1
1
|
require 'cucumber'
|
2
|
-
|
2
|
+
require 'flatware/cucumber/runtime'
|
3
3
|
module Flatware
|
4
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
5
|
|
18
6
|
extend self
|
19
7
|
|
@@ -1,15 +1,26 @@
|
|
1
|
-
require 'cucumber/formatter/console'
|
2
1
|
require 'flatware/checkpoint'
|
3
2
|
require 'flatware/scenario_decorator'
|
3
|
+
require 'flatware/sink'
|
4
|
+
require 'ostruct'
|
4
5
|
module Flatware
|
5
6
|
module Cucumber
|
6
7
|
class Formatter
|
8
|
+
|
7
9
|
def initialize(step_mother, *)
|
8
10
|
@collector = Collector.new step_mother
|
11
|
+
@scenarios = []
|
12
|
+
@in_a_step = false
|
9
13
|
end
|
10
14
|
|
11
15
|
def after_features(*)
|
12
|
-
|
16
|
+
checkpoint = collector.checkpoint
|
17
|
+
scenarios.select(&:exception).map(&:file_colon_line).each do |file_colon_line|
|
18
|
+
scenario = checkpoint.scenarios.detect do |scenario|
|
19
|
+
scenario.file_colon_line == file_colon_line
|
20
|
+
end
|
21
|
+
scenario.failed_outside_step!(file_colon_line) if scenario
|
22
|
+
end
|
23
|
+
Sink.checkpoint checkpoint
|
13
24
|
end
|
14
25
|
|
15
26
|
def after_step_result(_, _, _, status, *)
|
@@ -20,11 +31,58 @@ module Flatware
|
|
20
31
|
send_progress(status) if status
|
21
32
|
end
|
22
33
|
|
34
|
+
def exception(exception, status)
|
35
|
+
unless @in_a_step
|
36
|
+
current_scenario.exception = exception
|
37
|
+
send_progress(status)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to?(x)
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def before_outline_table(*)
|
46
|
+
@in_examples = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def after_outline_table(*)
|
50
|
+
@in_examples = false
|
51
|
+
end
|
52
|
+
|
53
|
+
def before_table_cell(*)
|
54
|
+
@in_a_step = @in_examples
|
55
|
+
end
|
56
|
+
|
57
|
+
def after_table_cell(*)
|
58
|
+
@in_a_step = ! @in_examples
|
59
|
+
end
|
60
|
+
|
61
|
+
def after_table_row(table_row)
|
62
|
+
exception(table_row.exception, :failed) if table_row.exception
|
63
|
+
end
|
64
|
+
|
65
|
+
def before_step(*)
|
66
|
+
@in_a_step = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def after_step(*)
|
70
|
+
@in_a_step = false
|
71
|
+
end
|
72
|
+
|
73
|
+
def scenario_name(_, name, file_colon_line, *)
|
74
|
+
scenarios.push OpenStruct.new file_colon_line: file_colon_line, name: name
|
75
|
+
end
|
76
|
+
|
23
77
|
private
|
24
|
-
attr_reader :collector
|
78
|
+
attr_reader :collector, :scenarios
|
79
|
+
|
80
|
+
def current_scenario
|
81
|
+
scenarios.last
|
82
|
+
end
|
25
83
|
|
26
84
|
def send_progress(status)
|
27
|
-
Sink.
|
85
|
+
Sink.progress Result.new status
|
28
86
|
end
|
29
87
|
|
30
88
|
class Collector
|
@@ -52,13 +110,5 @@ module Flatware
|
|
52
110
|
end
|
53
111
|
end
|
54
112
|
end
|
55
|
-
|
56
|
-
class ProgressString
|
57
|
-
extend ::Cucumber::Formatter::Console
|
58
|
-
def self.format(status)
|
59
|
-
return '' unless status
|
60
|
-
format_string FORMATS[status], status
|
61
|
-
end
|
62
|
-
end
|
63
113
|
end
|
64
114
|
end
|
data/lib/flatware/dispatcher.rb
CHANGED
@@ -2,18 +2,23 @@ module Flatware
|
|
2
2
|
class Dispatcher
|
3
3
|
PORT = 'ipc://dispatch'
|
4
4
|
|
5
|
-
def self.start(jobs
|
5
|
+
def self.start(jobs)
|
6
|
+
trap 'INT' do
|
7
|
+
Flatware.close
|
8
|
+
exit 1
|
9
|
+
end
|
6
10
|
new(jobs).dispatch!
|
7
11
|
end
|
8
12
|
|
9
13
|
def initialize(jobs)
|
10
|
-
@jobs
|
14
|
+
@jobs = jobs
|
15
|
+
@fireable = Fireable.new
|
16
|
+
@dispatch = Flatware.socket ZMQ::REP, bind: PORT
|
11
17
|
end
|
12
18
|
|
13
19
|
def dispatch!
|
14
|
-
return if jobs.empty?
|
15
20
|
fireable.until_fired dispatch do |request|
|
16
|
-
if job = jobs.
|
21
|
+
if job = jobs.shift
|
17
22
|
dispatch.send job
|
18
23
|
else
|
19
24
|
dispatch.send 'seppuku'
|
@@ -23,14 +28,6 @@ module Flatware
|
|
23
28
|
|
24
29
|
private
|
25
30
|
|
26
|
-
attr_reader :jobs
|
27
|
-
|
28
|
-
def fireable
|
29
|
-
@fireable ||= Fireable.new
|
30
|
-
end
|
31
|
-
|
32
|
-
def dispatch
|
33
|
-
@dispatch ||= Flatware.socket ZMQ::REP, bind: PORT
|
34
|
-
end
|
31
|
+
attr_reader :jobs, :fireable, :dispatch
|
35
32
|
end
|
36
33
|
end
|
data/lib/flatware/fireable.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'flatware/poller'
|
2
|
+
|
1
3
|
module Flatware
|
2
4
|
class Fireable
|
3
5
|
PORT = 'ipc://die'
|
@@ -20,7 +22,8 @@ module Flatware
|
|
20
22
|
|
21
23
|
def until_fired(socket, &block)
|
22
24
|
poller = Poller.new socket, die
|
23
|
-
poller.each do |
|
25
|
+
poller.each do |s|
|
26
|
+
message = s.recv
|
24
27
|
break if message == 'seppuku'
|
25
28
|
block.call message
|
26
29
|
end
|
@@ -28,23 +31,4 @@ module Flatware
|
|
28
31
|
Flatware.close
|
29
32
|
end
|
30
33
|
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
|
50
34
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Flatware
|
2
|
+
module Formatters
|
3
|
+
def self.load_by_name(names)
|
4
|
+
formatters = names.map do |name|
|
5
|
+
require "flatware/formatters/#{name}"
|
6
|
+
klass = const_get name.capitalize
|
7
|
+
klass.new $stdout, $stderr
|
8
|
+
end
|
9
|
+
Broadcaster.new formatters
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Broadcaster
|
14
|
+
attr_reader :formatters
|
15
|
+
|
16
|
+
def initialize(formatters)
|
17
|
+
@formatters = formatters
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(name, *args)
|
21
|
+
formatters.each do |formatter|
|
22
|
+
formatter.send name, *args if formatter.respond_to? name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'flatware/formatters/console/summary'
|
2
|
+
require 'cucumber/formatter/console'
|
3
|
+
|
4
|
+
module Flatware
|
5
|
+
module Formatters
|
6
|
+
class Console
|
7
|
+
#for format_string
|
8
|
+
include ::Cucumber::Formatter::Console
|
9
|
+
|
10
|
+
FORMATS = {
|
11
|
+
passed: '.',
|
12
|
+
failed: 'F',
|
13
|
+
undefined: 'U',
|
14
|
+
pending: 'P',
|
15
|
+
skipped: '-'
|
16
|
+
}
|
17
|
+
|
18
|
+
STATUSES = FORMATS.keys
|
19
|
+
|
20
|
+
attr_reader :out, :err
|
21
|
+
|
22
|
+
def initialize(stdout, stderr)
|
23
|
+
@out, @err = stdout, stderr
|
24
|
+
end
|
25
|
+
|
26
|
+
def progress(result)
|
27
|
+
out.print format result.progress
|
28
|
+
end
|
29
|
+
|
30
|
+
def summarize(steps, scenarios)
|
31
|
+
Summary.new(steps, scenarios, out).summarize
|
32
|
+
end
|
33
|
+
|
34
|
+
def summarize_remaining(remaining_jobs)
|
35
|
+
out.puts
|
36
|
+
out.puts "The following features have not been run:"
|
37
|
+
for job in remaining_jobs
|
38
|
+
out.puts job.id
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def format(status)
|
44
|
+
format_string FORMATS[status], status
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|