flatware-rspec 0.4.0 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 96ee1612dd1b6bedadb6d14a2314dd62f8e06121
4
- data.tar.gz: 57140b234970845f9ef60dcfcfe5a500ee03277a
2
+ SHA256:
3
+ metadata.gz: e6a0aaa1271e9e220195b1198be4374e2a3e0a40fbe0dbdb61fad0c4735ab4e0
4
+ data.tar.gz: 2e16cb3d10ac6a990a393b84e0a3ae04ea4ccccfdbb9de3d1b264d4729a2d32f
5
5
  SHA512:
6
- metadata.gz: ff248de8080055159ea82ad929ef1f0481153fd4cc3fa4b4dfcc55e7a584009024f6255d226c652ea8dd85a326005d98771d1f0bead85913999c1cdf12ef07c1
7
- data.tar.gz: 9f44cdadf9e6fa43a3d56b0e2f601b5434460553d9c4962f89e4c36647f7ad7da3eb2c8806467c4058b2eeacbfe0df62ccb78edc2b08c7491ff756190310fa10
6
+ metadata.gz: 2b133ea289d7abe6828bf0c7188bb389c113126ea8e79866aed5b973d4213b473f79e219c4f3d151f6c1bbf175fd48790140c531c6ce4eeb488837c4bd1ed618
7
+ data.tar.gz: 26d0a1a2a322ee2ad2cb4ac9b5d33c42aefdbe15a80d1c108d8e7d8b11a4f50e8bfb36e8003496c3215c939c74c3797f1eca7dad0c11843d96d1e9e1f3f85c3c
data/README.md CHANGED
@@ -7,39 +7,13 @@
7
7
 
8
8
  Flatware parallelizes your test suite to significantly reduce test time.
9
9
 
10
- ## Requirements
11
-
12
- * ZeroMQ > 4.0
13
-
14
- ## Installation
15
-
16
- ### ZeroMQ
17
-
18
- #### Linux Ubuntu
19
-
20
- ```sh
21
- sudo apt-get install -qq libzmq3-dev
22
- ```
23
-
24
- (Never you mind the 3. This package contains ZMQ version 4.)
25
-
26
- #### Mac OSX
27
-
28
- Ruby FFI isn't getting along with the latest ZMQ formula. A tweaked verson is available in the Hashrocket tap.
29
-
30
- ```sh
31
- brew tap hashrocket/formulas
32
- brew install hashrocket/formulas/zeromq
33
- brew install zeromq
34
- ```
35
-
36
10
  ### Flatware
37
11
 
38
12
  Add the runners you need to your Gemfile:
39
13
 
40
14
  ```ruby
41
- gem 'flatware-rspec' # one
42
- gem 'flatware-cucumber' # or both
15
+ gem 'flatware-rspec', require: false # one
16
+ gem 'flatware-cucumber', require: false # or both
43
17
  ```
44
18
 
45
19
  then run
@@ -66,6 +40,18 @@ To run your entire suite with the default rspec options add the `flatware-rspec`
66
40
  $ flatware rspec
67
41
  ```
68
42
 
43
+ The rspec runner can balance worker loads, making your suite even faster.
44
+
45
+ It forms balaced groups of spec files according to their last run times, if you've set `example_status_persistence_file_path` [in your RSpec config](https://relishapp.com/rspec/rspec-core/v/3-8/docs/command-line/only-failures).
46
+
47
+ For this to work the configuration option must be loaded before any specs are run. The `.rspec` file is one way to achive this:
48
+
49
+ --require spec_helper
50
+
51
+ But beware, if you're using ActiveRecord in your suite you'll need to avoid doing things that cause it to establish a database connection in `spec_helper.rb`. If ActiveRecord connects before flatware forks off workers, each will die messily. All of this will just work if you're following [the recomended pattern of splitting your helpers into `spec_helper` and `rails_helper`](https://github.com/rspec/rspec-rails/blob/v3.8.2/lib/generators/rspec/install/templates/spec/rails_helper.rb). Another option is to use [the configurable hooks](
52
+ #faster-startup-with-activerecord
53
+ ).
54
+
69
55
  ### Options
70
56
 
71
57
  If you'd like to limit the number of forked workers, you can pass the 'w' flag:
@@ -78,7 +64,7 @@ You can also pass most cucumber/rspec options to Flatware. For example, to run o
78
64
  features that are not tagged 'javascript', you can:
79
65
 
80
66
  ```sh
