flatware-rspec 1.0.0 → 2.0.0.rc3

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: 5222039b04efa4cdd03b8a1fd99755ce7b5a796d
4
- data.tar.gz: 1c4ff5266e4f837ebed84de24b33cc2a86e98138
2
+ SHA256:
3
+ metadata.gz: 8c7ed50452251f91a122a1870cf0034e737bd9e245a2effca63876d352b6dfaa
4
+ data.tar.gz: 204dc1af97debe14947500af0bf54da88f5b5d6a8e95eda320b94ef81315be7f
5
5
  SHA512:
6
- metadata.gz: 373b77233a023fb74f712c4ece31b9690b9cfa495577fb8b681d227a58a7ed312af32e0185694c852231ca68ecbf92c9cda750a14fc8d154faffdf3caac44508
7
- data.tar.gz: a485e4f6d3da78f2802a143a416c0ea79211a0c5e23ad6c7b9ea68afbe916c2e4073ed35fa50b566b51f06e2ea3b2ef7e21b9d3b7d68590e6afec4c9293568d4
6
+ metadata.gz: 19970bba746199d0514531e83ceacdfd48a89ef3f0531ff6a9d42c5b89927e107fd429c11594f9a3e784c03014ee12911be9217188150609582e78091c4e578d
7
+ data.tar.gz: 8b1284ae49367edfa7ba0e5e06f95fe7df5523f9091340f0f5e08fba3e9a6332dcb6a342d075990ed78ed5fe723ae25b4604336a5cb60e16fc95918d8807cd45
data/README.md CHANGED
@@ -1,45 +1,10 @@
1
- # Flatware [![Build Status][travis-badge]][travis] [![Code Climate][code-climate-badge]][code-climate]
1
+ # Flatware [![Code Climate][code-climate-badge]][code-climate]
2
2
 
3
- [travis-badge]: https://travis-ci.org/briandunn/flatware.svg?branch=master
4
- [travis]: http://travis-ci.org/briandunn/flatware
5
3
  [code-climate-badge]: https://codeclimate.com/github/briandunn/flatware.png
6
4
  [code-climate]: https://codeclimate.com/github/briandunn/flatware
7
5
 
8
6
  Flatware parallelizes your test suite to significantly reduce test time.
9
7
 
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
8
  ### Flatware
44
9
 
45
10
  Add the runners you need to your Gemfile:
@@ -73,6 +38,18 @@ To run your entire suite with the default rspec options add the `flatware-rspec`
73
38
  $ flatware rspec
74
39
  ```
75
40
 
41
+ The rspec runner can balance worker loads, making your suite even faster.
42
+
43
+ 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).
44
+
45
+ For this to work the configuration option must be loaded before any specs are run. The `.rspec` file is one way to achive this:
46
+
47
+ --require spec_helper
48
+
49
+ 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](
50
+ #faster-startup-with-activerecord
51
+ ).
52
+
76
53
  ### Options
77
54
 
78
55
  If you'd like to limit the number of forked workers, you can pass the 'w' flag:
@@ -123,9 +100,31 @@ Now you are ready to rock:
123
100
  $ flatware rspec && flatware cucumber
124
101
  ```
125
102
 
126
- ## Planned Features
103
+ ### Faster Startup With ActiveRecord
127
104
 
128
- * Use heuristics to run your slowest tests first
105
+ Flatware has a couple lifecycle callbacks that you can use to avoid booting your app
106
+ over again on every core. One way to take advantage of this via a `spec/flatware_helper.rb` file like so:
107
+
108
+ ```ruby
109
+ Flatware.configure do |conf|
110
+ conf.before_fork do
111
+ require 'rails_helper'
112
+
113
+ ActiveRecord::Base.connection.disconnect!
114
+ end
115
+
116
+ conf.after_fork do |test_env_number|
117
+ config = ActiveRecord::Base.connection_config
118
+
119
+ ActiveRecord::Base.establish_connection(
120
+ config.merge(
121
+ database: config.fetch(:database) + test_env_number.to_s
122
+ )
123
+ )
124
+ end
125
+ end
126
+ ```
127
+ Now when I run `bundle exec flatware rspec -r ./spec/flatware_helper` My app only boots once, rather than once per core.
129
128
 
