flatware 1.1.0 → 1.2.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/lib/flatware/cli.rb +26 -14
- data/lib/flatware/version.rb +1 -1
- metadata +5 -26
- data/lib/flatware-cucumber.rb +0 -1
- data/lib/flatware-rspec.rb +0 -1
- data/lib/flatware/cucumber.rb +0 -50
- data/lib/flatware/cucumber/cli.rb +0 -22
- data/lib/flatware/cucumber/formatter.rb +0 -66
- data/lib/flatware/cucumber/formatters/console.rb +0 -48
- data/lib/flatware/cucumber/formatters/console/summary.rb +0 -65
- data/lib/flatware/cucumber/result.rb +0 -27
- data/lib/flatware/cucumber/runtime.rb +0 -36
- data/lib/flatware/cucumber/scenario_result.rb +0 -38
- data/lib/flatware/cucumber/step_result.rb +0 -30
- data/lib/flatware/rspec.rb +0 -28
- data/lib/flatware/rspec/checkpoint.rb +0 -29
- data/lib/flatware/rspec/cli.rb +0 -16
- data/lib/flatware/rspec/example_notification.rb +0 -21
- data/lib/flatware/rspec/examples_notification.rb +0 -24
- data/lib/flatware/rspec/formatter.rb +0 -50
- data/lib/flatware/rspec/formatters/console.rb +0 -33
- data/lib/flatware/rspec/job_builder.rb +0 -110
- data/lib/flatware/rspec/summary.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9da02a0ce1009b7c0fc4c03cd33af10e5e2984b706ef46aca34dd8f33c593267
|
4
|
+
data.tar.gz: 94563ca34708d9d7563f36291eb781ab6f48654a09a0fe7d7912d04aae008a5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ac813bab61e13d630fde64a875e57eb973fd74c6a59d48772ab1ec2d5a688a99899b70e4874c9fcdd71348f88a4b668a97955fdea156c667968fe2ca63523fe
|
7
|
+
data.tar.gz: d7af5aa56a6ffd45043834440c144f8baebc7c7b59a6fc1d80e93dddc959d20f3db3b6cb66cd183e3f055898abbcd09ec9864266ed91f8df361efae15f70671e
|
data/lib/flatware/cli.rb
CHANGED
@@ -1,37 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require 'flatware/pids'
|
3
5
|
module Flatware
|
4
6
|
class CLI < Thor
|
5
|
-
|
6
7
|
def self.processors
|
7
8
|
@processors ||= ProcessorInfo.count
|
8
9
|
end
|
9
10
|
|
10
11
|
def self.worker_option
|
11
|
-
method_option :workers, aliases:
|
12
|
+
method_option :workers, aliases: '-w', type: :numeric, default: processors, desc: 'Number of concurent processes to run'
|
12
13
|
end
|
13
14
|
|
14
|
-
class_option :log, aliases:
|
15
|
+
class_option :log, aliases: '-l', type: :boolean, desc: 'Print debug messages to $stderr'
|
15
16
|
|
16
17
|
worker_option
|
17
|
-
desc
|
18
|
+
desc 'fan [COMMAND]', 'executes the given job on all of the workers'
|
18
19
|
def fan(*command)
|
19
20
|
Flatware.verbose = options[:log]
|
20
21
|
|
21
|
-
command = command.join(
|
22
|
+
command = command.join(' ')
|
22
23
|
puts "Running '#{command}' on #{workers} workers"
|
23
24
|
|
24
25
|
workers.times do |i|
|
25
26
|
fork do
|
26
|
-
exec({
|
27
|
+
exec({ 'TEST_ENV_NUMBER' => i.to_s }, command)
|
27
28
|
end
|
28
29
|
end
|
29
30
|
Process.waitall
|
30
31
|
end
|
31
32
|
|
32
|
-
desc
|
33
|
+
desc 'clear', 'kills all flatware processes'
|
33
34
|
def clear
|
34
|
-
(Flatware.pids - [
|
35
|
+
(Flatware.pids - [$PROCESS_ID]).each do |pid|
|
35
36
|
Process.kill 6, pid
|
36
37
|
end
|
37
38
|
end
|
@@ -39,7 +40,7 @@ module Flatware
|
|
39
40
|
private
|
40
41
|
|
41
42
|
def start_sink(jobs:, workers:, formatter:)
|
42
|
-
|
43
|
+
$0 = 'flatware sink'
|
43
44
|
Process.setpgrp
|
44
45
|
passed = Sink.start_server jobs: jobs, formatter: Flatware::Broadcaster.new([formatter]), sink: options['sink-endpoint'], dispatch: options['dispatch-endpoint'], worker_count: workers
|
45
46
|
exit passed ? 0 : 1
|
@@ -55,10 +56,21 @@ module Flatware
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
58
|
-
flatware_gems =
|
59
|
+
flatware_gems = %w[flatware-rspec flatware-cucumber]
|
60
|
+
|
61
|
+
loaded_flatware_gem_count = flatware_gems.map do |flatware_gem|
|
62
|
+
begin
|
63
|
+
require flatware_gem
|
64
|
+
rescue LoadError
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end.compact.size
|
68
|
+
|
69
|
+
if loaded_flatware_gem_count.zero?
|
70
|
+
warn(
|
71
|
+
format(<<~MESSAGE, gem_list: flatware_gems.join(' or ')))
|
72
|
+
The flatware gem is a dependency of flatware runners for rspec and cucumber.
|
73
|
+
Install %<gem_list>s for more usefull commands.
|
74
|
+
MESSAGE
|
59
75
|
|
60
|
-
if flatware_gems.count > 0
|
61
|
-
flatware_gems.each(&method(:require))
|
62
|
-
else
|
63
|
-
puts "The flatware gem is a dependency of flatware runners for rspec and cucumber. Try adding flatware-rspec or flatware-cucumber to your Gemfile."
|
64
76
|
end
|
data/lib/flatware/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flatware
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Dunn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi-rzmq
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 10.1.0
|
69
|
-
description: A distributed
|
69
|
+
description: A distributed rspec and cucumber runner
|
70
70
|
email: brian@hashrocket.com
|
71
71
|
executables:
|
72
72
|
- flatware
|
@@ -78,33 +78,13 @@ files:
|
|
78
78
|
- LICENSE.txt
|
79
79
|
- README.md
|
80
80
|
- bin/flatware
|
81
|
-
- lib/flatware-cucumber.rb
|
82
|
-
- lib/flatware-rspec.rb
|
83
81
|
- lib/flatware.rb
|
84
82
|
- lib/flatware/broadcaster.rb
|
85
83
|
- lib/flatware/cli.rb
|
86
|
-
- lib/flatware/cucumber.rb
|
87
|
-
- lib/flatware/cucumber/cli.rb
|
88
|
-
- lib/flatware/cucumber/formatter.rb
|
89
|
-
- lib/flatware/cucumber/formatters/console.rb
|
90
|
-
- lib/flatware/cucumber/formatters/console/summary.rb
|
91
|
-
- lib/flatware/cucumber/result.rb
|
92
|
-
- lib/flatware/cucumber/runtime.rb
|
93
|
-
- lib/flatware/cucumber/scenario_result.rb
|
94
|
-
- lib/flatware/cucumber/step_result.rb
|
95
84
|
- lib/flatware/job.rb
|
96
85
|
- lib/flatware/pids.rb
|
97
86
|
- lib/flatware/poller.rb
|
98
87
|
- lib/flatware/processor_info.rb
|
99
|
-
- lib/flatware/rspec.rb
|
100
|
-
- lib/flatware/rspec/checkpoint.rb
|
101
|
-
- lib/flatware/rspec/cli.rb
|
102
|
-
- lib/flatware/rspec/example_notification.rb
|
103
|
-
- lib/flatware/rspec/examples_notification.rb
|
104
|
-
- lib/flatware/rspec/formatter.rb
|
105
|
-
- lib/flatware/rspec/formatters/console.rb
|
106
|
-
- lib/flatware/rspec/job_builder.rb
|
107
|
-
- lib/flatware/rspec/summary.rb
|
108
88
|
- lib/flatware/serialized_exception.rb
|
109
89
|
- lib/flatware/sink.rb
|
110
90
|
- lib/flatware/sink/client.rb
|
@@ -130,9 +110,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
110
|
- !ruby/object:Gem::Version
|
131
111
|
version: '0'
|
132
112
|
requirements: []
|
133
|
-
|
134
|
-
rubygems_version: 2.7.6
|
113
|
+
rubygems_version: 3.0.3
|
135
114
|
signing_key:
|
136
115
|
specification_version: 4
|
137
|
-
summary: A distributed
|
116
|
+
summary: A distributed rspec and cucumber runner
|
138
117
|
test_files: []
|
data/lib/flatware-cucumber.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'flatware/cucumber'
|
data/lib/flatware-rspec.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'flatware/rspec'
|
data/lib/flatware/cucumber.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'cucumber'
|
2
|
-
require 'flatware/cucumber/formatter'
|
3
|
-
require 'flatware/cucumber/result'
|
4
|
-
require 'flatware/cucumber/scenario_result'
|
5
|
-
require 'flatware/cucumber/step_result'
|
6
|
-
require 'flatware/cucumber/formatters/console'
|
7
|
-
require 'flatware/cucumber/cli'
|
8
|
-
|
9
|
-
module Flatware
|
10
|
-
module Cucumber
|
11
|
-
class Config
|
12
|
-
attr_reader :config, :args
|
13
|
-
def initialize(cucumber_config, args)
|
14
|
-
@config, @args = cucumber_config, args
|
15
|
-
end
|
16
|
-
|
17
|
-
def feature_dir
|
18
|
-
@config.feature_dirs.first
|
19
|
-
end
|
20
|
-
|
21
|
-
def jobs
|
22
|
-
feature_files.map { |file| Job.new file, args }.to_a
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def feature_files
|
28
|
-
config.feature_files - config.feature_dirs
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
extend self
|
33
|
-
|
34
|
-
def configure(args, out_stream=$stdout, error_stream=$stderr)
|
35
|
-
raw_args = args.dup
|
36
|
-
cli_config = ::Cucumber::Cli::Configuration.new(out_stream, error_stream)
|
37
|
-
cli_config.parse! args + %w[--format Flatware::Cucumber::Formatter]
|
38
|
-
cucumber_config = ::Cucumber::Configuration.new cli_config
|
39
|
-
Config.new cucumber_config, raw_args
|
40
|
-
end
|
41
|
-
|
42
|
-
def run(feature_files, options)
|
43
|
-
runtime(Array(feature_files) + options).run!
|
44
|
-
end
|
45
|
-
|
46
|
-
def runtime(args)
|
47
|
-
::Cucumber::Runtime.new(configure(args).config)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'flatware/cli'
|
2
|
-
|
3
|
-
module Flatware
|
4
|
-
class CLI
|
5
|
-
worker_option
|
6
|
-
method_option 'dispatch-endpoint', type: :string, default: 'ipc://dispatch'
|
7
|
-
method_option 'sink-endpoint', type: :string, default: 'ipc://task'
|
8
|
-
desc "cucumber [FLATWARE_OPTS] [CUCUMBER_ARGS]", "parallelizes cucumber with custom arguments"
|
9
|
-
def cucumber(*args)
|
10
|
-
config = Cucumber.configure args
|
11
|
-
|
12
|
-
unless config.jobs.any?
|
13
|
-
puts "Please create some feature files in the #{config.feature_dir} directory."
|
14
|
-
exit 1
|
15
|
-
end
|
16
|
-
|
17
|
-
Flatware.verbose = options[:log]
|
18
|
-
Worker.spawn count: workers, runner: Cucumber, dispatch: options['dispatch-endpoint'], sink: options['sink-endpoint']
|
19
|
-
start_sink jobs: config.jobs, workers: workers, formatter: Flatware::Cucumber::Formatters::Console.new($stdout, $stderr)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'cucumber'
|
2
|
-
require 'flatware/sink'
|
3
|
-
require 'ostruct'
|
4
|
-
module Flatware
|
5
|
-
module Cucumber
|
6
|
-
class Formatter
|
7
|
-
Checkpoint = Struct.new :steps, :scenarios do
|
8
|
-
def failures?
|
9
|
-
scenarios.any? &:failed?
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
Scenario = Struct.new :name, :file_colon_line, :status do
|
14
|
-
def failed?
|
15
|
-
status == :failed
|
16
|
-
end
|
17
|
-
|
18
|
-
def failed_outside_step?
|
19
|
-
false
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize(config)
|
24
|
-
config.on_event :test_case_finished, &method(:on_test_case_finished)
|
25
|
-
config.on_event :test_step_finished, &method(:on_test_step_finished)
|
26
|
-
config.on_event :test_run_finished, &method(:on_test_run_finished)
|
27
|
-
config.on_event :step_activated, &method(:on_step_activated)
|
28
|
-
reset
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def reset
|
34
|
-
@steps = []
|
35
|
-
@scenarios = []
|
36
|
-
@matched_steps = Set.new
|
37
|
-
end
|
38
|
-
|
39
|
-
attr_reader :steps, :scenarios, :matched_steps
|
40
|
-
|
41
|
-
def on_test_case_finished(event)
|
42
|
-
scenarios << Scenario.new(event.test_case.name, event.test_case.location.to_s, event.result.to_sym)
|
43
|
-
end
|
44
|
-
|
45
|
-
def on_step_activated(event)
|
46
|
-
@matched_steps << event.step_match.location.to_s
|
47
|
-
end
|
48
|
-
|
49
|
-
def on_test_step_finished(event)
|
50
|
-
if really_a_step?(event.test_step) or event.result.undefined?
|
51
|
-
steps << StepResult.new(event.result.to_sym, event.result.failed? && event.result.exception)
|
52
|
-
Sink::client.progress Result.new event.result.to_sym
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def on_test_run_finished(*)
|
57
|
-
Sink::client.checkpoint Checkpoint.new steps, scenarios
|
58
|
-
reset
|
59
|
-
end
|
60
|
-
|
61
|
-
def really_a_step?(step)
|
62
|
-
matched_steps.include?(step.action_location.to_s)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'flatware/cucumber/formatters/console/summary'
|
2
|
-
require 'cucumber/formatter/console'
|
3
|
-
|
4
|
-
module Flatware::Cucumber::Formatters
|
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
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'cucumber/formatter/console'
|
2
|
-
require 'flatware/cucumber/formatters/console'
|
3
|
-
|
4
|
-
module Flatware::Cucumber::Formatters
|
5
|
-
class Console
|
6
|
-
class Summary
|
7
|
-
include ::Cucumber::Formatter::Console
|
8
|
-
attr_reader :io, :steps, :scenarios
|
9
|
-
|
10
|
-
def initialize(steps, scenarios=[], io=StringIO.new)
|
11
|
-
@io = io
|
12
|
-
@steps = steps
|
13
|
-
@scenarios = scenarios
|
14
|
-
end
|
15
|
-
|
16
|
-
def summarize
|
17
|
-
2.times { io.puts }
|
18
|
-
print_failures(steps, 'step')
|
19
|
-
print_failures(scenarios.select(&:failed_outside_step?), 'scenario')
|
20
|
-
print_failed_scenarios scenarios
|
21
|
-
print_counts 'scenario', scenarios
|
22
|
-
print_counts 'step', steps
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def print_failed_scenarios(scenarios)
|
28
|
-
return unless scenarios.any? &with_status(:failed)
|
29
|
-
|
30
|
-
io.puts format_string "Failing Scenarios:", :failed
|
31
|
-
scenarios.select(&with_status(:failed)).sort_by(&:file_colon_line).each do |scenario|
|
32
|
-
io.puts format_string(scenario.file_colon_line, :failed) + format_string(" # Scenario: " + scenario.name, :comment)
|
33
|
-
end
|
34
|
-
io.puts
|
35
|
-
end
|
36
|
-
|
37
|
-
def print_failures(collection, label)
|
38
|
-
failures = collection.select(&with_status(:failed))
|
39
|
-
print_elements failures, :failed, pluralize(label, failures.size)
|
40
|
-
end
|
41
|
-
|
42
|
-
def print_counts(label, collection)
|
43
|
-
io.puts pluralize(label, collection.size) + count_summary(collection)
|
44
|
-
end
|
45
|
-
|
46
|
-
def pluralize(word, number)
|
47
|
-
"#{number} #{number == 1 ? word : word + 's'}"
|
48
|
-
end
|
49
|
-
|
50
|
-
def with_status(status)
|
51
|
-
proc {|r| r.status == status}
|
52
|
-
end
|
53
|
-
|
54
|
-
def count_summary(results)
|
55
|
-
return "" unless results.any?
|
56
|
-
status_counts = STATUSES.map do |status|
|
57
|
-
count = results.select(&with_status(status)).size
|
58
|
-
format_string "#{count} #{status}", status if count > 0
|
59
|
-
end.compact.join ", "
|
60
|
-
|
61
|
-
" (#{status_counts})"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,27 +0,0 @@
|
|
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
|
@@ -1,36 +0,0 @@
|
|
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
|
@@ -1,38 +0,0 @@
|
|
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
|
@@ -1,30 +0,0 @@
|
|
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) if 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
|
-
e.backtrace and e.backtrace.unshift e.backtrace.shift.sub(Dir.pwd, '.')
|
26
|
-
SerializedException.new(e.class, e.message, e.backtrace)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/flatware/rspec.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rspec/core'
|
4
|
-
require 'rspec/expectations'
|
5
|
-
require 'flatware/rspec/cli'
|
6
|
-
|
7
|
-
module Flatware
|
8
|
-
module RSpec
|
9
|
-
require 'flatware/rspec/formatters/console'
|
10
|
-
require 'flatware/rspec/formatter'
|
11
|
-
require 'flatware/rspec/job_builder'
|
12
|
-
|
13
|
-
def self.extract_jobs_from_args(args, workers:)
|
14
|
-
JobBuilder.new(args, workers: workers).jobs
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.run(job, _options = {})
|
18
|
-
runner = ::RSpec::Core::Runner
|
19
|
-
def runner.trap_interrupt() end
|
20
|
-
|
21
|
-
args = %w[
|
22
|
-
--format Flatware::RSpec::Formatter
|
23
|
-
] + Array(job)
|
24
|
-
|
25
|
-
runner.run(args, $stderr, $stdout)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'flatware/rspec/examples_notification'
|
2
|
-
|
3
|
-
module Flatware
|
4
|
-
module RSpec
|
5
|
-
class Checkpoint
|
6
|
-
attr_reader :summary, :failures_notification
|
7
|
-
|
8
|
-
def initialize(summary, failures_notification)
|
9
|
-
@summary, @failures_notification = summary, ExamplesNotification.new(failures_notification.failure_notifications)
|
10
|
-
end
|
11
|
-
|
12
|
-
def +(other)
|
13
|
-
self.class.new summary + other.summary, failures_notification + other.failures_notification
|
14
|
-
end
|
15
|
-
|
16
|
-
def failures?
|
17
|
-
summary.failure_count > 0 || summary.errors_outside_of_examples_count > 0
|
18
|
-
end
|
19
|
-
|
20
|
-
def failure_notifications
|
21
|
-
failures_notification.failure_notifications
|
22
|
-
end
|
23
|
-
|
24
|
-
def fully_formatted_failed_examples(*args)
|
25
|
-
failures_notification.fully_formatted_failed_examples(*args)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/lib/flatware/rspec/cli.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'flatware/cli'
|
2
|
-
|
3
|
-
module Flatware
|
4
|
-
class CLI
|
5
|
-
worker_option
|
6
|
-
method_option 'dispatch-endpoint', type: :string, default: 'ipc://dispatch'
|
7
|
-
method_option 'sink-endpoint', type: :string, default: 'ipc://task'
|
8
|
-
desc "rspec [FLATWARE_OPTS]", "parallelizes rspec"
|
9
|
-
def rspec(*rspec_args)
|
10
|
-
jobs = RSpec.extract_jobs_from_args rspec_args, workers: workers
|
11
|
-
Flatware.verbose = options[:log]
|
12
|
-
Worker.spawn count: workers, runner: RSpec, dispatch: options['dispatch-endpoint'], sink: options['sink-endpoint']
|
13
|
-
start_sink jobs: jobs, workers: workers, formatter: Flatware::RSpec::Formatters::Console.new($stdout, $stderr)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'rspec/core/formatters/console_codes'
|
2
|
-
|
3
|
-
module Flatware
|
4
|
-
module RSpec
|
5
|
-
class ExampleNotification
|
6
|
-
attr_reader :formatted
|
7
|
-
def initialize(notification)
|
8
|
-
@formatted = notification.fully_formatted '!', default_colorizer
|
9
|
-
end
|
10
|
-
|
11
|
-
def fully_formatted(i, _=nil)
|
12
|
-
formatted.sub '!', i.to_s
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
def default_colorizer
|
17
|
-
::RSpec::Core::Formatters::ConsoleCodes
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'flatware/rspec/example_notification'
|
2
|
-
module Flatware
|
3
|
-
module RSpec
|
4
|
-
class ExamplesNotification
|
5
|
-
attr_reader :failure_notifications
|
6
|
-
|
7
|
-
def initialize(failure_notifications)
|
8
|
-
@failure_notifications = failure_notifications.map(&ExampleNotification.method(:new))
|
9
|
-
end
|
10
|
-
|
11
|
-
def +(other)
|
12
|
-
self.class.new failure_notifications + other.failure_notifications
|
13
|
-
end
|
14
|
-
|
15
|
-
def fully_formatted_failed_examples(*)
|
16
|
-
formatted = "\n\nFailures:\n"
|
17
|
-
failure_notifications.each_with_index do |failure, index|
|
18
|
-
formatted << failure.fully_formatted(index.next)
|
19
|
-
end
|
20
|
-
formatted
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'flatware/rspec/checkpoint'
|
2
|
-
require 'flatware/rspec/summary'
|
3
|
-
require 'rspec/core/formatters/console_codes'
|
4
|
-
|
5
|
-
module Flatware
|
6
|
-
module RSpec
|
7
|
-
ProgressMessage = Struct.new(:progress)
|
8
|
-
|
9
|
-
class Formatter
|
10
|
-
attr_reader :summary, :output
|
11
|
-
|
12
|
-
def initialize(stdout)
|
13
|
-
@output = stdout
|
14
|
-
end
|
15
|
-
|
16
|
-
def example_passed(example)
|
17
|
-
send_progress :passed
|
18
|
-
end
|
19
|
-
|
20
|
-
def example_failed(example)
|
21
|
-
send_progress :failed
|
22
|
-
end
|
23
|
-
|
24
|
-
def example_pending(example)
|
25
|
-
send_progress :pending
|
26
|
-
end
|
27
|
-
|
28
|
-
def dump_summary(summary)
|
29
|
-
@summary = Summary.from_notification(summary)
|
30
|
-
end
|
31
|
-
|
32
|
-
def dump_failures(failure_notification)
|
33
|
-
@failure_notification = failure_notification
|
34
|
-
end
|
35
|
-
|
36
|
-
def close(*)
|
37
|
-
Sink::client.checkpoint Checkpoint.new(summary, @failure_notification)
|
38
|
-
@failure_notification = nil
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def send_progress(status)
|
44
|
-
Sink::client.progress ProgressMessage.new status
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
::RSpec::Core::Formatters.register Formatter, :example_passed, :example_failed, :example_pending, :dump_summary, :dump_failures, :close
|
49
|
-
end
|
50
|
-
end
|
@@ -1,33 +0,0 @@
|
|
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
|
@@ -1,110 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Flatware
|
4
|
-
module RSpec
|
5
|
-
# groups spec files into one job per worker.
|
6
|
-
# reads from persisted example statuses, if available,
|
7
|
-
# and attempts to ballence the jobs accordingly.
|
8
|
-
class JobBuilder
|
9
|
-
extend Forwardable
|
10
|
-
attr_reader :args, :workers, :configuration
|
11
|
-
def_delegators(
|
12
|
-
:configuration,
|
13
|
-
:files_to_run,
|
14
|
-
:example_status_persistence_file_path
|
15
|
-
)
|
16
|
-
|
17
|
-
def initialize(args, workers:)
|
18
|
-
@args = args
|
19
|
-
@workers = workers
|
20
|
-
|
21
|
-
@configuration = ::RSpec.configuration
|
22
|
-
configuration.define_singleton_method(:command) { 'rspec' }
|
23
|
-
|
24
|
-
::RSpec::Core::ConfigurationOptions.new(args).configure(@configuration)
|
25
|
-
end
|
26
|
-
|
27
|
-
def jobs
|
28
|
-
bucket_count = [files_to_run.size, workers].min
|
29
|
-
|
30
|
-
seconds_per_file = load_persisted_example_statuses
|
31
|
-
.select(&passing)
|
32
|
-
.map(&parse_example)
|
33
|
-
.reduce({}, &sum_by_example_file)
|
34
|
-
|
35
|
-
timed_files, untimed_files = files_to_run
|
36
|
-
.map(&method(:normalize_path))
|
37
|
-
.reduce(
|
38
|
-
[[], []]
|
39
|
-
) do |(timed, untimed), file|
|
40
|
-
if (time = seconds_per_file[file])
|
41
|
-
[timed.append([file, time]), untimed]
|
42
|
-
else
|
43
|
-
[timed, untimed.append(file)]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
balance_by(bucket_count, timed_files, &:last)
|
48
|
-
.map { |bucket| bucket.map(&:first) }
|
49
|
-
.zip(
|
50
|
-
round_robin(bucket_count, untimed_files)
|
51
|
-
).map(&:flatten)
|
52
|
-
.map do |files|
|
53
|
-
Job.new(files, args)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def normalize_path(path)
|
60
|
-
::RSpec::Core::Metadata.relative_path(File.expand_path(path))
|
61
|
-
end
|
62
|
-
|
63
|
-
def load_persisted_example_statuses
|
64
|
-
::RSpec::Core::ExampleStatusPersister.load_from(
|
65
|
-
example_status_persistence_file_path || ''
|
66
|
-
)
|
67
|
-
end
|
68
|
-
|
69
|
-
def sum_by_example_file
|
70
|
-
lambda do |times, file_name:, seconds:|
|
71
|
-
times.merge(file_name => seconds) { |_, old = 0, new| old + new }
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def passing
|
76
|
-
->(status:, **) { status =~ /pass/i }
|
77
|
-
end
|
78
|
-
|
79
|
-
def parse_example
|
80
|
-
lambda do |example_id:, run_time:, **|
|
81
|
-
seconds = run_time.match(/\d+(\.\d+)?/).to_s.to_f
|
82
|
-
file_name = ::RSpec::Core::Example.parse_id(example_id).first
|
83
|
-
{ seconds: seconds, file_name: file_name }
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def round_robin(count, items)
|
88
|
-
Array.new(count) { [] }.tap do |groups|
|
89
|
-
items.each_with_index do |entry, i|
|
90
|
-
groups[i % count] << entry
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def balance_by(count, items, &block)
|
96
|
-
# find the group with the smallest sum and add it there
|
97
|
-
Array.new(count) { [] }.tap do |groups|
|
98
|
-
items
|
99
|
-
.sort_by(&block)
|
100
|
-
.reverse
|
101
|
-
.each do |entry|
|
102
|
-
groups.min_by do |group|
|
103
|
-
group.map(&block).reduce(:+) || 0
|
104
|
-
end.push(entry)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'rspec/core/notifications'
|
2
|
-
module Flatware
|
3
|
-
module RSpec
|
4
|
-
class Example
|
5
|
-
attr_reader :location_rerun_argument, :full_description
|
6
|
-
def initialize(rspec_example)
|
7
|
-
@full_description = rspec_example.full_description
|
8
|
-
@location_rerun_argument = rspec_example.location_rerun_argument
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
class Summary < ::RSpec::Core::Notifications::SummaryNotification
|
13
|
-
def +(other)
|
14
|
-
self.class.new(*zip(other).map {|a,b| a + b})
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.from_notification(summary)
|
18
|
-
serialized_examples = [summary.examples, summary.failed_examples, summary.pending_examples].map do |examples|
|
19
|
-
examples.map(&Example.method(:new))
|
20
|
-
end
|
21
|
-
|
22
|
-
new summary.duration, *serialized_examples, *summary.to_a[4..-1]
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|