81
- $ flatware cucumber -t ~@javascript
67
+ $ flatware cucumber -t 'not @javascript'
82
68
  ```
83
69
 
84
70
  Additionally, for either cucumber or rspec you can specify a directory:
@@ -116,9 +102,31 @@ Now you are ready to rock:
116
102
  $ flatware rspec && flatware cucumber
117
103
  ```
118
104
 
119
- ## Planned Features
105
+ ### Faster Startup With ActiveRecord
120
106
 
121
- * Use heuristics to run your slowest tests first
107
+ Flatware has a couple lifecycle callbacks that you can use to avoid booting your app
108
+ over again on every core. One way to take advantage of this via a `spec/flatware_helper.rb` file like so:
109
+
110
+ ```ruby
111
+ Flatware.configure do |conf|
112
+ conf.before_fork do
113
+ require 'rails_helper'
114
+
115
+ ActiveRecord::Base.connection.disconnect!
116
+ end
117
+
118
+ conf.after_fork do |test_env_number|
119
+ config = ActiveRecord::Base.connection_config
120
+
121
+ ActiveRecord::Base.establish_connection(
122
+ config.merge(
123
+ database: config.fetch(:database) + test_env_number.to_s
124
+ )
125
+ )
126
+ end
127
+ end
128
+ ```
129
+ Now when I run `bundle exec flatware rspec -r ./spec/flatware_helper` My app only boots once, rather than once per core.
122
130
 
123
131
  ## Design Goals
124
132
 
@@ -149,20 +157,10 @@ directory. CD there and `flatware` will be in your path so you can tinker away.
149
157
 
150
158
  ## How it works
151
159
 
152
- Flatware relies on a message passing system to enable concurrency.
153
- The main process declares a worker for each cpu in the computer. Each
154
- worker forks from the main process and is then assigned a portion of the
155
- test suite. As the worker runs the test suite it sends progress
156
- messages to the main process. These messages are collected and when
157
- the last worker is finished the main process provides a report on the
158
- collected progress messages.
160
+ Flatware relies on a message passing system to enable concurrency. The main process forks a worker for each cpu in the computer. These workers are each given a chunk of the tests to run. The workers report back to the main process about their progress. The main process prints those progress messages. When the last worker is finished the main process prints the results.
159
161
 
160
162
  ## Resources
161
163
 
162
- To learn more about the messaging system that Flatware uses, take a look at the
163
- [excellent ZeroMQ guide][z].
164
-
165
- [z]: http://zguide.zeromq.org/page:all
166
164
  [a]: https://github.com/cucumber/aruba
167
165
 
168
166
  ## Contributing to Flatware
@@ -0,0 +1,29 @@
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/formatter'
10
+ require 'flatware/rspec/job_builder'
11
+
12
+ module_function
13
+
14
+ def extract_jobs_from_args(args, workers:)
15
+ JobBuilder.new(args, workers: workers).jobs
16
+ end
17
+
18
+ def run(job, _options = {})
19
+ runner = ::RSpec::Core::Runner
20
+ def runner.trap_interrupt() end
21
+
22
+ args = %w[
23
+ --format Flatware::RSpec::Formatter
24
+ ] + Array(job)
25
+
26
+ runner.run(args, $stderr, $stdout)
27
+ end
28
+ end
29
+ end
@@ -1,28 +1,60 @@
1
- require 'flatware/rspec/examples_notification'
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'flatware/rspec/marshalable'
2
5
 