130
129
  ## Design Goals
131
130
 
@@ -156,20 +155,10 @@ directory. CD there and `flatware` will be in your path so you can tinker away.
156
155
 
157
156
  ## How it works
158
157
 
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.
158
+ 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
159
 
167
160
  ## Resources
168
161
 
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
162
  [a]: https://github.com/cucumber/aruba
174
163
 
175
164
  ## Contributing to Flatware
@@ -0,0 +1,33 @@
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 runner
19
+ ::RSpec::Core::Runner.tap do |runner|
20
+ def runner.trap_interrupt() end
21
+ end
22
+ end
23
+
24
+ def run(job, _options = [])
25
+ ::RSpec.configuration.deprecation_stream = StringIO.new
26
+ ::RSpec.configuration.output_stream = StringIO.new
27
+ ::RSpec.configuration.add_formatter(Flatware::RSpec::Formatter)
28
+
29
+ runner.run(Array(job), $stderr, $stdout)
30
+ ::RSpec.reset # prevents duplicate runs
31
+ end
32
+ end
33
+ end
@@ -1,28 +1,74 @@
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[
19
+ deprecation
20
+ dump_failures
21
+ dump_pending
22
+ dump_profile
23
+ dump_summary
24
+ ].freeze
25
+
26
+ attr_reader :events
27
+
28
+ def initialize(events = {})
29
+ @events = { deprecation: [] }.merge(events)
30
+ end
31
+
32
+ def self.listen_for(event, &block)
33
+ define_method(event) do |notification|
34
+ instance_exec(Marshalable.for_event(event).from_rspec(notification), &block)
35
+ end
36
+ end
7
37
 
8
- def initialize(summary, failures_notification)
9
- @summary, @failures_notification = summary, ExamplesNotification.new(failures_notification.failure_notifications)
38
+ (EVENTS - %i[deprecation]).each do |event|
39
+ listen_for(event) do |notification|
40
+ events[event] = notification
41
+ end
42
+ end
43
+
44
+ listen_for(:deprecation) do |deprecation|
45
+ events[:deprecation] << deprecation
10
46
  end
11
47
 
12
48
  def +(other)
13
- self.class.new summary + other.summary, failures_notification + other.failures_notification
49
+ self.class.new(events.merge(other.events) { |_, event, other_event| event + other_event })
50
+ end
51
+
52
+ def summary
53
+ events.fetch(:dump_summary)
14
54
  end
15
55
 
16
- def failures?
17
- summary.failure_count > 0
56
+ def deprecations
57
+ events.fetch(:deprecation)
18
58
  end
19
59
 
20
- def failure_notifications
21
- failures_notification.failure_notifications
60
+ def profile
61
+ events[:dump_profile]
62
+ end
63
+
64
+ private
65
+
66
+ def failures_notification
67
+ events.fetch(:dump_failures)
22
68
  end
23
69
 
24
- def fully_formatted_failed_examples(*args)
25
- failures_notification.fully_formatted_failed_examples(*args)
70
+ def pending_notification
71
+ events.fetch(:dump_pending)
26
72
  end
27
73
  end
28
74
  end
@@ -1,16 +1,30 @@
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:flatware-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
+
20
+ formatter = Flatware::RSpec::Formatters::Console.new(
21
+ ::RSpec.configuration.output_stream,
22
+ deprecation_stream: ::RSpec.configuration.deprecation_stream
23
+ )
24
+
11
25
  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)
26
+ Worker.spawn count: workers, runner: RSpec, sink: options['sink-endpoint']
27
+ start_sink(jobs: jobs, workers: workers, formatter: formatter)
14
28
  end
15
29
  end
16
30
  end
@@ -1,50 +1,57 @@
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
34
- end
35
-
36
32
  def close(*)
37
- Sink::client.checkpoint Checkpoint.new(summary, @failure_notification)
38
- @failure_notification = nil
33
+ Sink.client.checkpoint checkpoint
34
+ @checkpoint = nil
39
35
  end
