rspec-core 3.7.0 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +80 -2
  5. data/README.md +16 -16
  6. data/lib/rspec/core.rb +1 -0
  7. data/lib/rspec/core/bisect/coordinator.rb +26 -30
  8. data/lib/rspec/core/bisect/example_minimizer.rb +12 -8
  9. data/lib/rspec/core/bisect/fork_runner.rb +134 -0
  10. data/lib/rspec/core/bisect/server.rb +5 -14
  11. data/lib/rspec/core/bisect/{runner.rb → shell_command.rb} +27 -70
  12. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  13. data/lib/rspec/core/bisect/utilities.rb +58 -0
  14. data/lib/rspec/core/configuration.rb +219 -62
  15. data/lib/rspec/core/configuration_options.rb +41 -4
  16. data/lib/rspec/core/did_you_mean.rb +46 -0
  17. data/lib/rspec/core/example.rb +8 -5
  18. data/lib/rspec/core/example_group.rb +8 -3
  19. data/lib/rspec/core/formatters.rb +13 -6
  20. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  21. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  22. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
  23. data/lib/rspec/core/formatters/deprecation_formatter.rb +3 -1
  24. data/lib/rspec/core/formatters/documentation_formatter.rb +35 -3
  25. data/lib/rspec/core/formatters/exception_presenter.rb +13 -1
  26. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  27. data/lib/rspec/core/formatters/html_printer.rb +0 -2
  28. data/lib/rspec/core/formatters/protocol.rb +17 -17
  29. data/lib/rspec/core/formatters/syntax_highlighter.rb +19 -19
  30. data/lib/rspec/core/hooks.rb +15 -9
  31. data/lib/rspec/core/invocations.rb +8 -6
  32. data/lib/rspec/core/memoized_helpers.rb +33 -14
  33. data/lib/rspec/core/metadata.rb +2 -3
  34. data/lib/rspec/core/option_parser.rb +8 -0
  35. data/lib/rspec/core/profiler.rb +3 -1
  36. data/lib/rspec/core/rake_task.rb +21 -1
  37. data/lib/rspec/core/reporter.rb +11 -6
  38. data/lib/rspec/core/runner.rb +25 -14
  39. data/lib/rspec/core/shared_example_group.rb +2 -4
  40. data/lib/rspec/core/shell_escape.rb +2 -2
  41. data/lib/rspec/core/version.rb +1 -1
  42. data/lib/rspec/core/world.rb +11 -0
  43. metadata +22 -13
  44. metadata.gz.sig +0 -0
  45. data/lib/rspec/core/formatters/bisect_formatter.rb +0 -69
@@ -1,13 +1,11 @@
1
1
  require 'drb/drb'
2
2
  require 'drb/acl'
3
+ RSpec::Support.require_rspec_core "bisect/utilities"
3
4
 
4
5
  module RSpec
5
6
  module Core
6
7
  # @private
7
8
  module Bisect
8
- # @private
9
- BisectFailedError = Class.new(StandardError)
10
-
11
9
  # @private
12
10
  # A DRb server that receives run results from a separate RSpec process
13
11
  # started by the bisect process.
@@ -27,7 +25,7 @@ module RSpec
27
25
  run_output = yield
28
26
 
29
27
  if latest_run_results.nil? || latest_run_results.all_example_ids.empty?
30
- raise_bisect_failed(run_output)
28
+ raise BisectFailedError.for_failed_spec_run(run_output)
31
29
  end
32
30
 
33
31
  latest_run_results
@@ -35,7 +33,7 @@ module RSpec
35
33
 
36
34
  def start
37
35
  # Only allow remote DRb requests from this machine.
38
- DRb.install_acl ACL.new(%w[ deny all allow localhost allow 127.0.0.1 ])
36
+ DRb.install_acl ACL.new(%w[ deny all allow localhost allow 127.0.0.1 allow ::1 ])
39
37
 
40
38
  # We pass `nil` as the first arg to allow it to pick a DRb port.
41
39
  @drb = DRb.start_service(nil, self)
@@ -49,21 +47,14 @@ module RSpec
49
47
  @drb_port ||= Integer(@drb.uri[/\d+$/])
50
48
  end
51
49
 
52
- # Fetched via DRb by the BisectFormatter to determine when to abort.
50
+ # Fetched via DRb by the BisectDRbFormatter to determine when to abort.
53
51
  attr_accessor :expected_failures
54
52
 
55
- # Set via DRb by the BisectFormatter with the results of the run.
53
+ # Set via DRb by the BisectDRbFormatter with the results of the run.
56
54
  attr_accessor :latest_run_results
57
55
 
58
56
  # Fetched via DRb to tell clients which files to run
59
57
  attr_accessor :files_or_directories_to_run
60
-
61
- private
62
-
63
- def raise_bisect_failed(run_output)
64
- raise BisectFailedError, "Failed to get results from the spec " \
65
- "run. Spec run output:\n\n#{run_output}"
66
- end
67
58
  end
68
59
  end
69
60
  end
@@ -1,36 +1,30 @@
1
1
  RSpec::Support.require_rspec_core "shell_escape"
2
- require 'open3'
3
2
  require 'shellwords'
4
3
 
5
4
  module RSpec
6
5
  module Core
7
6
  module Bisect