3
6
  module Flatware
4
7
  module RSpec
8
+ ##
9
+ # Marshalable container for the run details from a worker.
10
+ # Can be added to other checkpoints to create a final run summary.
5
11
  class Checkpoint
6
- attr_reader :summary, :failures_notification
12
+ extend Forwardable
13
+
14
+ def_delegator :summary, :failures?
15
+ def_delegators :failures_notification, :fully_formatted_failed_examples, :failure_notifications
16
+ def_delegators :pending_notification, :fully_formatted_pending_examples, :pending_examples
17
+
18
+ EVENTS = %i[dump_profile dump_summary dump_pending dump_failures].freeze
19
+
20
+ attr_reader :events
21
+
22
+ def initialize(events = {})
23
+ @events = events
24
+ end
7
25
 
8
- def initialize(summary, failures_notification)
9
- @summary, @failures_notification = summary, ExamplesNotification.new(failures_notification.failure_notifications)
26
+ EVENTS.each do |event|
27
+ define_method(event) do |notification|
28
+ events[event] = Marshalable.for_event(event).from_notification(notification)
29
+ end
10
30
  end
11
31
 
12
32
  def +(other)
13
- self.class.new summary + other.summary, failures_notification + other.failures_notification
33
+ self.class.new(
34
+ events.merge(other.events) { |_, event, other_event| event + other_event }
35
+ )
14
36
  end
15
37
 
16
38
  def failures?
17
- summary.failure_count > 0
39
+ summary.failures?
18
40
  end
19
41
 
20
- def failure_notifications
21
- failures_notification.failure_notifications
42
+ def summary
43
+ events.fetch(:dump_summary)
44
+ end
45
+
46
+ def profile
47
+ events[:dump_profile]
48
+ end
49
+
50
+ private
51
+
52
+ def failures_notification
53
+ events.fetch(:dump_failures)
22
54
  end
23
55
 
24
- def fully_formatted_failed_examples(*args)
25
- failures_notification.fully_formatted_failed_examples(*args)
56
+ def pending_notification
57
+ events.fetch(:dump_pending)
26
58
  end
27
59
  end
28
60
  end
@@ -1,16 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'flatware/cli'
4
+ require 'flatware/rspec'
5
+ require 'flatware/rspec/formatters/console'
2
6
 
3
7
  module Flatware
8
+ # rspec thor command
4
9
  class CLI
5
10
  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"
11
+ method_option(
12
+ 'sink-endpoint',
13
+ type: :string,
14
+ default: 'drbunix:sink'
15
+ )
16
+ desc 'rspec [FLATWARE_OPTS]', 'parallelizes rspec'
9
17
  def rspec(*rspec_args)
10
18
  jobs = RSpec.extract_jobs_from_args rspec_args, workers: workers
19
+ formatter = Flatware::RSpec::Formatters::Console.new($stdout, $stderr)
11
20
  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)
21
+ Worker.spawn count: workers, runner: RSpec, sink: options['sink-endpoint']
22
+ start_sink(jobs: jobs,
23
+ workers: workers,
24
+ formatter: formatter)
14
25
  end
15
26
  end
16
27
  end
@@ -1,50 +1,58 @@
1
1
  require 'flatware/rspec/checkpoint'
2
- require 'flatware/rspec/summary'
3
2
  require 'rspec/core/formatters/console_codes'
3
+ require 'forwardable'
4
4
 
5
5
  module Flatware
6
6
  module RSpec
7
7
  ProgressMessage = Struct.new(:progress)
8
8
 
9
9
  class Formatter
10
- attr_reader :summary, :output
10
+ extend Forwardable
11
+
12
+ def_delegators :@checkpoint, *Checkpoint::EVENTS
13
+
14
+ attr_reader :output
11
15
 
12
16
  def initialize(stdout)
13
17
  @output = stdout
14
18
  end