40
36
 
41
37
  private
42
38
 
43
39
  def send_progress(status)
44
- Sink::client.progress ProgressMessage.new status
40
+ Sink.client.progress ProgressMessage.new status
45
41
  end
46
- end
47
42
 
48
- ::RSpec::Core::Formatters.register Formatter, :example_passed, :example_failed, :example_pending, :dump_summary, :dump_failures, :close
43
+ def checkpoint
44
+ @checkpoint ||= Checkpoint.new
45
+ end
46
+
47
+ ::RSpec::Core::Formatters.register(
48
+ self,
49
+ *Checkpoint::EVENTS,
50
+ :example_passed,
51
+ :example_failed,
52
+ :example_pending,
53
+ :close
54
+ )
55
+ end
49
56
  end
50
57
  end
@@ -1,33 +1,84 @@
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, :out, :deprecation_stream
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, deprecation_stream: StringIO.new)
10
+ @out = out
11
+ @deprecation_stream = deprecation_stream
12
+ ::RSpec.configuration.backtrace_exclusion_patterns += [%r{/lib/flatware/worker}, %r{/lib/flatware/rspec}]
13
+ @progress_formatter = ::RSpec::Core::Formatters::ProgressFormatter.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_pending(result) if result.pending_examples.any?
26
+ progress_formatter.dump_failures(result)
27
+ dump_deprecations(result.deprecations)
28
+ dump_profile(result.profile) if result.profile
29
+ progress_formatter.dump_summary(result.summary)
30
+ end
31
+
32
+ def summarize_remaining(remaining)
33
+ progress_formatter.output.puts(colorizer.wrap(<<~MESSAGE, :detail))
22
34
 
23
- private
35
+ The following specs weren't run:
24
36
 
25
- def message_for(result)
26
- {
27
- passed: :example_passed,
28
- failed: :example_failed,
29
- pending: :example_pending
30
- }.fetch result.progress
37
+ #{spec_list(remaining)}
38
+
39
+ MESSAGE
40
+ end
41
+
42
+ private
43
+
44
+ def dump_deprecations(deprecations)
45
+ formatter = ::RSpec::Core::Formatters::DeprecationFormatter.new(
46
+ deprecation_stream,
47
+ out
48
+ )
49
+
50
+ deprecations.each(&formatter.method(:deprecation))
51
+ formatter.deprecation_summary(nil)
52
+ end
53
+
54
+ def dump_profile(profile)
55
+ ::RSpec::Core::Formatters::ProfileFormatter.new(out).dump_profile(profile)
56
+ end
57
+
58
+ def spec_list(remaining)
59
+ remaining
60
+ .flat_map(&:id).sort.each_with_index
61
+ .map do |example, index|
62
+ format(
63
+ '%<index>4d) %<example>s',
64
+ index: index.next,
65
+ example: example
66
+ )
67
+ end.join("\n")
68
+ end
69
+
70
+ def colorizer
71
+ ::RSpec::Core::Formatters::ConsoleCodes
72
+ end
73
+
74
+ def message_for(result)
75
+ {
76
+ passed: :example_passed,
77
+ failed: :example_failed,
78
+ pending: :example_pending
79
+ }.fetch result.progress
80
+ end
81
+ end
31
82
  end
32
83
  end
33
84
  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,22 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ require 'flatware/rspec/marshalable/deprecation_notification'
