rspec-multiprocess_runner 0.1.0 → 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4c75ed167254b35531b51f11a1dda272f3587055
4
+ data.tar.gz: c5cb965e6c3e0caaecc65f7e4236124c98d407af
5
+ SHA512:
6
+ metadata.gz: 35139e859a97c7b20cbff792b652be8172c1a70dbed65ac2277bf53a18486092cae8199820782d8747ce4830cbf927ec73f4ae6625888f2939b10a6103668c0e
7
+ data.tar.gz: 79868c598e2dfc09f467ccf9badc6382afce4b9930f6711784bdb7f336878a3921608a8fd240dbaf77fdac6818be1c99816b20687fe29755f9f4259e895738ad
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ # 0.2.0
2
+
3
+ * Terminate workers when the coordinator process is interrupted with SIGINT (^C)
4
+ or SIGTERM (`kill` with no args) (#3)
5
+ * Add per-example timeout option. When using `multirspec` or the rake tasks, it
6
+ defaults to 15 seconds (#2)
7
+ * **Breaking change**: per-file timeout is now disabled by default (#2)
8
+ * **Breaking change**: changed arguments for `Coordinator`'s constructor
9
+ * Correct signal used when requesting that stalled processes quit (use TERM
10
+ instead of QUIT)
11
+ * Stop idle processes once there's no work, instead of waiting and stopping
12
+ everything when the suite is complete
13
+
1
14
  # 0.1.0
2
15
 
3
16
  * Initial version
data/README.md CHANGED
@@ -95,16 +95,27 @@ Create a coordinator and tell it to run:
95
95
 
96
96
  worker_count = 4
97
97
  per_file_timeout = 5 * 60 # 5 minutes in seconds
98
- rspec_args = %w(--backtrace)
98
+ rspec_options = %w(--backtrace)
99
99
  files = Dir['**/*_spec.rb']
100
100
 
101
- coordinator = RSpec::MultiprocessRunner::Coordinator(worker_count, per_file_timeout, rspec_args, files)
101
+ coordinator = RSpec::MultiprocessRunner::Coordinator.new(
102
+ worker_count, files,
103
+ {
104
+ file_timeout_seconds: per_file_timeout,
105
+ rspec_options: rspec_options
106
+ }
107
+ )
102
108
  coordinator.run
103
109
 
104
110
  ## Contributing
105
111
 
106
112
  Bug reports and pull requests are welcome on GitHub at https://github.com/cdd/rspec-multiprocess_runner.
107
113
 
114
+ ### Project infrastructure
115
+
116
+ * [Source on GitHub](https://github.com/cdd/rspec-multiprocess_runner)
117
+ * [![Build Status](https://travis-ci.org/cdd/rspec-multiprocess_runner.svg?branch=master)](https://travis-ci.org/cdd/rspec-multiprocess_runner)
118
+ [Continuous Integration on Travis-CI](https://travis-ci.org/cdd/rspec-multiprocess_runner)
108
119
 
109
120
  ## License
110
121
 
data/exe/multirspec CHANGED
@@ -6,11 +6,27 @@ require 'rspec/multiprocess_runner/coordinator'
6
6
  options = RSpec::MultiprocessRunner::CommandLineOptions.new.parse(ARGV.dup)
7
7
  exit(2) unless options
8
8
 
9
- success = RSpec::MultiprocessRunner::Coordinator.new(
9
+ coordinator = RSpec::MultiprocessRunner::Coordinator.new(
10
10
  options.worker_count,
11
- options.file_timeout_seconds,
12
- options.rspec_options,
13
- options.files_to_run
14
- ).run
11
+ options.files_to_run,
12
+ {
13
+ file_timeout_seconds: options.file_timeout_seconds,
14
+ example_timeout_seconds: options.example_timeout_seconds,
15
+ rspec_options: options.rspec_options
16
+ }
17
+ )
18
+
19
+ trap("INT") do
20
+ $stderr.puts "INT happened"
21
+ coordinator.shutdown(print_summary: true)
22
+ Kernel.exit(4)
23
+ end
24
+
25
+ trap("TERM") do
26
+ coordinator.shutdown
27
+ Kernel.exit(3)
28
+ end
29
+
30
+ success = coordinator.run
15
31
 
16
32
  exit(success ? 0 : 1)
@@ -5,12 +5,13 @@ require 'pathname'
5
5
  module RSpec::MultiprocessRunner
6
6
  # @private
7
7
  class CommandLineOptions
8
- attr_accessor :worker_count, :file_timeout_seconds, :rspec_options,
9
- :explicit_files_or_directories, :pattern
8
+ attr_accessor :worker_count, :file_timeout_seconds, :example_timeout_seconds,
9
+ :rspec_options, :explicit_files_or_directories, :pattern
10
10
 
11
11
  def initialize
12
12
  self.worker_count = 3
13
- self.file_timeout_seconds = 300
13
+ self.file_timeout_seconds = nil
14
+ self.example_timeout_seconds = 15
14
15
  self.pattern = "**/*_spec.rb"
15
16
  self.rspec_options = []
16
17
  end
@@ -58,19 +59,31 @@ module RSpec::MultiprocessRunner
58
59
  @help_requested
59
60
  end
60
61
 
62
+ def print_default(default_value)
63
+ if default_value
64
+ "default: #{default_value}"
65
+ else
66
+ "none by default"
67
+ end
68
+ end
69
+
61
70
  def build_parser
62
71
  OptionParser.new do |parser|
63
72
  parser.banner = "#{File.basename $0} [options] [files or directories] [-- rspec options]"
64
73
 
65
- parser.on("-w", "--worker-count COUNT", Integer, "Number of workers to run (default: #{worker_count})") do |n|
74
+ parser.on("-w", "--worker-count COUNT", Integer, "Number of workers to run (#{print_default worker_count})") do |n|
66
75
  self.worker_count = n
67
76
  end
68
77
 
69
- parser.on("-t", "--file-timeout SECONDS", Integer, "Maximum time to allow any single file to run (default: #{file_timeout_seconds})") do |s|
78
+ parser.on("-t", "--file-timeout SECONDS", Float, "Maximum time to allow any single file to run (#{print_default file_timeout_seconds})") do |s|
70
79
  self.file_timeout_seconds = s
71
80
  end
72
81
 
73
- parser.on("-P", "--pattern PATTERN", "A glob to use to select files to run (default: #{pattern})") do |pattern|
82
+ parser.on("-T", "--example-timeout SECONDS", Float, "Maximum time to allow any single example to run (#{print_default example_timeout_seconds})") do |s|
83
+ self.example_timeout_seconds = s
84
+ end
85
+
86
+ parser.on("-P", "--pattern PATTERN", "A glob to use to select files to run (#{print_default pattern})") do |pattern|
74
87
  self.pattern = pattern
75
88
  end
76
89
 
@@ -4,13 +4,14 @@ require 'rspec/multiprocess_runner/worker'
4
4
 
5
5
  module RSpec::MultiprocessRunner
6
6
  class Coordinator
7
- def initialize(worker_count, file_timeout, rspec_options, files)
7
+ def initialize(worker_count, files, options={})
8
8
  @worker_count = worker_count
9
- @file_timeout = file_timeout
10
- @rspec_options = rspec_options
9
+ @file_timeout_seconds = options[:file_timeout_seconds]
10
+ @example_timeout_seconds = options[:example_timeout_seconds]
11
+ @rspec_options = options[:rspec_options]
11
12
  @spec_files = files
12
13
  @workers = []
13
- @deactivated_workers = []
14
+ @stopped_workers = []
14
15
  end
15
16
 
16
17
  def run
@@ -26,7 +27,23 @@ module RSpec::MultiprocessRunner
26
27
  end
27
28
 
28
29
  def failed?
29
- !@deactivated_workers.empty? || any_example_failed?
30
+ !failed_workers.empty? || !@spec_files.empty? || any_example_failed?
31
+ end
32
+
33
+ def shutdown(options={})
34
+ if @shutting_down
35
+ # Immediately kill the workers if shutdown is requested again
36
+ end_workers_in_parallel(@workers.dup, :kill_now)
37
+ else
38
+ @shutting_down = true
39
+ print "Shutting down #{pluralize(@workers.size, "worker")} …" if options[:print_summary]
40
+ # end_workers_in_parallel modifies @workers, so dup before sending in
41
+ end_workers_in_parallel(@workers.dup, :shutdown_now)
42
+ if options[:print_summary]
43
+ puts " done"
44
+ print_summary
45
+ end
46
+ end
30
47
  end
31
48
 
32
49
  private
@@ -40,28 +57,46 @@ module RSpec::MultiprocessRunner
40
57
  act_on_available_worker_messages(0.3)
41
58
  reap_stalled_workers
42
59
  start_missing_workers
60
+ quit_idle_unnecessary_workers
43
61
  break unless @workers.detect(&:working?)
44
62
  end
45
63
  end
46
64
 
47
65
  def quit_all_workers
48
- quit_threads = @workers.map do |worker|
66
+ # quit_workers modifies @workers, so dup before sending in
67
+ quit_workers(@workers.dup)
68
+ end
69
+
70
+ def end_workers_in_parallel(some_workers, end_method)
71
+ end_threads = some_workers.map do |worker|
72
+ # This method is not threadsafe because it updates instance variables.
73
+ # But it's fine to run it outside of the thread because it doesn't
74
+ # block.
75
+ mark_worker_as_stopped(worker)
49
76
  Thread.new do
50
- worker.quit
51
- worker.wait_until_quit
77
+ worker.send(end_method)
52
78
  end
53
79
  end
54
- quit_threads.each(&:join)
80
+ end_threads.each(&:join)
81
+ end
82
+
83
+ def quit_workers(some_workers)
84
+ end_workers_in_parallel(some_workers, :quit_when_idle_and_wait_for_quit)
55
85
  end
56
86
 
57
87
  def work_left_to_do?
58
88
  !@spec_files.empty?
59
89
  end
60
90
 
91
+ def failed_workers
92
+ @stopped_workers.select { |w| w.deactivation_reason }
93
+ end
94
+
61
95
  def act_on_available_worker_messages(timeout)
62
96
  while (select_result = IO.select(worker_sockets, nil, nil, timeout))
63
97
  select_result.first.each do |readable_socket|
64
98
  ready_worker = @workers.detect { |worker| worker.socket == readable_socket }
99
+ next unless ready_worker # Worker is already gone
65
100
  worker_status = ready_worker.receive_and_act_on_message_from_worker
66
101
  if worker_status == :dead
67
102
  reap_one_worker(ready_worker, "died")
@@ -74,8 +109,12 @@ module RSpec::MultiprocessRunner
74
109
 
75
110
  def reap_one_worker(worker, reason)
76
111
  worker.reap
77
- @deactivated_workers << worker
78
112
  worker.deactivation_reason = reason
113
+ mark_worker_as_stopped(worker)
114
+ end
115
+
116
+ def mark_worker_as_stopped(worker)
117
+ @stopped_workers << worker
79
118
  @workers.reject! { |w| w == worker }
80
119
  end
81
120
 
@@ -92,7 +131,12 @@ module RSpec::MultiprocessRunner
92
131
  def create_and_start_worker_if_necessary(n)
93
132
  if work_left_to_do?
94
133
  $stderr.puts "(Re)starting worker #{n}"
95
- new_worker = Worker.new(n, @file_timeout, @rspec_options)
134
+ new_worker = Worker.new(
135
+ n,
136
+ file_timeout_seconds: @file_timeout_seconds,
137
+ example_timeout_seconds: @example_timeout_seconds,
138
+ rspec_options: @rspec_options
139
+ )
96
140
  @workers << new_worker
97
141
  new_worker.start
98
142
  new_worker.run_file(@spec_files.shift)
@@ -109,11 +153,19 @@ module RSpec::MultiprocessRunner
109
153
  end
110
154
  end
111
155
 
156
+ def quit_idle_unnecessary_workers
157
+ unless work_left_to_do?
158
+ idle_workers = @workers.reject(&:working?)
159
+ quit_workers(idle_workers)
160
+ end
161
+ end
162
+
112
163
  def print_summary
113
164
  elapsed = Time.now - @start_time
114
165
  by_status_and_time = combine_example_results.each_with_object({}) do |result, idx|
115
166
  (idx[result.status] ||= []) << result
116
167
  end
168
+ print_skipped_files_details
117
169
  print_pending_example_details(by_status_and_time["pending"])
118
170
  print_failed_example_details(by_status_and_time["failed"])
119
171
  print_failed_process_details
@@ -124,11 +176,20 @@ module RSpec::MultiprocessRunner
124
176
  end
125
177
 
126
178
  def combine_example_results
127
- (@workers + @deactivated_workers).flat_map(&:example_results).sort_by { |r| r.time_finished }
179
+ (@workers + @stopped_workers).flat_map(&:example_results).sort_by { |r| r.time_finished }
128
180
  end
129
181
 
130
182
  def any_example_failed?
131
- (@workers + @deactivated_workers).detect { |w| w.example_results.detect { |r| r.status == "failed" } }
183
+ (@workers + @stopped_workers).detect { |w| w.example_results.detect { |r| r.status == "failed" } }
184
+ end
185
+
186
+ def print_skipped_files_details
187
+ return if @spec_files.empty?
188
+ puts
189
+ puts "Skipped files:"
190
+ @spec_files.each do |spec_file|
191
+ puts " - #{spec_file}"
192
+ end
132
193
  end
133
194
 
134
195
  def print_pending_example_details(pending_example_results)
@@ -161,21 +222,23 @@ module RSpec::MultiprocessRunner
161
222
  example_count = by_status_and_time.map { |status, results| results.size }.inject(0) { |sum, ct| sum + ct }
162
223
  failure_count = by_status_and_time["failed"] ? by_status_and_time["failed"].size : 0
163
224
  pending_count = by_status_and_time["pending"] ? by_status_and_time["pending"].size : 0
164
- process_failure_count = @deactivated_workers.size
225
+ process_failure_count = failed_workers.size
226
+ skipped_count = @spec_files.size
165
227
 
166
228
  # Copied from RSpec
167
229
  summary = pluralize(example_count, "example")
168
230
  summary << ", " << pluralize(failure_count, "failure")
169
231
  summary << ", #{pending_count} pending" if pending_count > 0
170
232
  summary << ", " << pluralize(process_failure_count, "failed proc") if process_failure_count > 0
233
+ summary << ", " << pluralize(skipped_count, "skipped file") if skipped_count > 0
171
234
  puts summary
172
235
  end
173
236
 
174
237
  def print_failed_process_details
175
- return if @deactivated_workers.empty?
238
+ return if failed_workers.empty?
176
239
  puts
177
240
  puts "Failed processes:"
178
- @deactivated_workers.each do |worker|
241
+ failed_workers.each do |worker|
179
242
  puts " - #{worker.pid} (env #{worker.environment_number}) #{worker.deactivation_reason} on #{worker.current_file}"
180
243
  end
181
244
  end
@@ -30,9 +30,13 @@ module RSpec::MultiprocessRunner
30
30
  attr_accessor :worker_count
31
31
 
32
32
  # The maximum number of seconds to allow a single spec file to run
33
- # before killing it. Defaults to 300.
33
+ # before killing it. Defaults to disabled.
34
34
  attr_accessor :file_timeout_seconds
35
35
 
36
+ # The maximum number of seconds to allow a single example to run
37
+ # before killing it. Defaults to 15.
38
+ attr_accessor :example_timeout_seconds
39
+
36
40
  # Whether or not to fail Rake when an error occurs (typically when
37
41
  # examples fail). Defaults to `true`.
38
42
  attr_accessor :fail_on_error
@@ -94,6 +98,9 @@ module RSpec::MultiprocessRunner
94
98
  if worker_count
95
99
  cmd_parts << '--worker-count' << worker_count.to_s
96
100
  end
101
+ if example_timeout_seconds
102
+ cmd_parts << '--example-timeout' << example_timeout_seconds.to_s
103
+ end
97
104
  if file_timeout_seconds
98
105
  cmd_parts << '--file-timeout' << file_timeout_seconds.to_s
99
106
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module MultiprocessRunner
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -29,11 +29,12 @@ module RSpec::MultiprocessRunner
29
29
  STATUS_EXAMPLE_COMPLETE = "example_complete"
30
30
  STATUS_RUN_COMPLETE = "run_complete"
31
31
 
32
- def initialize(environment_number, file_timeout, rspec_arguments=[])
32
+ def initialize(environment_number, options)
33
33
  @environment_number = environment_number
34
34
  @worker_socket, @coordinator_socket = Socket.pair(:UNIX, :STREAM)
35
- @rspec_arguments = rspec_arguments + ["--format", ReportingFormatter.to_s]
36
- @file_timeout = file_timeout
35
+ @rspec_arguments = (options[:rspec_options] || []) + ["--format", ReportingFormatter.to_s]
36
+ @example_timeout_seconds = options[:example_timeout_seconds]
37
+ @file_timeout_seconds = options[:file_timeout_seconds]
37
38
  @example_results = []
38
39
 
39
40
  # Use a single configuration and world across all individual runs
@@ -65,6 +66,17 @@ module RSpec::MultiprocessRunner
65
66
  @coordinator_socket.close
66
67
  @pid = Process.pid
67
68
  ENV["TEST_ENV_NUMBER"] = environment_number.to_s
69
+
70
+ # reset TERM handler so that
71
+ # - the coordinator's version (if any) is not executed twice
72
+ # - it actually terminates the process, instead of doing the ruby
73
+ # default (throw an exception, which gets caught by RSpec)
74
+ Kernel.trap("TERM", "SYSTEM_DEFAULT")
75
+ # rely on the coordinator to handle INT
76
+ Kernel.trap("INT", "IGNORE")
77
+ # prevent RSpec from trapping INT, also
78
+ ::RSpec::Core::Runner.instance_eval { def self.trap_interrupt; end }
79
+
68
80
  set_process_name
69
81
  run_loop
70
82
  end
@@ -84,18 +96,15 @@ module RSpec::MultiprocessRunner
84
96
 
85
97
  public
86
98
 
87
- def quit
99
+ def quit_when_idle_and_wait_for_quit
88
100
  send_message_to_worker(command: COMMAND_QUIT)
89
- end
90
-
91
- def wait_until_quit
92
101
  Process.wait(self.pid)
93
102
  end
94
103
 
95
104
  def run_file(filename)
96
105
  send_message_to_worker(command: COMMAND_RUN_FILE, filename: filename)
97
106
  @current_file = filename
98
- @current_file_started_at = Time.now
107
+ @current_file_started_at = @current_example_started_at = Time.now
99
108
  end
100
109
 
101
110
  def working?
@@ -103,21 +112,28 @@ module RSpec::MultiprocessRunner
103
112
  end
104
113
 
105
114
  def stalled?
106
- working? && (Time.now - @current_file_started_at > @file_timeout)
115
+ file_stalled =
116
+ if @file_timeout_seconds
117
+ working? && (Time.now - @current_file_started_at > @file_timeout_seconds)
118
+ end
119
+ example_stalled =
120
+ if @example_timeout_seconds
121
+ working? && (Time.now - @current_example_started_at > @example_timeout_seconds)
122
+ end
123
+ file_stalled || example_stalled
124
+ end
125
+
126
+ def shutdown_now
127
+ terminate_then_kill(5)
128
+ end
129
+
130
+ def kill_now
131
+ Process.kill(:KILL, pid)
132
+ Process.detach(pid)
107
133
  end
108
134
 
109
135
  def reap
110
- begin
111
- Timeout.timeout(4) do
112
- $stderr.puts "Reaping troubled process #{environment_number} (#{pid}; #{@current_file}) with QUIT"
113
- Process.kill(:QUIT, pid)
114
- Process.wait(pid)
115
- end
116
- rescue Timeout::Error
117
- $stderr.puts "Reaping troubled process #{environment_number} (#{pid}) with KILL"
118
- Process.kill(:KILL, pid)
119
- Process.wait(pid)
120
- end
136
+ terminate_then_kill(3, "Reaping troubled process #{environment_number} (#{pid}; #{@current_file})")
121
137
  end
122
138
 
123
139
  def receive_and_act_on_message_from_worker
@@ -126,6 +142,19 @@ module RSpec::MultiprocessRunner
126
142
 
127
143
  private
128
144
 
145
+ def terminate_then_kill(timeout, message=nil)
146
+ begin
147
+ Timeout.timeout(timeout) do
148
+ $stderr.puts "#{message} with TERM" if message
149
+ Process.kill(:TERM, pid)
150
+ Process.wait(pid)
151
+ end
152
+ rescue Timeout::Error
153
+ $stderr.puts "#{message} with KILL" if message
154
+ kill_now
155
+ end
156
+ end
157
+
129
158
  def receive_message_from_worker
130
159
  receive_message(@coordinator_socket)
131
160
  end
@@ -136,6 +165,7 @@ module RSpec::MultiprocessRunner
136
165
  when STATUS_RUN_COMPLETE
137
166
  @current_file = nil
138
167
  @current_file_started_at = nil
168
+ @current_example_started_at = nil
139
169
  when STATUS_EXAMPLE_COMPLETE
140
170
  example_results << ExampleResult.new(message_hash)
141
171
  suffix =
@@ -149,6 +179,7 @@ module RSpec::MultiprocessRunner
149
179
  suffix += "\n#{message_hash["details"]}"
150
180
  end
151
181
  $stdout.puts "#{environment_number} (#{pid}): #{message_hash["description"]}#{suffix}"
182
+ @current_example_started_at = Time.now
152
183
  else
153
184
  $stderr.puts "Received unsupported status #{message_hash["status"].inspect} in worker #{pid}"
154
185
  end
@@ -194,16 +225,25 @@ module RSpec::MultiprocessRunner
194
225
  end
195
226
  end
196
227
 
228
+ def handle_closed_coordinator_socket
229
+ # when the coordinator socket is closed, there's nothing more to do
230
+ exit
231
+ end
232
+
197
233
  def receive_message_from_coordinator(socket)
198
234
  receive_message(socket)
199
235
  end
200
236
 
201
237
  def send_message_to_coordinator(message_hash)
202
- send_message(@worker_socket, message_hash)
238
+ begin
239
+ send_message(@worker_socket, message_hash)
240
+ rescue Errno::EPIPE
241
+ handle_closed_coordinator_socket
242
+ end
203
243
  end
204
244
 
205
245
  def act_on_message_from_coordinator(message_hash)
206
- return unless message_hash # ignore EOF
246
+ return handle_closed_coordinator_socket unless message_hash # EOF
207
247
  case message_hash["command"]
208
248
  when "quit"
209
249
  exit
metadata CHANGED
@@ -1,84 +1,75 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-multiprocess_runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Rhett Sutphin
9
8
  autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2015-12-15 00:00:00.000000000 Z
11
+ date: 2015-12-24 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rspec-core
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
19
  version: '2.0'
22
- - - <
20
+ - - "<"
23
21
  - !ruby/object:Gem::Version
24
22
  version: 2.99.0
25
23
  type: :runtime
26
24
  prerelease: false
27
25
  version_requirements: !ruby/object:Gem::Requirement
28
- none: false
29
26
  requirements:
30
- - - ~>
27
+ - - "~>"
31
28
  - !ruby/object:Gem::Version
32
29
  version: '2.0'
33
- - - <
30
+ - - "<"
34
31
  - !ruby/object:Gem::Version
35
32
  version: 2.99.0
36
33
  - !ruby/object:Gem::Dependency
37
34
  name: bundler
38
35
  requirement: !ruby/object:Gem::Requirement
39
- none: false
40
36
  requirements:
41
- - - ~>
37
+ - - "~>"
42
38
  - !ruby/object:Gem::Version
43
39
  version: '1.10'
44
40
  type: :development
45
41
  prerelease: false
46
42
  version_requirements: !ruby/object:Gem::Requirement
47
- none: false
48
43
  requirements:
49
- - - ~>
44
+ - - "~>"
50
45
  - !ruby/object:Gem::Version
51
46
  version: '1.10'
52
47
  - !ruby/object:Gem::Dependency
53
48
  name: rake
54
49
  requirement: !ruby/object:Gem::Requirement
55
- none: false
56
50
  requirements:
57
- - - ~>
51
+ - - "~>"
58
52
  - !ruby/object:Gem::Version
59
53
  version: '10.0'
60
54
  type: :development
61
55
  prerelease: false
62
56
  version_requirements: !ruby/object:Gem::Requirement
63
- none: false
64
57
  requirements:
65
- - - ~>
58
+ - - "~>"
66
59
  - !ruby/object:Gem::Version
67
60
  version: '10.0'
68
61
  - !ruby/object:Gem::Dependency
69
62
  name: rspec
70
63
  requirement: !ruby/object:Gem::Requirement
71
- none: false
72
64
  requirements:
73
- - - ! '>='
65
+ - - ">="
74
66
  - !ruby/object:Gem::Version
75
67
  version: '0'
76
68
  type: :development
77
69
  prerelease: false
78
70
  version_requirements: !ruby/object:Gem::Requirement
79
- none: false
80
71
  requirements:
81
- - - ! '>='
72
+ - - ">="
82
73
  - !ruby/object:Gem::Version
83
74
  version: '0'
84
75
  description:
@@ -89,9 +80,9 @@ executables:
89
80
  extensions: []
90
81
  extra_rdoc_files: []
91
82
  files:
92
- - .gitignore
93
- - .rspec
94
- - .travis.yml
83
+ - ".gitignore"
84
+ - ".rspec"
85
+ - ".travis.yml"
95
86
  - CHANGELOG.md
96
87
  - Gemfile
97
88
  - LICENSE.txt
@@ -112,32 +103,25 @@ files:
112
103
  homepage: https://github.com/cdd/rspec-multiprocess_runner
113
104
  licenses:
114
105
  - MIT
106
+ metadata: {}
115
107
  post_install_message:
116
108
  rdoc_options: []
117
109
  require_paths:
118
110
  - lib
119
111
  required_ruby_version: !ruby/object:Gem::Requirement
120
- none: false
121
112
  requirements:
122
- - - ! '>='
113
+ - - ">="
123
114
  - !ruby/object:Gem::Version
124
115
  version: '0'
125
- segments:
126
- - 0
127
- hash: -1609184745420465239
128
116
  required_rubygems_version: !ruby/object:Gem::Requirement
129
- none: false
130
117
  requirements:
131
- - - ! '>='
118
+ - - ">="
132
119
  - !ruby/object:Gem::Version
133
120
  version: '0'
134
- segments:
135
- - 0
136
- hash: -1609184745420465239
137
121
  requirements: []
138
122
  rubyforge_project:
139
- rubygems_version: 1.8.25
123
+ rubygems_version: 2.4.5.1
140
124
  signing_key:
141
- specification_version: 3
125
+ specification_version: 4
142
126
  summary: A runner for RSpec 2 that uses multiple processes to execute specs in parallel
143
127
  test_files: []