15
19
 
16
- def example_passed(example)
20
+ def example_passed(_example)
17
21
  send_progress :passed
18
22
  end
19
23
 
20
- def example_failed(example)
24
+ def example_failed(_example)
21
25
  send_progress :failed
22
26
  end
23
27
 
24
- def example_pending(example)
28
+ def example_pending(_example)
25
29
  send_progress :pending
26
30
  end
27
31
 
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
32
+ def start_dump(*)
33
+ @checkpoint = Checkpoint.new
34
34
  end
35
35
 
36
36
  def close(*)
37
- Sink::client.checkpoint Checkpoint.new(summary, @failure_notification)
38
- @failure_notification = nil
37
+ Sink.client.checkpoint @checkpoint
38
+ @checkpoint = nil
39
39
  end
40
40
 
41
41
  private
42
42
 
43
43
  def send_progress(status)
44
- Sink::client.progress ProgressMessage.new status
44
+ Sink.client.progress ProgressMessage.new status
45
45
  end
46
46
  end
47
47
 
48
- ::RSpec::Core::Formatters.register Formatter, :example_passed, :example_failed, :example_pending, :dump_summary, :dump_failures, :close
48
+ ::RSpec::Core::Formatters.register(
49
+ Formatter,
50
+ *Checkpoint::EVENTS,
51
+ :example_passed,
52
+ :example_failed,
53
+ :example_pending,
54
+ :start_dump,
55
+ :close
56
+ )
49
57
  end
50
58
  end
@@ -1,33 +1,69 @@
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
1
+ require 'rspec/core'
10
2
 
11
- def progress(result)
12
- formatter.send(message_for(result),nil)
13
- end
3
+ module Flatware
4
+ module RSpec
5
+ module Formatters
6
+ class Console
7
+ attr_reader :progress_formatter, :profile_formatter
14
8
 
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
9
+ def initialize(out, _err)
10
+ ::RSpec.configuration.tty = true
11
+ ::RSpec.configuration.color = true
12
+ @progress_formatter = ::RSpec::Core::Formatters::ProgressFormatter.new(out)
13
+ @profile_formatter = ::RSpec::Core::Formatters::ProfileFormatter.new(out)
14
+ end
15
+
16
+ def progress(result)
17
+ progress_formatter.public_send(message_for(result), nil)
18
+ end
19
+
20
+ def summarize(checkpoints)
21
+ return if checkpoints.empty?
22
+
23
+ result = checkpoints.reduce :+
24
+
25
+ progress_formatter.dump_failures(result)
26
+ progress_formatter.dump_summary(result.summary)
27
+ profile_formatter.dump_profile(result.profile) if result.profile
28
+ progress_formatter.dump_pending(result) if result.pending_examples.any?
29
+ end
22
30
 
23
- private
31
+ def summarize_remaining(remaining)
32
+ progress_formatter.output.puts(colorizer.wrap(<<~MESSAGE, :detail))
24
33
 
25
- def message_for(result)
26
- {
27
- passed: :example_passed,
28
- failed: :example_failed,
29
- pending: :example_pending
30
- }.fetch result.progress
34
+ The following specs weren't run:
35
+
36
+ #{spec_list(remaining)}
37
+
38
+ MESSAGE
39
+ end
40
+
41
+ private
42
+
43
+ def spec_list(remaining)
44
+ remaining
45
+ .flat_map(&:id).sort.each_with_index
46
+ .map do |example, index|
47
+ format(
48
+ '%<index>4d) %<example>s',
49
+ index: index.next,
50
+ example: example
51
+ )
52
+ end.join("\n")
53
+ end
54
+
55
+ def colorizer
56
+ ::RSpec::Core::Formatters::ConsoleCodes
57
+ end
58
+
59
+ def message_for(result)
60
+ {
61
+ passed: :example_passed,
62
+ failed: :example_failed,
63
+ pending: :example_pending
64
+ }.fetch result.progress
65
+ end
66
+ end
31
67
  end