5
+ require 'flatware/rspec/marshalable/examples_notification'
6
+ require 'flatware/rspec/marshalable/profile_notification'
7
+ require 'flatware/rspec/marshalable/summary_notification'
8
+
9
+ module_function
10
+
11
+ def for_event(event)
12
+ {
13
+ dump_pending: ExamplesNotification,
14
+ dump_failures: ExamplesNotification,
15
+ dump_profile: ProfileNotification,
16
+ dump_summary: SummaryNotification,
17
+ deprecation: DeprecationNotification
18
+ }.fetch(event)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ class DeprecationNotification < ::RSpec::Core::Notifications::SeedNotification
5
+ def self.from_rspec(notification)
6
+ notification
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ require 'flatware/rspec/marshalable/execution_result'
5
+ require 'flatware/rspec/marshalable/shared_group_inclusion_backtrace'
6
+
7
+ ##
8
+ # a subset of the rspec example interface that can traverse drb
9
+ Example = Struct.new(
10
+ *%i[
11
+ execution_result
12
+ full_description
13
+ location
14
+ location_rerun_argument
15
+ ]
16
+ ) do
17
+ def initialize(rspec_example)
18
+ super(marshalable_execution_result(rspec_example.execution_result), *members[1..].map do |attribute|
19
+ rspec_example.public_send(attribute)
20
+ end)
21
+
22
+ @metadata = marshalable_metadata(rspec_example.metadata)
23
+ end
24
+
25
+ attr_reader :metadata
26
+
27
+ private
28
+
29
+ def marshalable_execution_result(execution_result)
30
+ ExecutionResult.from_rspec(execution_result)
31
+ end
32
+
33
+ def marshalable_metadata(rspec_metadata)
34
+ rspec_metadata.slice(:extra_failure_lines).tap do |metadata|
35
+ if (backtraces = rspec_metadata[:shared_group_inclusion_backtrace])
36
+ metadata[:shared_group_inclusion_backtrace] =
37
+ backtraces.map(&SharedGroupInclusionBacktrace.method(:from_rspec))
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ 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,18 @@
1
+ module Flatware
2
+ module RSpec
3
+ module Marshalable
4
+ require 'flatware/serialized_exception'
5
+ class ExecutionResult < ::RSpec::Core::Example::ExecutionResult
6
+ def self.from_rspec(result)
7
+ new.tap do |marshalable|
8
+ marshalable.exception = result.exception && SerializedException.from(result.exception)
9
+
10
+ %i[finished_at run_time started_at status].each do |member|
11
+ marshalable.public_send(:"#{member}=", result.public_send(member))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ 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: 1.0.0
4
+ version: 2.0.0.rc3
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-08-28 00:00:00.000000000 Z
11
+ date: 2021-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flatware
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 1.0.0
19
+ version: 2.0.0.rc3
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: 1.0.0
26
+ version: 2.0.0.rc3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '3.4'
33
+ version: '3.6'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '3.4'
40
+ version: '3.6'
41
41
  description: A distributed rspec runner
42
42
  email: brian@hashrocket.com
43
43
  executables: []
@@ -49,35 +49,45 @@ 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/deprecation_notification.rb
60
+ - lib/flatware/rspec/marshalable/example.rb
61
+ - lib/flatware/rspec/marshalable/example_group.rb
62
+ - lib/flatware/rspec/marshalable/examples_notification.rb
63
+ - lib/flatware/rspec/marshalable/execution_result.rb
64
+ - lib/flatware/rspec/marshalable/profile_notification.rb
65
+ - lib/flatware/rspec/marshalable/shared_group_inclusion_backtrace.rb
66
+ - lib/flatware/rspec/marshalable/summary_notification.rb
59
67
  homepage: http://github.com/briandunn/flatware
60
68
  licenses:
61
69
  - MIT
62
70
  metadata: {}
63
- post_install_message:
71
+ post_install_message:
64
72
  rdoc_options: []
65
73
  require_paths:
66
74
  - lib
67
75
  required_ruby_version: !ruby/object:Gem::Requirement
68
76
  requirements:
69
- - - "~>"
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '2.6'
80
+ - - "<"
70
81
  - !ruby/object:Gem::Version
71
- version: '2.1'
82
+ version: '3.1'
72
83
  required_rubygems_version: !ruby/object:Gem::Requirement
73
84
  requirements:
74
- - - ">="
85
+ - - ">"
75
86
  - !ruby/object:Gem::Version
76
- version: '0'
87
+ version: 1.3.1
77
88
  requirements: []
78
- rubyforge_project:
79
- rubygems_version: 2.6.12
80
- signing_key:
89
+ rubygems_version: 3.2.3
90
+ signing_key:
81
91
  specification_version: 4
82
92
  summary: A distributed rspec runner
83
93
  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