8
- # Provides an API to run the suite for a set of locations, using
9
- # the given bisect server to capture the results.
7
+ # Provides an API to generate shell commands to run the suite for a
8
+ # set of locations, using the given bisect server to capture the results.
10
9
  # @private
11
- class Runner
10
+ class ShellCommand
12
11
  attr_reader :original_cli_args
13
12
 
14
- def initialize(server, original_cli_args)
15
- @server = server
13
+ def initialize(original_cli_args)
16
14
  @original_cli_args = original_cli_args.reject { |arg| arg.start_with?("--bisect") }
17
15
  end
18
16
 
19
- def run(locations)
20
- run_locations(locations, original_results.failed_example_ids)
21
- end
22
-
23
- def command_for(locations)
17
+ def command_for(locations, server)
24
18
  parts = []
25
19
 
26
20
  parts << RUBY << load_path
27
21
  parts << open3_safe_escape(RSpec::Core.path_to_executable)
28
22
 
29
- parts << "--format" << "bisect"
30
- parts << "--drb-port" << @server.drb_port
23
+ parts << "--format" << "bisect-drb"
24
+ parts << "--drb-port" << server.drb_port
31
25
 
32
- parts.concat reusable_cli_options
33
- parts.concat locations.map { |l| open3_safe_escape(l) }
26
+ parts.concat(reusable_cli_options)
27
+ parts.concat(locations.map { |l| open3_safe_escape(l) })
34
28
 
35
29
  parts.join(" ")
36
30
  end
@@ -46,8 +40,24 @@ module RSpec
46
40
  parts.join(" ")
47
41
  end
48
42
 
49
- def original_results
50
- @original_results ||= run_locations(original_locations)
43
+ def original_locations
44
+ parsed_original_cli_options.fetch(:files_or_directories_to_run)
45
+ end
46
+
47
+ def bisect_environment_hash
48
+ if ENV.key?('SPEC_OPTS')
49
+ { 'SPEC_OPTS' => spec_opts_without_bisect }
50
+ else
51
+ {}
52
+ end
53
+ end
54
+
55
+ def spec_opts_without_bisect
56
+ Shellwords.join(
57
+ Shellwords.split(ENV.fetch('SPEC_OPTS', '')).reject do |arg|
58
+ arg =~ /^--bisect/
59
+ end
60
+ )
51
61
  end
52
62
 
53
63
  private
@@ -63,61 +73,12 @@ module RSpec
63
73
  alias open3_safe_escape escape
64
74
  end
65
75
 
66
- def run_locations(*capture_args)
67
- @server.capture_run_results(*capture_args) do
68
- run_command command_for([])
69
- end
70
- end
71
-
72
- # `Open3.capture2e` does not work on JRuby:
73
- # https://github.com/jruby/jruby/issues/2766
74
- if Open3.respond_to?(:capture2e) && !RSpec::Support::Ruby.jruby?
75
- def run_command(cmd)
76
- Open3.capture2e(bisect_environment_hash, cmd).first
77
- end
78
- else # for 1.8.7
79
- # :nocov:
80
- def run_command(cmd)
81
- out = err = nil
82
-
83
- original_spec_opts = ENV['SPEC_OPTS']
84
- ENV['SPEC_OPTS'] = spec_opts_without_bisect
85
-
86
- Open3.popen3(cmd) do |_, stdout, stderr|
87
- # Reading the streams blocks until the process is complete
88
- out = stdout.read
89
- err = stderr.read
90
- end
91
-
92
- "Stdout:\n#{out}\n\nStderr:\n#{err}"
93
- ensure
94
- ENV['SPEC_OPTS'] = original_spec_opts
95
- end
96
- # :nocov:
97
- end
98
-
99
- def bisect_environment_hash
100
- if ENV.key?('SPEC_OPTS')
101
- { 'SPEC_OPTS' => spec_opts_without_bisect }
102
- else
103
- {}
104
- end
105
- end
106
-
107
76
  def environment_repro_parts
108
77
  bisect_environment_hash.map do |k, v|