32
68
  end
33
69
  end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Flatware
6
+ module RSpec
7
+ # groups spec files into one job per worker.
8
+ # reads from persisted example statuses, if available,
9
+ # and attempts to ballence the jobs accordingly.
10
+ class JobBuilder
11
+ extend Forwardable
12
+ attr_reader :args, :workers, :configuration
13
+
14
+ def_delegators(
15
+ :configuration,
16
+ :files_to_run,
17
+ :example_status_persistence_file_path
18
+ )
19
+
20
+ def initialize(args, workers:)
21
+ @args = args
22
+ @workers = workers
23
+
24
+ @configuration = ::RSpec.configuration
25
+ configuration.define_singleton_method(:command) { 'rspec' }
26
+
27
+ ::RSpec::Core::ConfigurationOptions.new(args).configure(@configuration)
28
+ end
29
+
30
+ def jobs
31
+ timed_files, untimed_files = timed_and_untimed_files(
32
+ sum_seconds(load_persisted_example_statuses)
33
+ )
34
+
35
+ balance_jobs(
36
+ bucket_count: [files_to_run.size, workers].min,
37
+ timed_files: timed_files,
38
+ untimed_files: untimed_files
39
+ )
40
+ end
41
+
42
+ private
43
+
44
+ def balance_jobs(bucket_count:, timed_files:, untimed_files:)
45
+ balance_by(bucket_count, timed_files, &:last)
46
+ .map { |bucket| bucket.map(&:first) }
47
+ .zip(
48
+ round_robin(bucket_count, untimed_files)
49
+ ).map(&:flatten)
50
+ .map { |files| Job.new(files, args) }
51
+ end
52
+
53
+ def timed_and_untimed_files(seconds_per_file)
54
+ files_to_run
55
+ .map(&method(:normalize_path))
56
+ .reduce([[], []]) do |(timed, untimed), file|
57
+ if (time = seconds_per_file[file])
58
+ [timed + [[file, time]], untimed]
59
+ else
60
+ [timed, untimed + [file]]
61
+ end
62
+ end
63
+ end
64
+
65
+ def normalize_path(path)
66
+ ::RSpec::Core::Metadata.relative_path(File.expand_path(path))
67
+ end
68
+
69
+ def load_persisted_example_statuses
70
+ ::RSpec::Core::ExampleStatusPersister.load_from(
71
+ example_status_persistence_file_path || ''
72
+ )
73
+ end
74
+
75
+ def sum_seconds(statuses)
76
+ statuses.select(&passing)
77
+ .map { |example| parse_example(**example) }
78
+ .reduce({}) do |times, example|
79
+ times.merge(
80
+ example.fetch(:file_name) => example.fetch(:seconds)
81
+ ) do |_, old = 0, new|
82
+ old + new
83
+ end
84
+ end
85
+ end
86
+
87
+ def passing
88
+ ->(example) { example.fetch(:status) =~ /pass/i }
89
+ end
90
+
91
+ def parse_example(example_id:, run_time:, **)
92
+ seconds = run_time.match(/\d+(\.\d+)?/).to_s.to_f
93
+ file_name = ::RSpec::Core::Example.parse_id(example_id).first
94
+ { seconds: seconds, file_name: file_name }
95
+ end
96
+
97
+ def round_robin(count, items)
98
+ Array.new(count) { [] }.tap do |groups|
99
+ items.each_with_index do |entry, i|
100
+ groups[i % count] << entry
101
+ end
102
+ end
103
+ end
104
+
105
+ def balance_by(count, items, &block)
106
+ # find the group with the smallest sum and add it there
107
+ Array.new(count) { [] }.tap do |groups|
108
+ items
109
+ .sort_by(&block)
110
+ .reverse
111
+ .each do |entry|
112
+ groups.min_by do |group|
113
+ group.map(&block).reduce(:+) || 0
114
+ end.push(entry)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,20 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ require 'flatware/rspec/marshalable/examples_notification'
5
+ require 'flatware/rspec/marshalable/profile_notification'
6
+ require 'flatware/rspec/marshalable/summary_notification'
7
+
8
+ module_function
9
+
10
+ def for_event(event)
11
+ {
12
+ dump_pending: ExamplesNotification,
13
+ dump_failures: ExamplesNotification,
14
+ dump_profile: ProfileNotification,
15
+ dump_summary: SummaryNotification
16
+ }.fetch(event)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ ##
5
+ # a subset of the rspec example interface that can traverse drb
6
+ Example = Struct.new(
7
+ *%i[
8
+ execution_result
9
+ full_description
10
+ location
11
+ location_rerun_argument
12
+ ]
13
+ ) do
14
+ def initialize(rspec_example)
15
+ super(*members.map do |attribute|
16
+ rspec_example.public_send(attribute)
17
+ end)
18
+
19
+ @metadata = rspec_example.metadata.slice(:extra_failure_lines, :shared_group_inclusion_backtrace)
20
+ end
21
+
22
+ attr_reader :metadata
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ ExampleGroup = Struct.new(:location) do
5
+ def initialize(rspec_example_group)
6
+ super(rspec_example_group.location)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'rspec/core'
2
+ require 'flatware/rspec/marshalable/example'
3
+
4
+ module Flatware
5
+ module RSpec
6
+ module Marshalable
7
+ class ExamplesNotification < ::RSpec::Core::Notifications::ExamplesNotification
8
+ Reporter = Struct.new(:examples, :failed_examples, :pending_examples) do
9
+ def self.from_rspec(reporter)
10
+ new(*members.map { |member| reporter.public_send(member).map(&Example.method(:new)) })
11
+ end
12
+
13
+ def +(other)
14
+ self.class.new(*zip(other).map { |a, b| a + b })
15
+ end
16
+ end
17
+
18
+ attr_reader :reporter
19
+
20
+ def self.from_notification(rspec_notification)
21
+ new Reporter.from_rspec(rspec_notification.instance_variable_get(:@reporter))
22
+ end
23
+
24
+ def +(other)
25
+ self.class.new reporter + other.reporter
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'flatware/rspec/marshalable/example'
2
+ require 'flatware/rspec/marshalable/example_group'
3
+
4
+ module Flatware
5
+ module RSpec
6
+ module Marshalable
7
+ class ProfileNotification < ::RSpec::Core::Notifications::ProfileNotification
8
+ attr_reader :example_groups
9
+
10
+ def +(other)
11
+ self.class.new(
12
+ duration + other.duration,
13
+ examples + other.examples,
14
+ number_of_examples,
15
+ example_groups.merge(other.example_groups)
16
+ )
17
+ end
18
+
19
+ def self.from_notification(rspec_notification)
20
+ new(
21
+ rspec_notification.duration,
22
+ rspec_notification.examples.map(&Example.method(:new)),
23
+ rspec_notification.number_of_examples,
24
+ rspec_notification.instance_variable_get(:@example_groups).transform_keys(&ExampleGroup.method(:new))
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'rspec/core/notifications'
2
+ require 'flatware/rspec/marshalable/example'
3
+
4
+ module Flatware
5
+ module RSpec
6
+ module Marshalable
7
+ class SummaryNotification < ::RSpec::Core::Notifications::SummaryNotification
8
+ def +(other)
9
+ self.class.new(*zip(other).map { |a, b| a + b })
10
+ end
11
+
12
+ def failures?
13
+ [failure_count, errors_outside_of_examples_count].any?(&:positive?)
14
+ end
15
+
16
+ def self.from_notification(summary)
17
+ serialized_examples = [
18
+ summary.examples,
19
+ summary.failed_examples,
20
+ summary.pending_examples
21
+ ].map do |examples|
22
+ examples.map(&Example.method(:new))
23
+ end
24
+
25
+ new(summary.duration, *serialized_examples, *summary.to_a[4..])
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flatware-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 2.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Dunn
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-27 00:00:00.000000000 Z
11
+ date: 2021-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flatware
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.4.0
19
+ version: 2.0.0.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.4.0
26
+ version: 2.0.0.rc1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -49,35 +49,42 @@ files:
49
49
  - LICENSE.txt
50
50
  - README.md
51
51
  - lib/flatware-rspec.rb
52
+ - lib/flatware/rspec.rb
52
53
  - lib/flatware/rspec/checkpoint.rb
53
54
  - lib/flatware/rspec/cli.rb
54
- - lib/flatware/rspec/example_notification.rb
55
- - lib/flatware/rspec/examples_notification.rb
56
55
  - lib/flatware/rspec/formatter.rb
57
56
  - lib/flatware/rspec/formatters/console.rb
58
- - lib/flatware/rspec/summary.rb
57
+ - lib/flatware/rspec/job_builder.rb
58
+ - lib/flatware/rspec/marshalable.rb
59
+ - lib/flatware/rspec/marshalable/example.rb
60
+ - lib/flatware/rspec/marshalable/example_group.rb
61
+ - lib/flatware/rspec/marshalable/examples_notification.rb
62
+ - lib/flatware/rspec/marshalable/profile_notification.rb
63
+ - lib/flatware/rspec/marshalable/summary_notification.rb
59
64
  homepage: http://github.com/briandunn/flatware
60
65
  licenses:
61
66
  - MIT
62
67
  metadata: {}
63
- post_install_message:
68
+ post_install_message:
64
69
  rdoc_options: []
65
70
  require_paths:
66
71
  - lib
67
72
  required_ruby_version: !ruby/object:Gem::Requirement
68
73
  requirements:
69
- - - "~>"
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '2.6'
77
+ - - "<"
70
78
  - !ruby/object:Gem::Version
71
- version: '2.1'
79
+ version: '3.1'
72
80
  required_rubygems_version: !ruby/object:Gem::Requirement
73
81
  requirements:
74
- - - ">="
82
+ - - ">"
75
83
  - !ruby/object:Gem::Version
76
- version: '0'
84
+ version: 1.3.1
77
85
  requirements: []
78
- rubyforge_project:
79
- rubygems_version: 2.6.8
80
- signing_key:
86
+ rubygems_version: 3.2.3
87
+ signing_key:
81
88
  specification_version: 4
82
89
  summary: A distributed rspec runner
83
90
  test_files: []
@@ -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,40 +0,0 @@
1
- require 'rspec/core/notifications'
2
- module Flatware
3
- module RSpec
4
- Summary = Struct.new(:duration, :examples, :failed_examples, :pending_examples, :load_time)
5
-
6
- class Example
7
- attr_reader :location_rerun_argument, :full_description
8
- def initialize(rspec_example)
9
- @full_description = rspec_example.full_description
10
- @location_rerun_argument = rspec_example.location_rerun_argument
11
- end
12
- end
13
-
14
- class Summary
15
- def +(other)
16
- self.class.new duration + other.duration,
17
- examples + other.examples,
18
- failed_examples + other.failed_examples,
19
- pending_examples + other.pending_examples,
20
- load_time + other.load_time
21
- end
22
-
23
- def fully_formatted
24
- ::RSpec::Core::Notifications::SummaryNotification.new(duration, examples, failed_examples, pending_examples, load_time).fully_formatted
25
- end
26
-
27
- def failure_count
28
- failed_examples.size
29
- end
30
-
31
- def self.from_notification(summary)
32
- serialized_examples = [summary.examples, summary.failed_examples, summary.pending_examples].map do |examples|
33
- examples.map(&Example.method(:new))
34
- end
35
-
36
- new summary.duration, *serialized_examples, summary.load_time
37
- end
38
- end
39
- end
40
- end