flatware-rspec 0.4.1 → 2.0.0.rc2

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: a94cdb25d8e2fd3f4dbc64b6828b9c97899afd3d
4
- data.tar.gz: 839154dc7dff646c731c65f712ee58fb228446ea
2
+ SHA256:
3
+ metadata.gz: b0c080b0bb6d068e3d65c19230aacd62d811e0e151358ffebec9f83b0e1ab0ba
4
+ data.tar.gz: '079c5ecaea4b3aa461bd7ffa6c6ee7e6ed1e592d990e4d76af5f0dcf3fa4863f'
5
5
  SHA512:
6
- metadata.gz: de9a7c19946debe902a98a281e1eab79cbff1086970722ac76e40e384c3d3de4fcd488d96a9edbd745321d18329b5b2ac6deb64aa83ef6af811358086e142208
7
- data.tar.gz: 4ee41008f1e54da5a9fe0f985288857b3843aacb6a054104dd93232a65c81a91f629e184360272f0a47537b0600db871052db0e93bffcac51959971d2bd11aad
6
+ metadata.gz: '0692ddafa90238adf85ffb8648b9b64d5971a0a4047e932063226e88391344953f847fc90093c65a37d8e233426f3f53e6f1f53fc0bb056dc2705de1704283bd'
7
+ data.tar.gz: 4ccd74df9c6954319baa3c79da326d55dcb816ef0661439cf7be3a5c4bffa1fe29c9c51c3ffbe613aabd336e0c1a22fe4bcc1ca3463f8c8111c7a4d74e4eabaf
data/README.md CHANGED
@@ -7,39 +7,6 @@
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
-
29
- If you're on macOS 10.12, use the custom hashrocket ZMQ homebrew formula.
30
-
31
- ```sh
32
- brew tap hashrocket/formulas
33
- brew install hashrocket/formulas/zeromq
34
- ```
35
-
36
- The stock homebrew version will likely work on older versions of macOS.
37
-
38
-
39
- ```sh
40
- brew install zeromq
41
- ```
42
-
43
10
  ### Flatware
44
11
 
45
12
  Add the runners you need to your Gemfile:
@@ -73,6 +40,18 @@ To run your entire suite with the default rspec options add the `flatware-rspec`
73
40
  $ flatware rspec
74
41
  ```
75
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
+
76
55
  ### Options
77
56
 
78
57
  If you'd like to limit the number of forked workers, you can pass the 'w' flag:
@@ -85,7 +64,7 @@ You can also pass most cucumber/rspec options to Flatware. For example, to run o
85
64
  features that are not tagged 'javascript', you can:
86
65
 
87
66
  ```sh
88
- $ flatware cucumber -t ~@javascript
67
+ $ flatware cucumber -t 'not @javascript'
89
68
  ```
90
69
 
91
70
  Additionally, for either cucumber or rspec you can specify a directory:
@@ -123,9 +102,31 @@ Now you are ready to rock:
123
102
  $ flatware rspec && flatware cucumber
124
103
  ```
125
104
 
126
- ## Planned Features
105
+ ### Faster Startup With ActiveRecord
127
106
 
128
- * 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.
129
130
 
130
131
  ## Design Goals
131
132
 
@@ -156,20 +157,10 @@ directory. CD there and `flatware` will be in your path so you can tinker away.
156
157
 
157
158
  ## How it works
158
159
 
159
- Flatware relies on a message passing system to enable concurrency.
160
- The main process declares a worker for each cpu in the computer. Each
161
- worker forks from the main process and is then assigned a portion of the
162
- test suite. As the worker runs the test suite it sends progress
163
- messages to the main process. These messages are collected and when
164
- the last worker is finished the main process provides a report on the
165
- 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.
166
161
 
167
162
  ## Resources
168
163
 
169
- To learn more about the messaging system that Flatware uses, take a look at the
170
- [excellent ZeroMQ guide][z].
171
-
172
- [z]: http://zguide.zeromq.org/page:all
173
164
  [a]: https://github.com/cucumber/aruba
174
165
 
175
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_rspec(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,39 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ require 'flatware/rspec/marshalable/shared_group_inclusion_backtrace'
5
+
6
+ ##
7
+ # a subset of the rspec example interface that can traverse drb
8
+ Example = Struct.new(
9
+ *%i[
10
+ execution_result
11
+ full_description
12
+ location
13
+ location_rerun_argument
14
+ ]
15
+ ) do
16
+ def initialize(rspec_example)
17
+ super(*members.map do |attribute|
18
+ rspec_example.public_send(attribute)
19
+ end)
20
+
21
+ @metadata = marshalable_metadata(rspec_example.metadata)
22
+ end
23
+
24
+ attr_reader :metadata
25
+
26
+ private
27
+
28
+ def marshalable_metadata(rspec_metadata)
29
+ rspec_metadata.slice(:extra_failure_lines).tap do |metadata|
30
+ if (backtraces = rspec_metadata[:shared_group_inclusion_backtrace])
31
+ metadata[:shared_group_inclusion_backtrace] =
32
+ backtraces.map(&SharedGroupInclusionBacktrace.method(:from_rspec))
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ 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_rspec(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_rspec(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,11 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ class SharedGroupInclusionBacktrace < ::RSpec::Core::SharedExampleGroupInclusionStackFrame
5
+ def self.from_rspec(backtrace)
6
+ new(backtrace.shared_group_name.to_s, backtrace.inclusion_location)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ 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_rspec(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.1
4
+ version: 2.0.0.rc2
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: 2018-03-16 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.1
19
+ version: 2.0.0.rc2
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.1
26
+ version: 2.0.0.rc2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -49,35 +49,43 @@ 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/shared_group_inclusion_backtrace.rb
64
+ - lib/flatware/rspec/marshalable/summary_notification.rb
59
65
  homepage: http://github.com/briandunn/flatware
60
66
  licenses:
61
67
  - MIT
62
68
  metadata: {}
63
- post_install_message:
69
+ post_install_message:
64
70
  rdoc_options: []
65
71
  require_paths:
66
72
  - lib
67
73
  required_ruby_version: !ruby/object:Gem::Requirement
68
74
  requirements:
69
- - - "~>"
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '2.6'
78
+ - - "<"
70
79
  - !ruby/object:Gem::Version
71
- version: '2.1'
80
+ version: '3.1'
72
81
  required_rubygems_version: !ruby/object:Gem::Requirement
73
82
  requirements:
74
- - - ">="
83
+ - - ">"
75
84
  - !ruby/object:Gem::Version
76
- version: '0'
85
+ version: 1.3.1
77
86
  requirements: []
78
- rubyforge_project:
79
- rubygems_version: 2.6.14
80
- signing_key:
87
+ rubygems_version: 3.2.3
88
+ signing_key:
81
89
  specification_version: 4
82
90
  summary: A distributed rspec runner
83
91
  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,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