109
78
  %Q(#{k}="#{v}")
110
79
  end
111
80
  end
112
81
 
113
- def spec_opts_without_bisect
114
- Shellwords.join(
115
- Shellwords.split(ENV.fetch('SPEC_OPTS', '')).reject do |arg|
116
- arg =~ /^--bisect/
117
- end
118
- )
119
- end
120
-
121
82
  def reusable_cli_options
122
83
  @reusable_cli_options ||= begin
123
84
  opts = original_cli_args_without_locations
@@ -146,10 +107,6 @@ module RSpec
146
107
  @parsed_original_cli_options ||= Parser.parse(@original_cli_args)
147
108
  end
148
109
 
149
- def original_locations
150
- parsed_original_cli_options.fetch(:files_or_directories_to_run)
151
- end
152
-
153
110
  def load_path
154
111
  @load_path ||= "-I#{$LOAD_PATH.map { |p| open3_safe_escape(p) }.join(':')}"
155
112
  end
@@ -0,0 +1,73 @@
1
+ require 'open3'
2
+ RSpec::Support.require_rspec_core "bisect/server"
3
+
4
+ module RSpec
5
+ module Core
6
+ module Bisect
7
+ # Provides an API to run the suite for a set of locations, using
8
+ # the given bisect server to capture the results.
9
+ #
10
+ # Sets of specs are run by shelling out.
11
+ # @private
12
+ class ShellRunner
13
+ def self.start(shell_command, _spec_runner)
14
+ Server.run do |server|
15
+ yield new(server, shell_command)
16
+ end
17
+ end
18
+
19
+ def self.name
20
+ :shell
21
+ end
22
+
23
+ def initialize(server, shell_command)
24
+ @server = server
25
+ @shell_command = shell_command
26
+ end
27
+
28
+ def run(locations)
29
+ run_locations(locations, original_results.failed_example_ids)
30
+ end
31
+
32
+ def original_results
33
+ @original_results ||= run_locations(@shell_command.original_locations)
34
+ end
35
+
36
+ private
37
+
38
+ def run_locations(*capture_args)
39
+ @server.capture_run_results(*capture_args) do
40
+ run_command @shell_command.command_for([], @server)
41
+ end
42
+ end
43
+
44
+ # `Open3.capture2e` does not work on JRuby:
45
+ # https://github.com/jruby/jruby/issues/2766
46
+ if Open3.respond_to?(:capture2e) && !RSpec::Support::Ruby.jruby?
47
+ def run_command(cmd)
48
+ Open3.capture2e(@shell_command.bisect_environment_hash, cmd).first
49
+ end
50
+ else # for 1.8.7
51
+ # :nocov:
52
+ def run_command(cmd)
53
+ out = err = nil
54
+
55
+ original_spec_opts = ENV['SPEC_OPTS']
56
+ ENV['SPEC_OPTS'] = @shell_command.spec_opts_without_bisect
57
+
58
+ Open3.popen3(cmd) do |_, stdout, stderr|
59
+ # Reading the streams blocks until the process is complete
60
+ out = stdout.read
61
+ err = stderr.read
62
+ end
63
+
64
+ "Stdout:\n#{out}\n\nStderr:\n#{err}"
65
+ ensure
66
+ ENV['SPEC_OPTS'] = original_spec_opts
67
+ end
68
+ # :nocov:
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,58 @@
1
+ module RSpec
2
+ module Core
3
+ module Bisect
4
+ # @private
5
+ ExampleSetDescriptor = Struct.new(:all_example_ids, :failed_example_ids)
6
+
7
+ # @private
8
+ class BisectFailedError < StandardError
9
+ def self.for_failed_spec_run(spec_output)
10
+ new("Failed to get results from the spec run. Spec run output:\n\n" +
11
+ spec_output)
12
+ end
13
+ end
14
+
15
+ # Wraps a `formatter` providing a simple means to notify it in place
16
+ # of an `RSpec::Core::Reporter`, without involving configuration in
17
+ # any way.
18
+ # @private
19
+ class Notifier
20
+ def initialize(formatter)
21
+ @formatter = formatter
22
+ end
23
+
24
+ def publish(event, *args)
25
+ return unless @formatter.respond_to?(event)
26
+ notification = Notifications::CustomNotification.for(*args)
27
+ @formatter.__send__(event, notification)
28
+ end
29
+ end
30
+
31
+ # Wraps a pipe to support sending objects between a child and
32
+ # parent process.
33
+ # @private
34
+ class Channel
35
+ def initialize
36
+ @read_io, @write_io = IO.pipe
37
+ end
38
+
39
+ def send(message)
40
+ packet = Marshal.dump(message)
41
+ @write_io.write("#{packet.bytesize}\n#{packet}")
42
+ end
43
+
44
+ # rubocop:disable Security/MarshalLoad
45
+ def receive
46
+ packet_size = Integer(@read_io.gets)
47
+ Marshal.load(@read_io.read(packet_size))
48
+ end
49
+ # rubocop:enable Security/MarshalLoad
50
+
51
+ def close
52
+ @read_io.close
53
+ @write_io.close
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -9,10 +9,24 @@ module RSpec
9
9
 
10
10
  # Stores runtime configuration information.
11
11
  #
12
- # Configuration options are loaded from `~/.rspec`, `.rspec`,
13
- # `.rspec-local`, command line switches, and the `SPEC_OPTS` environment
14
- # variable (listed in lowest to highest precedence; for example, an option
15
- # in `~/.rspec` can be overridden by an option in `.rspec-local`).
12
+ # Configuration options are loaded from multiple files and joined together
13
+ # with command-line switches and the `SPEC_OPTS` environment variable.
14
+ #
15
+ # Precedence order (where later entries overwrite earlier entries on
16
+ # conflicts):
17
+ #
18
+ # * Global (`$XDG_CONFIG_HOME/rspec/options`, or `~/.rspec` if it does
19
+ # not exist)
20
+ # * Project-specific (`./.rspec`)
21
+ # * Local (`./.rspec-local`)
22
+ # * Command-line options
23
+ # * `SPEC_OPTS`
24
+ #
25
+ # For example, an option set in the local file will override an option set
26
+ # in your global file.
27
+ #
28
+ # The global, project-specific and local files can all be overridden with a
29
+ # separate custom file using the --options command-line parameter.
16
30
  #
17
31
  # @example Standard settings
18
32
  # RSpec.configure do |c|
@@ -89,7 +103,6 @@ module RSpec
89
103
 
90
104
  # @macro [attach] add_setting
91
105
  # @!attribute [rw] $1
92
- # @!method $1=(value)
93
106
  #
94
107
  # @macro [attach] define_reader
95
108
  # @!attribute [r] $1
@@ -101,6 +114,7 @@ module RSpec
101
114
  #
102
115
  # @note Other scripts invoking `rspec` indirectly will ignore this
103
116
  # setting.
117
+ # @return [String]
104
118
  add_read_only_setting :default_path
105
119
  def default_path=(path)
106
120
  project_source_dirs << path
@@ -110,6 +124,7 @@ module RSpec
110
124
  # @macro add_setting
111
125
  # Run examples over DRb (default: `false`). RSpec doesn't supply the DRb
112
126
  # server, but you can use tools like spork.
127
+ # @return [Boolean]
113
128
  add_setting :drb
114
129
 
115
130
  # @macro add_setting
@@ -122,6 +137,7 @@ module RSpec
122
137
 
123
138
  # Indicates if the DSL has been exposed off of modules and `main`.
124
139
  # Default: true
140
+ # @return [Boolean]
125
141
  def expose_dsl_globally?
126
142
  Core::DSL.exposed_globally?
127
143
  end
@@ -186,10 +202,33 @@ module RSpec
186
202
  only_failures? && !example_status_persistence_file_path
187
203
  end
188
204
 
189
- # @macro add_setting
205
+ # @macro define_reader
190
206
  # If specified, indicates the number of failures required before cleaning
191
- # up and exit (default: `nil`).
192
- add_setting :fail_fast
207
+ # up and exit (default: `nil`). Can also be `true` to fail and exit on first
208
+ # failure
209
+ define_reader :fail_fast
210
+
211
+ # @see fail_fast
212
+ def fail_fast=(value)
213
+ case value
214
+ when true, 'true'
215
+ @fail_fast = true
216
+ when false, 'false', 0
217
+ @fail_fast = false
218
+ when nil
219
+ @fail_fast = nil
220
+ else
221
+ @fail_fast = value.to_i
222
+
223
+ if value.to_i == 0
224
+ # TODO: in RSpec 4, consider raising an error here.
225
+ RSpec.warning "Cannot set `RSpec.configuration.fail_fast`" \
226
+ " to `#{value.inspect}`. Only `true`, `false`, `nil` and integers" \
227
+ " are valid values."
228
+ @fail_fast = true
229
+ end
230
+ end
231
+ end
193
232
 
194
233
  # @macro add_setting
195
234
  # Prints the formatter output of your suite without running any
@@ -198,24 +237,29 @@ module RSpec
198
237
 
199
238
  # @macro add_setting
200
239
  # The exit code to return if there are any failures (default: 1).
240
+ # @return [Integer]
201
241
  add_setting :failure_exit_code
202
242
 
203
243
  # @macro add_setting
204
244
  # Whether or not to fail when there are no RSpec examples (default: false).
245
+ # @return [Boolean]
205
246
  add_setting :fail_if_no_examples
206
247
 
207
248
  # @macro define_reader
208
249
  # Indicates files configured to be required.
250
+ # @return [Array<String>]
209
251
  define_reader :requires
210
252
 
211
253
  # @macro define_reader
212
254
  # Returns dirs that have been prepended to the load path by the `-I`
213
255
  # command line option.
256
+ # @return [Array<String>]
214
257
  define_reader :libs
215
258
 
216
259
  # @macro add_setting
217
260
  # Determines where RSpec will send its output.
218
261
  # Default: `$stdout`.
262
+ # @return [IO, String]
219
263
  define_reader :output_stream
220
264
 
221
265
  # Set the output stream for reporter.
@@ -234,6 +278,7 @@ module RSpec
234
278
 
235
279
  # @macro define_reader
236
280
  # Load files matching this pattern (default: `'**{,/*/**}/*_spec.rb'`).
281
+ # @return [String]
237
282
  define_reader :pattern
238
283
 
239
284
  # Set pattern to match files to load.
@@ -244,6 +289,7 @@ module RSpec
244
289
 
245
290
  # @macro define_reader
246
291
  # Exclude files matching this pattern.
292
+ # @return [String]
247
293
  define_reader :exclude_pattern
248
294
 
249
295
  # Set pattern to match files to exclude.
@@ -265,6 +311,7 @@ module RSpec
265
311
  # @macro add_setting
266
312
  # Report the times for the slowest examples (default: `false`).
267
313
  # Use this to specify the number of examples to include in the profile.
314
+ # @return [Boolean]
268
315
  add_setting :profile_examples
269
316
 
270
317
  # @macro add_setting
@@ -275,55 +322,56 @@ module RSpec
275
322
  add_setting :run_all_when_everything_filtered
276
323
 
277
324
  # @macro add_setting
278
- # Color to use to indicate success.
279
- # @param color [Symbol] defaults to `:green` but can be set to one of the
280
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
281
- # :cyan]`
325
+ # Color to use to indicate success. Defaults to `:green` but can be set
326
+ # to one of the following: `[:black, :white, :red, :green, :yellow,
327
+ # :blue, :magenta, :cyan]`
328
+ # @return [Symbol]
282
329
  add_setting :success_color
283
330
 
284
331
  # @macro add_setting
285
- # Color to use to print pending examples.
286
- # @param color [Symbol] defaults to `:yellow` but can be set to one of the
287
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
288
- # :cyan]`
332
+ # Color to use to print pending examples. Defaults to `:yellow` but can
333
+ # be set to one of the following: `[:black, :white, :red, :green,
334
+ # :yellow, :blue, :magenta, :cyan]`
335
+ # @return [Symbol]
289
336
  add_setting :pending_color
290
337
 
291
338
  # @macro add_setting
292
- # Color to use to indicate failure.
293
- # @param color [Symbol] defaults to `:red` but can be set to one of the
294
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
295
- # :cyan]`
339
+ # Color to use to indicate failure. Defaults to `:red` but can be set to
340
+ # one of the following: `[:black, :white, :red, :green, :yellow, :blue,
341
+ # :magenta, :cyan]`
342
+ # @return [Symbol]
296
343
  add_setting :failure_color
297
344
 
298
345
  # @macro add_setting
299
- # The default output color.
300
- # @param color [Symbol] defaults to `:white` but can be set to one of the
301
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
302
- # :cyan]`
346
+ # The default output color. Defaults to `:white` but can be set to one of
347
+ # the following: `[:black, :white, :red, :green, :yellow, :blue,
348
+ # :magenta, :cyan]`
349
+ # @return [Symbol]
303
350
  add_setting :default_color
304
351
 
305
352
  # @macro add_setting
306
- # Color used when a pending example is fixed.
307
- # @param color [Symbol] defaults to `:blue` but can be set to one of the
308
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
309
- # :cyan]`
353
+ # Color used when a pending example is fixed. Defaults to `:blue` but can
354
+ # be set to one of the following: `[:black, :white, :red, :green,
355
+ # :yellow, :blue, :magenta, :cyan]`
356
+ # @return [Symbol]
310
357
  add_setting :fixed_color
311
358
 
312
359
  # @macro add_setting
313
- # Color used to print details.
314
- # @param color [Symbol] defaults to `:cyan` but can be set to one of the
315
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
316
- # :cyan]`
360
+ # Color used to print details. Defaults to `:cyan` but can be set to one
361
+ # of the following: `[:black, :white, :red, :green, :yellow, :blue,
362
+ # :magenta, :cyan]`
363
+ # @return [Symbol]
317
364
  add_setting :detail_color
318
365
 
319
366
  # @macro add_setting
320
367
  # Don't print filter info i.e. "Run options: include {:focus=>true}"
321
368
  # (default `false`).
369
+ # return [Boolean]
322
370
  add_setting :silence_filter_announcements
323
371
 
324
- # Deprecated. This config option was added in RSpec 2 to pave the way
325
- # for this being the default behavior in RSpec 3. Now this option is
326
- # a no-op.
372
+ # @deprecated This config option was added in RSpec 2 to pave the way
373
+ # for this being the default behavior in RSpec 3. Now this option is
374
+ # a no-op.
327
375
  def treat_symbols_as_metadata_keys_with_true_values=(_value)
328
376
  RSpec.deprecate(
329
377
  "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values=",
@@ -387,18 +435,53 @@ module RSpec
387
435
  end
388
436
 
389
437
  # Record the start time of the spec suite to measure load time.
438
+ # return [Time]
390
439
  add_setting :start_time
391
440
 
392
441
  # @macro add_setting
393
442
  # Use threadsafe options where available.
394
443
  # Currently this will place a mutex around memoized values such as let blocks.
444
+ # return [Boolean]
395
445
  add_setting :threadsafe
396
446
 
397
447
  # @macro add_setting
398
448
  # Maximum count of failed source lines to display in the failure reports.
399
449
  # (default `10`).
450
+ # return [Integer]
400
451
  add_setting :max_displayed_failure_line_count
401
452
 
453
+ # Determines which bisect runner implementation gets used to run subsets
454
+ # of the suite during a bisection. Your choices are:
455
+ #
456
+ # - `:shell`: Performs a spec run by shelling out, booting RSpec and your
457
+ # application environment each time. This runner is the most widely
458
+ # compatible runner, but is not as fast. On platforms that do not
459
+ # support forking, this is the default.
460
+ # - `:fork`: Pre-boots RSpec and your application environment in a parent
461
+ # process, and then forks a child process for each spec run. This runner
462
+ # tends to be significantly faster than the `:shell` runner but cannot
463
+ # be used in some situations. On platforms that support forking, this
464
+ # is the default. If you use this runner, you should ensure that all
465
+ # of your one-time setup logic goes in a `before(:suite)` hook instead
466
+ # of getting run at the top-level of a file loaded by `--require`.
467
+ #
468
+ # @note This option will only be used by `--bisect` if you set it in a file
469
+ # loaded via `--require`.
470
+ #
471
+ # @return [Symbol]
472
+ attr_reader :bisect_runner
473
+ def bisect_runner=(value)
474
+ if @bisect_runner_class && value != @bisect_runner
475
+ raise "`config.bisect_runner = #{value.inspect}` can no longer take " \
476
+ "effect as the #{@bisect_runner.inspect} bisect runnner is already " \
477
+ "in use. This config setting must be set in a file loaded by a " \
478
+ "`--require` option (passed at the CLI or in a `.rspec` file) for " \
479
+ "it to have any effect."
480
+ end
481
+
482
+ @bisect_runner = value
483
+ end
484
+
402
485
  # @private
403
486
  # @deprecated Use {#color_mode} = :on, instead of {#color} with {#tty}
404
487
  add_setting :tty
@@ -411,8 +494,7 @@ module RSpec
411
494
  # @private
412
495
  attr_reader :backtrace_formatter, :ordering_manager, :loaded_spec_files
413
496
 
414
- # rubocop:disable Metrics/AbcSize
415
- # rubocop:disable Metrics/MethodLength
497
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
416
498
 
417
499
  # Build an object to store runtime configuration options and set defaults
418
500
  def initialize
@@ -424,6 +506,9 @@ module RSpec
424
506
  @extend_modules = FilterableItemRepository::QueryOptimized.new(:any?)
425
507
  @prepend_modules = FilterableItemRepository::QueryOptimized.new(:any?)
426
508
 
509
+ @bisect_runner = RSpec::Support::RubyFeatures.fork_supported? ? :fork : :shell
510
+ @bisect_runner_class = nil
511
+
427
512
  @before_suite_hooks = []
428
513
  @after_suite_hooks = []
429
514
 
@@ -467,8 +552,7 @@ module RSpec
467
552
 
468
553
  define_built_in_hooks
469
554
  end
470
- # rubocop:enable Metrics/MethodLength
471
- # rubocop:enable Metrics/AbcSize
555
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
472
556
 
473
557
  # @private
474
558
  #
@@ -549,6 +633,7 @@ module RSpec
549
633
  end
550
634
 
551
635
  # Returns the configured mock framework adapter module.
636
+ # @return [Symbol]
552
637
  def mock_framework
553
638
  if @mock_framework.nil?
554
639
  begin
@@ -576,12 +661,13 @@ module RSpec
576
661
  # To override this behaviour and display a full backtrace, use
577
662
  # `--backtrace` on the command line, in a `.rspec` file, or in the
578
663
  # `rspec_options` attribute of RSpec's rake task.
664
+ # @return [Array<Regexp>]
579
665
  def backtrace_exclusion_patterns
580
666
  @backtrace_formatter.exclusion_patterns
581
667
  end
582
668
 
583
669
  # Set regular expressions used to exclude lines in backtrace.
584
- # @param patterns [Regexp] set the backtrace exlusion pattern
670
+ # @param patterns [Array<Regexp>] set backtrace_formatter exlusion_patterns
585
671
  def backtrace_exclusion_patterns=(patterns)
586
672
  @backtrace_formatter.exclusion_patterns = patterns
587
673
  end
@@ -594,12 +680,13 @@ module RSpec
594
680
  # will be included.
595
681
  #
596
682
  # You can modify the list via the getter, or replace it with the setter.
683
+ # @return [Array<Regexp>]
597
684
  def backtrace_inclusion_patterns
598
685
  @backtrace_formatter.inclusion_patterns
599
686
  end
600
687
 
601
688
  # Set regular expressions used to include lines in backtrace.
602
- # @attr patterns [Regexp] set backtrace_formatter inclusion_patterns
689
+ # @attr patterns [Array<Regexp>] set backtrace_formatter inclusion_patterns
603
690
  def backtrace_inclusion_patterns=(patterns)
604
691
  @backtrace_formatter.inclusion_patterns = patterns
605
692
  end
@@ -862,11 +949,11 @@ module RSpec
862
949
  # @overload add_formatter(formatter)
863
950
  # @overload add_formatter(formatter, output)
864
951
  #
865
- # @param formatter [Class, String] formatter to use. Can be any of the
952
+ # @param formatter [Class, String, Object] formatter to use. Can be any of the
866
953
  # string values supported from the CLI (`p`/`progress`,
867
- # `d`/`doc`/`documentation`, `h`/`html`, or `j`/`json`) or any
954
+ # `d`/`doc`/`documentation`, `h`/`html`, or `j`/`json`), any
868
955
  # class that implements the formatter protocol and has registered
869
- # itself with RSpec as a formatter.
956
+ # itself with RSpec as a formatter, or a formatter instance.
870
957
  # @param output [String, IO] where the formatter will write its output.
871
958
  # Can be an IO object or a string path to a file. If not provided,
872
959
  # the configured `output_stream` (`$stdout`, by default) will be used.
@@ -1042,7 +1129,7 @@ module RSpec
1042
1129
  #
1043
1130
  # # This lets you do this:
1044
1131
  #
1045
- # describe Thing do
1132
+ # RSpec.describe Thing do
1046
1133
  # pending "does something" do
1047
1134
  # thing = Thing.new
1048
1135
  # end
@@ -1050,7 +1137,7 @@ module RSpec
1050
1137
  #
1051
1138
  # # ... which is the equivalent of
1052
1139
  #
1053
- # describe Thing do
1140
+ # RSpec.describe Thing do
1054
1141
  # it "does something", :pending => true do
1055
1142
  # thing = Thing.new
1056
1143
  # end
@@ -1103,7 +1190,7 @@ module RSpec
1103
1190
  #
1104
1191
  # # allows the user to include a shared example group like:
1105
1192
  #
1106
- # describe Entity do
1193
+ # RSpec.describe Entity do
1107
1194
  # it_has_behavior 'sortability' do
1108
1195
  # let(:sortable) { Entity.new }
1109
1196
  # end
@@ -1452,7 +1539,7 @@ module RSpec
1452
1539
  def requires=(paths)
1453
1540
  directories = ['lib', default_path].select { |p| File.directory? p }
1454
1541
  RSpec::Core::RubyProject.add_to_load_path(*directories)
1455
- paths.each { |path| require path }
1542
+ paths.each { |path| load_file_handling_errors(:require, path) }
1456
1543
  @requires += paths
1457
1544
  end
1458
1545
 
@@ -1493,7 +1580,7 @@ module RSpec
1493
1580
 
1494
1581
  files_to_run.uniq.each do |f|
1495
1582
  file = File.expand_path(f)
1496
- load_spec_file_handling_errors(file)
1583
+ load_file_handling_errors(:load, file)
1497
1584
  loaded_spec_files << file
1498
1585
  end
1499
1586
 
@@ -1521,8 +1608,6 @@ module RSpec
1521
1608
  end
1522
1609
 
1523
1610
  # @private
1524
- # @macro [attach] delegate_to_ordering_manager
1525
- # @!method $1
1526
1611
  def self.delegate_to_ordering_manager(*methods)
1527
1612
  methods.each do |method|
1528
1613
  define_method method do |*args, &block|
@@ -1531,12 +1616,12 @@ module RSpec
1531
1616
  end
1532
1617
  end
1533
1618
 
1534
- # @macro delegate_to_ordering_manager
1619
+ # @!method seed=(value)
1535
1620
  #
1536
1621
  # Sets the seed value and sets the default global ordering to random.
1537
1622
  delegate_to_ordering_manager :seed=
1538
1623
 
1539
- # @macro delegate_to_ordering_manager
1624
+ # @!method seed
1540
1625
  # Seed for random ordering (default: generated randomly each run).
1541
1626
  #
1542
1627
  # When you run specs with `--order random`, RSpec generates a random seed
@@ -1550,7 +1635,7 @@ module RSpec
1550
1635
  # don't accidentally leave the seed encoded.
1551
1636
  delegate_to_ordering_manager :seed
1552
1637
 
1553
- # @macro delegate_to_ordering_manager
1638
+ # @!method order=(value)
1554
1639
  #
1555
1640
  # Sets the default global ordering strategy. By default this can be one
1556
1641
  # of `:defined`, `:random`, but is customizable through the
@@ -1560,7 +1645,8 @@ module RSpec
1560
1645
  # @see #register_ordering
1561
1646
  delegate_to_ordering_manager :order=
1562
1647
 
1563
- # @macro delegate_to_ordering_manager
1648
+ # @!method register_ordering(name)
1649
+ #
1564
1650
  # Registers a named ordering strategy that can later be
1565
1651
  # used to order an example group's subgroups by adding
1566
1652
  # `:order => <name>` metadata to the example group.
@@ -1655,7 +1741,7 @@ module RSpec
1655
1741
  # rspec.expose_current_running_example_as :example
1656
1742
  # end
1657
1743
  #
1658
- # describe MyClass do
1744
+ # RSpec.describe MyClass do
1659
1745
  # before do
1660
1746
  # # `example` can be used here because of the above config.
1661
1747
  # do_something if example.metadata[:type] == "foo"
@@ -1773,7 +1859,7 @@ module RSpec
1773
1859
  # require 'support/db'
1774
1860
  # end
1775
1861
  # end
1776
- def when_first_matching_example_defined(*filters, &block)
1862
+ def when_first_matching_example_defined(*filters)
1777
1863
  specified_meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
1778
1864
 
1779
1865
  callback = lambda do |example_or_group_meta|
@@ -1784,7 +1870,7 @@ module RSpec
1784
1870
  # Ensure the callback only fires once.
1785
1871
  @derived_metadata_blocks.delete(callback, specified_meta)
1786
1872
 
1787
- block.call
1873
+ yield
1788
1874
  end
1789
1875
 
1790
1876
  @derived_metadata_blocks.append(callback, specified_meta)
@@ -1792,9 +1878,28 @@ module RSpec
1792
1878
 
1793
1879
  # @private
1794
1880
  def apply_derived_metadata_to(metadata)
1795
- @derived_metadata_blocks.items_for(metadata).each do |block|
1796
- block.call(metadata)
1881
+ already_run_blocks = Set.new
1882
+
1883
+ # We loop and attempt to re-apply metadata blocks to support cascades
1884
+ # (e.g. where a derived bit of metadata triggers the application of
1885
+ # another piece of derived metadata, etc)
1886
+ #
1887
+ # We limit our looping to 200 times as a way to detect infinitely recursing derived metadata blocks.
1888
+ # It's hard to imagine a valid use case for a derived metadata cascade greater than 200 iterations.
1889
+ 200.times do
1890
+ return if @derived_metadata_blocks.items_for(metadata).all? do |block|
1891
+ already_run_blocks.include?(block).tap do |skip_block|
1892
+ block.call(metadata) unless skip_block
1893
+ already_run_blocks << block
1894
+ end
1895
+ end
1797
1896
  end
1897
+
1898
+ # If we got here, then `@derived_metadata_blocks.items_for(metadata).all?` never returned
1899
+ # `true` above and we treat this as an attempt to recurse infinitely. It's better to fail
1900
+ # with a clear # error than hang indefinitely, which is what would happen if we didn't limit
1901
+ # the looping above.
1902
+ raise SystemStackError, "Attempted to recursively derive metadata indefinitely."
1798
1903
  end
1799
1904
 
1800
1905
  # Defines a `before` hook. See {Hooks#before} for full docs.
@@ -1810,6 +1915,12 @@ module RSpec
1810
1915
  handle_suite_hook(scope, meta) do
1811
1916
  @before_suite_hooks << Hooks::BeforeHook.new(block, {})
1812
1917
  end || begin
1918
+ # defeat Ruby 2.5 lazy proc allocation to ensure
1919
+ # the methods below are passed the same proc instances
1920
+ # so `Hook` equality is preserved. For more info, see:
1921
+ # https://bugs.ruby-lang.org/issues/14045#note-5
1922
+ block.__id__
1923
+
1813
1924
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.before(scope, *meta, &block) }
1814
1925
  super(scope, *meta, &block)
1815
1926
  end
@@ -1833,6 +1944,12 @@ module RSpec
1833
1944
  handle_suite_hook(scope, meta) do
1834
1945
  @before_suite_hooks.unshift Hooks::BeforeHook.new(block, {})
1835
1946
  end || begin
1947
+ # defeat Ruby 2.5 lazy proc allocation to ensure
1948
+ # the methods below are passed the same proc instances
1949
+ # so `Hook` equality is preserved. For more info, see:
1950
+ # https://bugs.ruby-lang.org/issues/14045#note-5
1951
+ block.__id__
1952
+
1836
1953
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.prepend_before(scope, *meta, &block) }
1837
1954
  super(scope, *meta, &block)
