flatware-rc 1.0.0.rc
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/LICENSE.txt +21 -0
- data/README.md +132 -0
- data/bin/flatware +6 -0
- data/lib/flatware.rb +10 -0
- data/lib/flatware/checkpoint_handler.rb +34 -0
- data/lib/flatware/cli.rb +89 -0
- data/lib/flatware/cucumber.rb +34 -0
- data/lib/flatware/cucumber/checkpoint.rb +28 -0
- data/lib/flatware/cucumber/formatter.rb +112 -0
- data/lib/flatware/cucumber/result.rb +27 -0
- data/lib/flatware/cucumber/runtime.rb +36 -0
- data/lib/flatware/cucumber/scenario_decorator.rb +24 -0
- data/lib/flatware/cucumber/scenario_result.rb +38 -0
- data/lib/flatware/cucumber/step_result.rb +29 -0
- data/lib/flatware/formatters.rb +26 -0
- data/lib/flatware/formatters/cucumber/console.rb +48 -0
- data/lib/flatware/formatters/cucumber/console/summary.rb +66 -0
- data/lib/flatware/formatters/cucumber/http.rb +83 -0
- data/lib/flatware/formatters/rspec/console.rb +33 -0
- data/lib/flatware/pids.rb +25 -0
- data/lib/flatware/poller.rb +35 -0
- data/lib/flatware/processor_info.rb +24 -0
- data/lib/flatware/rspec.rb +28 -0
- data/lib/flatware/rspec/checkpoint.rb +29 -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/summary.rb +40 -0
- data/lib/flatware/serialized_exception.rb +24 -0
- data/lib/flatware/sink.rb +105 -0
- data/lib/flatware/sink/client.rb +25 -0
- data/lib/flatware/socket.rb +181 -0
- data/lib/flatware/version.rb +3 -0
- data/lib/flatware/worker.rb +57 -0
- metadata +170 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Flatware
|
2
|
+
module Cucumber
|
3
|
+
class Result
|
4
|
+
attr_reader :progress, :worker
|
5
|
+
|
6
|
+
def initialize(progress)
|
7
|
+
@progress = progress
|
8
|
+
@worker = ENV['TEST_ENV_NUMBER'].to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def step(*args)
|
13
|
+
step = StepResult.new *args
|
14
|
+
new step.progress, [step]
|
15
|
+
end
|
16
|
+
|
17
|
+
def status(status)
|
18
|
+
new status
|
19
|
+
end
|
20
|
+
|
21
|
+
def background(status, exception)
|
22
|
+
new '', [StepResult.new(status, exception)]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'cucumber'
|
2
|
+
|
3
|
+
module Flatware
|
4
|
+
module Cucumber
|
5
|
+
class Runtime < ::Cucumber::Runtime
|
6
|
+
|
7
|
+
attr_accessor :configuration, :loader
|
8
|
+
attr_reader :out, :err
|
9
|
+
attr_reader :visitor
|
10
|
+
|
11
|
+
def initialize(out=StringIO.new, err=out)
|
12
|
+
@out, @err = out, err
|
13
|
+
super(default_configuration)
|
14
|
+
load_step_definitions
|
15
|
+
@results = Results.new(configuration)
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_configuration
|
19
|
+
config = ::Cucumber::Cli::Configuration.new
|
20
|
+
config.parse! []
|
21
|
+
config
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(feature_files=[], options=[])
|
25
|
+
@loader = nil
|
26
|
+
options = Array(feature_files) + %w[--format Flatware::Cucumber::Formatter] + options
|
27
|
+
|
28
|
+
configure(::Cucumber::Cli::Main.new(options, out, err).configuration)
|
29
|
+
|
30
|
+
self.visitor = configuration.build_tree_walker(self)
|
31
|
+
visitor.visit_features(features)
|
32
|
+
results
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
module Flatware
|
3
|
+
module Cucumber
|
4
|
+
class ScenarioDecorator
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :scenario, :name, :file_colon_line
|
7
|
+
|
8
|
+
attr_reader :status, :exception
|
9
|
+
|
10
|
+
def initialize(scenario)
|
11
|
+
@scenario, @status, @exception = scenario, scenario.status, scenario.exception
|
12
|
+
@scenario, @exception = scenario.scenario_outline, scenario.exception if example_row?
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :scenario
|
18
|
+
|
19
|
+
def example_row?
|
20
|
+
scenario.respond_to? :scenario_outline
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'flatware/serialized_exception'
|
2
|
+
module Flatware
|
3
|
+
module Cucumber
|
4
|
+
class ScenarioResult
|
5
|
+
attr_reader :status, :file_colon_line, :name
|
6
|
+
def initialize(status, file_colon_line, name, e)
|
7
|
+
@status = status
|
8
|
+
@file_colon_line = file_colon_line
|
9
|
+
@name = name
|
10
|
+
@exception = SerializedException.new(e.class, e.message, e.backtrace) if e
|
11
|
+
@failed_outside_step = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def passed?
|
15
|
+
status == :passed
|
16
|
+
end
|
17
|
+
|
18
|
+
def failed?
|
19
|
+
status == :failed
|
20
|
+
end
|
21
|
+
|
22
|
+
def failed_outside_step!(file_colon_line)
|
23
|
+
@failed_outside_step = file_colon_line
|
24
|
+
end
|
25
|
+
|
26
|
+
def failed_outside_step?
|
27
|
+
!!@failed_outside_step
|
28
|
+
end
|
29
|
+
|
30
|
+
def exception
|
31
|
+
@exception.tap do |e|
|
32
|
+
e.backtrace = e.backtrace.grep(Regexp.new(Dir.pwd)).map { |line| line[Dir.pwd.size..-1] }
|
33
|
+
e.backtrace = e.backtrace + [@failed_outside_step] if failed_outside_step?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'flatware/serialized_exception'
|
2
|
+
module Flatware
|
3
|
+
module Cucumber
|
4
|
+
class StepResult
|
5
|
+
attr_reader :status, :exception
|
6
|
+
|
7
|
+
def initialize(status, exception)
|
8
|
+
@status, @exception = status, serialized(exception)
|
9
|
+
end
|
10
|
+
|
11
|
+
def passed?
|
12
|
+
status == :passed
|
13
|
+
end
|
14
|
+
|
15
|
+
def failed?
|
16
|
+
status == :failed
|
17
|
+
end
|
18
|
+
|
19
|
+
def progress
|
20
|
+
Cucumber::ProgressString.format(status)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def serialized(e)
|
25
|
+
SerializedException.new(e.class, e.message, e.backtrace) if e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Flatware
|
2
|
+
module Formatters
|
3
|
+
def self.load_by_name(runner, names)
|
4
|
+
formatters = names.map do |name|
|
5
|
+
namespace = const_get({rspec: 'RSpec', cucumber: 'Cucumber'}.fetch(runner))
|
6
|
+
klass = namespace.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/cucumber/console/summary'
|
2
|
+
require 'cucumber/formatter/console'
|
3
|
+
|
4
|
+
module Flatware::Formatters::Cucumber
|
5
|
+
class Console
|
6
|
+
#for format_string
|
7
|
+
include ::Cucumber::Formatter::Console
|
8
|
+
|
9
|
+
FORMATS = {
|
10
|
+
passed: '.',
|
11
|
+
failed: 'F',
|
12
|
+
undefined: 'U',
|
13
|
+
pending: 'P',
|
14
|
+
skipped: '-'
|
15
|
+
}
|
16
|
+
|
17
|
+
STATUSES = FORMATS.keys
|
18
|
+
|
19
|
+
attr_reader :out, :err
|
20
|
+
|
21
|
+
def initialize(stdout, stderr)
|
22
|
+
@out, @err = stdout, stderr
|
23
|
+
end
|
24
|
+
|
25
|
+
def progress(result)
|
26
|
+
out.print format result.progress
|
27
|
+
end
|
28
|
+
|
29
|
+
def summarize(checkpoints)
|
30
|
+
steps = checkpoints.flat_map(&:steps)
|
31
|
+
scenarios = checkpoints.flat_map(&:scenarios)
|
32
|
+
Summary.new(steps, scenarios, out).summarize
|
33
|
+
end
|
34
|
+
|
35
|
+
def summarize_remaining(remaining_jobs)
|
36
|
+
out.puts
|
37
|
+
out.puts "The following features have not been run:"
|
38
|
+
for job in remaining_jobs
|
39
|
+
out.puts job.id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def format(status)
|
45
|
+
format_string FORMATS[status], status
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'cucumber/formatter/console'
|
2
|
+
require 'flatware/formatters/cucumber/console'
|
3
|
+
require 'flatware/cucumber/checkpoint'
|
4
|
+
|
5
|
+
module Flatware::Formatters::Cucumber
|
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
|
@@ -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,33 @@
|
|
1
|
+
module Flatware::Formatters::RSpec
|
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,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
|