1838
1955
  end
@@ -1851,6 +1968,12 @@ module RSpec
1851
1968
  handle_suite_hook(scope, meta) do
1852
1969
  @after_suite_hooks.unshift Hooks::AfterHook.new(block, {})
1853
1970
  end || begin
1971
+ # defeat Ruby 2.5 lazy proc allocation to ensure
1972
+ # the methods below are passed the same proc instances
1973
+ # so `Hook` equality is preserved. For more info, see:
1974
+ # https://bugs.ruby-lang.org/issues/14045#note-5
1975
+ block.__id__
1976
+
1854
1977
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.after(scope, *meta, &block) }
1855
1978
  super(scope, *meta, &block)
1856
1979
  end
@@ -1874,6 +1997,12 @@ module RSpec
1874
1997
  handle_suite_hook(scope, meta) do
1875
1998
  @after_suite_hooks << Hooks::AfterHook.new(block, {})
1876
1999
  end || begin
2000
+ # defeat Ruby 2.5 lazy proc allocation to ensure
2001
+ # the methods below are passed the same proc instances
2002
+ # so `Hook` equality is preserved. For more info, see:
2003
+ # https://bugs.ruby-lang.org/issues/14045#note-5
2004
+ block.__id__
2005
+
1877
2006
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.append_after(scope, *meta, &block) }
1878
2007
  super(scope, *meta, &block)
1879
2008
  end
@@ -1883,6 +2012,12 @@ module RSpec
1883
2012
  #
1884
2013
  # See {Hooks#around} for full `around` hook docs.
1885
2014
  def around(scope=nil, *meta, &block)
2015
+ # defeat Ruby 2.5 lazy proc allocation to ensure
2016
+ # the methods below are passed the same proc instances
2017
+ # so `Hook` equality is preserved. For more info, see:
2018
+ # https://bugs.ruby-lang.org/issues/14045#note-5
2019
+ block.__id__
2020
+
1886
2021
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.around(scope, *meta, &block) }
1887
2022
  super(scope, *meta, &block)
1888
2023
  end
@@ -1918,10 +2053,32 @@ module RSpec
1918
2053
  @on_example_group_definition_callbacks ||= []
1919
2054
  end
1920
2055
 
2056
+ # @private
2057
+ def bisect_runner_class
2058
+ @bisect_runner_class ||= begin
2059
+ case bisect_runner
2060
+ when :fork
2061
+ RSpec::Support.require_rspec_core 'bisect/fork_runner'
2062
+ Bisect::ForkRunner
2063
+ when :shell
2064
+ RSpec::Support.require_rspec_core 'bisect/shell_runner'
2065
+ Bisect::ShellRunner
2066
+ else
2067
+ raise "Unsupported value for `bisect_runner` (#{bisect_runner.inspect}). " \
2068
+ "Only `:fork` and `:shell` are supported."
2069
+ end
2070
+ end
2071
+ end
2072
+
1921
2073
  private
1922
2074
 
1923
- def load_spec_file_handling_errors(file)
1924
- load file
2075
+ def load_file_handling_errors(method, file)
2076
+ __send__(method, file)
2077
+ rescue LoadError => ex
2078
+ relative_file = Metadata.relative_path(file)
2079
+ suggestions = DidYouMean.new(relative_file).call
2080
+ reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.#{suggestions}")
2081
+ RSpec.world.wants_to_quit = true
1925
2082
  rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
1926
2083
  relative_file = Metadata.relative_path(file)
1927
2084
  reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.")