rspec-core 3.5.4 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) 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 +165 -2
  5. data/README.md +16 -16
  6. data/lib/rspec/core.rb +2 -1
  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 +10 -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 +315 -79
  15. data/lib/rspec/core/configuration_options.rb +43 -4
  16. data/lib/rspec/core/did_you_mean.rb +46 -0
  17. data/lib/rspec/core/drb.rb +3 -1
  18. data/lib/rspec/core/example.rb +19 -12
  19. data/lib/rspec/core/example_group.rb +17 -7
  20. data/lib/rspec/core/formatters.rb +28 -11
  21. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  22. data/lib/rspec/core/formatters/base_formatter.rb +1 -1
  23. data/lib/rspec/core/formatters/base_text_formatter.rb +3 -5
  24. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  25. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
  26. data/lib/rspec/core/formatters/console_codes.rb +7 -4
  27. data/lib/rspec/core/formatters/deprecation_formatter.rb +9 -9
  28. data/lib/rspec/core/formatters/documentation_formatter.rb +37 -4
  29. data/lib/rspec/core/formatters/exception_presenter.rb +21 -4
  30. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  31. data/lib/rspec/core/formatters/html_formatter.rb +4 -2
  32. data/lib/rspec/core/formatters/html_printer.rb +3 -3
  33. data/lib/rspec/core/formatters/html_snippet_extractor.rb +4 -0
  34. data/lib/rspec/core/formatters/json_formatter.rb +9 -3
  35. data/lib/rspec/core/formatters/progress_formatter.rb +1 -0
  36. data/lib/rspec/core/formatters/protocol.rb +43 -42
  37. data/lib/rspec/core/formatters/snippet_extractor.rb +1 -3
  38. data/lib/rspec/core/{source → formatters}/syntax_highlighter.rb +21 -1
  39. data/lib/rspec/core/hooks.rb +18 -10
  40. data/lib/rspec/core/invocations.rb +30 -10
  41. data/lib/rspec/core/memoized_helpers.rb +36 -14
  42. data/lib/rspec/core/metadata.rb +2 -3
  43. data/lib/rspec/core/metadata_filter.rb +29 -17
  44. data/lib/rspec/core/notifications.rb +34 -11
  45. data/lib/rspec/core/option_parser.rb +32 -4
  46. data/lib/rspec/core/output_wrapper.rb +29 -0
  47. data/lib/rspec/core/profiler.rb +3 -1
  48. data/lib/rspec/core/project_initializer/.rspec +0 -1
  49. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +1 -4
  50. data/lib/rspec/core/rake_task.rb +21 -1
  51. data/lib/rspec/core/reporter.rb +33 -16
  52. data/lib/rspec/core/runner.rb +31 -15
  53. data/lib/rspec/core/set.rb +5 -0
  54. data/lib/rspec/core/shared_example_group.rb +41 -19
  55. data/lib/rspec/core/shell_escape.rb +2 -2
  56. data/lib/rspec/core/version.rb +1 -1
  57. data/lib/rspec/core/world.rb +24 -5
  58. metadata +26 -20
  59. metadata.gz.sig +0 -0
  60. data/lib/rspec/core/formatters/bisect_formatter.rb +0 -69
  61. data/lib/rspec/core/source.rb +0 -86
  62. data/lib/rspec/core/source/location.rb +0 -13
  63. data/lib/rspec/core/source/node.rb +0 -93
  64. data/lib/rspec/core/source/token.rb +0 -87
@@ -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.
@@ -25,12 +23,17 @@ module RSpec
25
23
  self.files_or_directories_to_run = files_or_directories_to_run
26
24
  self.latest_run_results = nil
27
25
  run_output = yield
28
- latest_run_results || raise_bisect_failed(run_output)
26
+
27
+ if latest_run_results.nil? || latest_run_results.all_example_ids.empty?
28
+ raise BisectFailedError.for_failed_spec_run(run_output)
29
+ end
30
+
31
+ latest_run_results
29
32
  end
30
33
 
31
34
  def start
32
35
  # Only allow remote DRb requests from this machine.
33
- 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 ])
34
37
 
35
38
  # We pass `nil` as the first arg to allow it to pick a DRb port.
36
39
  @drb = DRb.start_service(nil, self)
@@ -44,21 +47,14 @@ module RSpec
44
47
  @drb_port ||= Integer(@drb.uri[/\d+$/])
45
48
  end
46
49
 
47
- # Fetched via DRb by the BisectFormatter to determine when to abort.
50
+ # Fetched via DRb by the BisectDRbFormatter to determine when to abort.
48
51
  attr_accessor :expected_failures
49
52
 
50
- # 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.
51
54
  attr_accessor :latest_run_results
52
55
 
53
56
  # Fetched via DRb to tell clients which files to run
54
57
  attr_accessor :files_or_directories_to_run
55
-
56
- private
57
-
58
- def raise_bisect_failed(run_output)
59
- raise BisectFailedError, "Failed to get results from the spec " \
60
- "run. Spec run output:\n\n#{run_output}"
61
- end
62
58
  end
63
59
  end
64
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
@@ -1,6 +1,7 @@
1
1
  RSpec::Support.require_rspec_core "backtrace_formatter"
2
2
  RSpec::Support.require_rspec_core "ruby_project"
3
3
  RSpec::Support.require_rspec_core "formatters/deprecation_formatter"
4
+ RSpec::Support.require_rspec_core "output_wrapper"
4
5
 
5
6
  module RSpec
6
7
  module Core
@@ -8,10 +9,24 @@ module RSpec
8
9
 
9
10
  # Stores runtime configuration information.
10
11
  #
11
- # Configuration options are loaded from `~/.rspec`, `.rspec`,
12
- # `.rspec-local`, command line switches, and the `SPEC_OPTS` environment
13
- # variable (listed in lowest to highest precedence; for example, an option
14
- # 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.
15
30
  #
16
31
  # @example Standard settings
17
32
  # RSpec.configure do |c|
@@ -88,7 +103,6 @@ module RSpec
88
103
 
89
104
  # @macro [attach] add_setting
90
105
  # @!attribute [rw] $1
91
- # @!method $1=(value)
92
106
  #
93
107
  # @macro [attach] define_reader
94
108
  # @!attribute [r] $1
@@ -100,6 +114,7 @@ module RSpec
100
114
  #
101
115
  # @note Other scripts invoking `rspec` indirectly will ignore this
102
116
  # setting.
117
+ # @return [String]
103
118
  add_read_only_setting :default_path
104
119
  def default_path=(path)
105
120
  project_source_dirs << path
@@ -109,6 +124,7 @@ module RSpec
109
124
  # @macro add_setting
110
125
  # Run examples over DRb (default: `false`). RSpec doesn't supply the DRb
111
126
  # server, but you can use tools like spork.
127
+ # @return [Boolean]
112
128
  add_setting :drb
113
129
 
114
130
  # @macro add_setting
@@ -121,6 +137,7 @@ module RSpec
121
137
 
122
138
  # Indicates if the DSL has been exposed off of modules and `main`.
123
139
  # Default: true
140
+ # @return [Boolean]
124
141
  def expose_dsl_globally?
125
142
  Core::DSL.exposed_globally?
126
143
  end
@@ -141,7 +158,7 @@ module RSpec
141
158
 
142
159
  # Determines where deprecation warnings are printed.
143
160
  # Defaults to `$stderr`.
144
- # @return [IO, String] IO to write to or filename to write to
161
+ # @return [IO, String] IO or filename to write to
145
162
  define_reader :deprecation_stream
146
163
 
147
164
  # Determines where deprecation warnings are printed.
@@ -160,7 +177,7 @@ module RSpec
160
177
 
161
178
  # @macro define_reader
162
179
  # The file path to use for persisting example statuses. Necessary for the
163
- # `--only-failures` and `--next-failures` CLI options.
180
+ # `--only-failures` and `--next-failure` CLI options.
164
181
  #
165
182
  # @overload example_status_persistence_file_path
166
183
  # @return [String] the file path
@@ -169,7 +186,7 @@ module RSpec
169
186
  define_reader :example_status_persistence_file_path
170
187
 
171
188
  # Sets the file path to use for persisting example statuses. Necessary for the
172
- # `--only-failures` and `--next-failures` CLI options.
189
+ # `--only-failures` and `--next-failure` CLI options.
173
190
  def example_status_persistence_file_path=(value)
174
191
  @example_status_persistence_file_path = value
175
192
  clear_values_derived_from_example_status_persistence_file_path
@@ -185,10 +202,33 @@ module RSpec
185
202
  only_failures? && !example_status_persistence_file_path
186
203
  end
187
204
 
188
- # @macro add_setting
205
+ # @macro define_reader
189
206
  # If specified, indicates the number of failures required before cleaning
190
- # up and exit (default: `nil`).
191
- 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
192
232
 
193
233
  # @macro add_setting
194
234
  # Prints the formatter output of your suite without running any
@@ -197,24 +237,33 @@ module RSpec
197
237
 
198
238
  # @macro add_setting
199
239
  # The exit code to return if there are any failures (default: 1).
240
+ # @return [Integer]
200
241
  add_setting :failure_exit_code
201
242
 
243
+ # @macro add_setting
244
+ # Whether or not to fail when there are no RSpec examples (default: false).
245
+ # @return [Boolean]
246
+ add_setting :fail_if_no_examples
247
+
202
248
  # @macro define_reader
203
249
  # Indicates files configured to be required.
250
+ # @return [Array<String>]
204
251
  define_reader :requires
205
252
 
206
253
  # @macro define_reader
207
254
  # Returns dirs that have been prepended to the load path by the `-I`
208
255
  # command line option.
256
+ # @return [Array<String>]
209
257
  define_reader :libs
210
258
 
211
259
  # @macro add_setting
212
260
  # Determines where RSpec will send its output.
213
261
  # Default: `$stdout`.
262
+ # @return [IO, String]
214
263
  define_reader :output_stream
215
264
 
216
265
  # Set the output stream for reporter.
217
- # @attr value [IO] value for output, defaults to $stdout
266
+ # @attr value [IO, String] IO to write to or filename to write to, defaults to $stdout
218
267
  def output_stream=(value)
219
268
  if @reporter && !value.equal?(@output_stream)
220
269
  warn "RSpec's reporter has already been initialized with " \
@@ -223,11 +272,13 @@ module RSpec
223
272
  "it to take effect. (Called from #{CallerFilter.first_non_rspec_line})"
224
273
  else
225
274
  @output_stream = value
275
+ output_wrapper.output = @output_stream
226
276
  end
227
277
  end
228
278
 
229
279
  # @macro define_reader
230
280
  # Load files matching this pattern (default: `'**{,/*/**}/*_spec.rb'`).
281
+ # @return [String]
231
282
  define_reader :pattern
232
283
 
233
284
  # Set pattern to match files to load.
@@ -238,6 +289,7 @@ module RSpec
238
289
 
239
290
  # @macro define_reader
240
291
  # Exclude files matching this pattern.
292
+ # @return [String]
241
293
  define_reader :exclude_pattern
242
294
 
243
295
  # Set pattern to match files to exclude.
@@ -259,6 +311,7 @@ module RSpec
259
311
  # @macro add_setting
260
312
  # Report the times for the slowest examples (default: `false`).
261
313
  # Use this to specify the number of examples to include in the profile.
314
+ # @return [Boolean]
262
315
  add_setting :profile_examples
263
316
 
264
317
  # @macro add_setting
@@ -269,55 +322,56 @@ module RSpec
269
322
  add_setting :run_all_when_everything_filtered
270
323
 
271
324
  # @macro add_setting
272
- # Color to use to indicate success.
273
- # @param color [Symbol] defaults to `:green` but can be set to one of the
274
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
275
- # :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]
276
329
  add_setting :success_color
277
330
 
278
331
  # @macro add_setting
279
- # Color to use to print pending examples.
280
- # @param color [Symbol] defaults to `:yellow` but can be set to one of the
281
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
282
- # :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]
283
336
  add_setting :pending_color
284
337
 
285
338
  # @macro add_setting
286
- # Color to use to indicate failure.
287
- # @param color [Symbol] defaults to `:red` but can be set to one of the
288
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
289
- # :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]
290
343
  add_setting :failure_color
291
344
 
292
345
  # @macro add_setting
293
- # The default output color.
294
- # @param color [Symbol] defaults to `:white` but can be set to one of the
295
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
296
- # :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]
297
350
  add_setting :default_color
298
351
 
299
352
  # @macro add_setting
300
- # Color used when a pending example is fixed.
301
- # @param color [Symbol] defaults to `:blue` but can be set to one of the
302
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
303
- # :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]
304
357
  add_setting :fixed_color
305
358
 
306
359
  # @macro add_setting
307
- # Color used to print details.
308
- # @param color [Symbol] defaults to `:cyan` but can be set to one of the
309
- # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta,
310
- # :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]
311
364
  add_setting :detail_color
312
365
 
313
366
  # @macro add_setting
314
367
  # Don't print filter info i.e. "Run options: include {:focus=>true}"
315
368
  # (default `false`).
369
+ # return [Boolean]
316
370
  add_setting :silence_filter_announcements
317
371
 
318
- # Deprecated. This config option was added in RSpec 2 to pave the way
319
- # for this being the default behavior in RSpec 3. Now this option is
320
- # 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.
321
375
  def treat_symbols_as_metadata_keys_with_true_values=(_value)
322
376
  RSpec.deprecate(
323
377
  "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values=",
@@ -381,19 +435,55 @@ module RSpec
381
435
  end
382
436
 
383
437
  # Record the start time of the spec suite to measure load time.
438
+ # return [Time]
384
439
  add_setting :start_time
385
440
 
386
441
  # @macro add_setting
387
442
  # Use threadsafe options where available.
388
443
  # Currently this will place a mutex around memoized values such as let blocks.
444
+ # return [Boolean]
389
445
  add_setting :threadsafe
390
446
 
391
447
  # @macro add_setting
392
448
  # Maximum count of failed source lines to display in the failure reports.
393
449
  # (default `10`).
450
+ # return [Integer]
394
451
  add_setting :max_displayed_failure_line_count
395
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
+
396
485
  # @private
486
+ # @deprecated Use {#color_mode} = :on, instead of {#color} with {#tty}
397
487
  add_setting :tty
398
488
  # @private
399
489
  attr_writer :files_to_run
@@ -404,8 +494,9 @@ module RSpec
404
494
  # @private
405
495
  attr_reader :backtrace_formatter, :ordering_manager, :loaded_spec_files
406
496
 
407
- # rubocop:disable Metrics/AbcSize
408
- # rubocop:disable Metrics/MethodLength
497
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
498
+
499
+ # Build an object to store runtime configuration options and set defaults
409
500
  def initialize
410
501
  # rubocop:disable Style/GlobalVars
411
502
  @start_time = $_rspec_core_load_started_at || ::RSpec::Core::Time.now
@@ -415,6 +506,9 @@ module RSpec
415
506
  @extend_modules = FilterableItemRepository::QueryOptimized.new(:any?)
416
507
  @prepend_modules = FilterableItemRepository::QueryOptimized.new(:any?)
417
508
 
509
+ @bisect_runner = RSpec::Support::RubyFeatures.fork_supported? ? :fork : :shell
510
+ @bisect_runner_class = nil
511
+
418
512
  @before_suite_hooks = []
419
513
  @after_suite_hooks = []
420
514
 
@@ -422,9 +516,11 @@ module RSpec
422
516
  @files_or_directories_to_run = []
423
517
  @loaded_spec_files = Set.new
424
518
  @color = false
519
+ @color_mode = :automatic
425
520
  @pattern = '**{,/*/**}/*_spec.rb'
426
521
  @exclude_pattern = ''
427
522
  @failure_exit_code = 1
523
+ @fail_if_no_examples = false
428
524
  @spec_files_loaded = false
429
525
 
430
526
  @backtrace_formatter = BacktraceFormatter.new
@@ -456,8 +552,7 @@ module RSpec
456
552
 
457
553
  define_built_in_hooks
458
554
  end
459
- # rubocop:enable Metrics/MethodLength
460
- # rubocop:enable Metrics/AbcSize
555
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
461
556
 
462
557
  # @private
463
558
  #
@@ -473,8 +568,14 @@ module RSpec
473
568
  # @private
474
569
  def reset
475
570
  @spec_files_loaded = false
571
+ reset_reporter
572
+ end
573
+
574
+ # @private
575
+ def reset_reporter
476
576
  @reporter = nil
477
577
  @formatter_loader = nil
578
+ @output_wrapper = nil
478
579
  end
479
580
 
480
581
  # @private
@@ -532,6 +633,7 @@ module RSpec
532
633
  end
533
634
 
534
635
  # Returns the configured mock framework adapter module.
636
+ # @return [Symbol]
535
637
  def mock_framework
536
638
  if @mock_framework.nil?
537
639
  begin
@@ -559,12 +661,13 @@ module RSpec
559
661
  # To override this behaviour and display a full backtrace, use
560
662
  # `--backtrace` on the command line, in a `.rspec` file, or in the
561
663
  # `rspec_options` attribute of RSpec's rake task.
664
+ # @return [Array<Regexp>]
562
665
  def backtrace_exclusion_patterns
563
666
  @backtrace_formatter.exclusion_patterns
564
667
  end
565
668
 
566
669
  # Set regular expressions used to exclude lines in backtrace.
567
- # @param patterns [Regexp] set the backtrace exlusion pattern
670
+ # @param patterns [Array<Regexp>] set backtrace_formatter exlusion_patterns
568
671
  def backtrace_exclusion_patterns=(patterns)
569
672
  @backtrace_formatter.exclusion_patterns = patterns
570
673
  end
@@ -577,12 +680,13 @@ module RSpec
577
680
  # will be included.
578
681
  #
579
682
  # You can modify the list via the getter, or replace it with the setter.
683
+ # @return [Array<Regexp>]
580
684
  def backtrace_inclusion_patterns
581
685
  @backtrace_formatter.inclusion_patterns
582
686
  end
583
687
 
584
688
  # Set regular expressions used to include lines in backtrace.
585
- # @attr patterns [Regexp] set backtrace_formatter inclusion_patterns
689
+ # @attr patterns [Array<Regexp>] set backtrace_formatter inclusion_patterns
586
690
  def backtrace_inclusion_patterns=(patterns)
587
691
  @backtrace_formatter.inclusion_patterns = patterns
588
692
  end
@@ -773,24 +877,54 @@ module RSpec
773
877
  @backtrace_formatter.full_backtrace = true_or_false
774
878
  end
775
879
 
776
- # Returns the configuration option for color, but should not
777
- # be used to check if color is supported.
880
+ # Enables color output if the output is a TTY. As of RSpec 3.6, this is
881
+ # the default behavior and this option is retained only for backwards
882
+ # compatibility.
778
883
  #
884
+ # @deprecated No longer recommended because of complex behavior. Instead,
885
+ # rely on the fact that TTYs will display color by default, or set
886
+ # {#color_mode} to :on to display color on a non-TTY output.
887
+ # @see color_mode
779
888
  # @see color_enabled?
780
889
  # @return [Boolean]
781
890
  def color
782
891
  value_for(:color) { @color }
783
892
  end
784
893
 
894
+ # The mode for determining whether to display output in color. One of:
895
+ #
896
+ # - :automatic - the output will be in color if the output is a TTY (the
897
+ # default)
898
+ # - :on - the output will be in color, whether or not the output is a TTY
899
+ # - :off - the output will not be in color
900
+ #
901
+ # @see color_enabled?
902
+ # @return [Boolean]
903
+ def color_mode
904
+ value_for(:color_mode) { @color_mode }
905
+ end
906
+
785
907
  # Check if color is enabled for a particular output.
786
908
  # @param output [IO] an output stream to use, defaults to the current
787
909
  # `output_stream`
788
910
  # @return [Boolean]
789
911
  def color_enabled?(output=output_stream)
790
- output_to_tty?(output) && color
912
+ case color_mode
913
+ when :on then true
914
+ when :off then false
915
+ else # automatic
916
+ output_to_tty?(output) || (color && tty?)
917
+ end
791
918
  end
792
919
 
920
+ # Set the color mode.
921
+ attr_writer :color_mode
922
+
793
923
  # Toggle output color.
924
+ #
925
+ # @deprecated No longer recommended because of complex behavior. Instead,
926
+ # rely on the fact that TTYs will display color by default, or set
927
+ # {:color_mode} to :on to display color on a non-TTY output.
794
928
  attr_writer :color
795
929
 
796
930
  # @private
@@ -813,19 +947,22 @@ module RSpec
813
947
  end
814
948
 
815
949
  # @overload add_formatter(formatter)
950
+ # @overload add_formatter(formatter, output)
816
951
  #
817
- # Adds a formatter to the formatters collection. `formatter` can be a
818
- # string representing any of the built-in formatters (see
819
- # `built_in_formatter`), or a custom formatter class.
952
+ # @param formatter [Class, String, Object] formatter to use. Can be any of the
953
+ # string values supported from the CLI (`p`/`progress`,
954
+ # `d`/`doc`/`documentation`, `h`/`html`, or `j`/`json`), any
955
+ # class that implements the formatter protocol and has registered
956
+ # itself with RSpec as a formatter, or a formatter instance.
957
+ # @param output [String, IO] where the formatter will write its output.
958
+ # Can be an IO object or a string path to a file. If not provided,
959
+ # the configured `output_stream` (`$stdout`, by default) will be used.
820
960
  #
821
- # ### Note
961
+ # Adds a formatter to the set RSpec will use for this run.
822
962
  #
823
- # For internal purposes, `add_formatter` also accepts the name of a class
824
- # and paths to use for output streams, but you should consider that a
825
- # private api that may change at any time without notice.
826
- def add_formatter(formatter_to_use, *paths)
827
- paths << output_stream if paths.empty?
828
- formatter_loader.add formatter_to_use, *paths
963
+ # @see RSpec::Core::Formatters::Protocol
964
+ def add_formatter(formatter, output=output_wrapper)
965
+ formatter_loader.add(formatter, output)
829
966
  end
830
967
  alias_method :formatter=, :add_formatter
831
968
 
@@ -890,7 +1027,7 @@ module RSpec
890
1027
  @reporter_buffer || @reporter ||=
891
1028
  begin
892
1029
  @reporter_buffer = DeprecationReporterBuffer.new
893
- formatter_loader.setup_default output_stream, deprecation_stream
1030
+ formatter_loader.prepare_default output_wrapper, deprecation_stream
894
1031
  @reporter_buffer.play_onto(formatter_loader.reporter)
895
1032
  @reporter_buffer = nil
896
1033
  formatter_loader.reporter
@@ -934,7 +1071,9 @@ module RSpec
934
1071
  if (path = example_status_persistence_file_path)
935
1072
  begin
936
1073
  ExampleStatusPersister.load_from(path).inject(statuses) do |hash, example|
937
- hash[example.fetch(:example_id)] = example.fetch(:status)
1074
+ status = example[:status]
1075
+ status = UNKNOWN_STATUS unless VALID_STATUSES.include?(status)
1076
+ hash[example.fetch(:example_id)] = status
938
1077
  hash
939
1078
  end
940
1079
  rescue SystemCallError => e
@@ -954,6 +1093,15 @@ module RSpec
954
1093
  # @private
955
1094
  FAILED_STATUS = "failed".freeze
956
1095
 
1096
+ # @private
1097
+ PASSED_STATUS = "passed".freeze
1098
+
1099
+ # @private
1100
+ PENDING_STATUS = "pending".freeze
1101
+
1102
+ # @private
1103
+ VALID_STATUSES = [UNKNOWN_STATUS, FAILED_STATUS, PASSED_STATUS, PENDING_STATUS]
1104
+
957
1105
  # @private
958
1106
  def spec_files_with_failures
959
1107
  @spec_files_with_failures ||= last_run_statuses.inject(Set.new) do |files, (id, status)|
@@ -981,7 +1129,7 @@ module RSpec
981
1129
  #
982
1130
  # # This lets you do this:
983
1131
  #
984
- # describe Thing do
1132
+ # RSpec.describe Thing do
985
1133
  # pending "does something" do
986
1134
  # thing = Thing.new
987
1135
  # end
@@ -989,7 +1137,7 @@ module RSpec
989
1137
  #
990
1138
  # # ... which is the equivalent of
991
1139
  #
992
- # describe Thing do
1140
+ # RSpec.describe Thing do
993
1141
  # it "does something", :pending => true do
994
1142
  # thing = Thing.new
995
1143
  # end
@@ -1042,7 +1190,7 @@ module RSpec
1042
1190
  #
1043
1191
  # # allows the user to include a shared example group like:
1044
1192
  #
1045
- # describe Entity do
1193
+ # RSpec.describe Entity do
1046
1194
  # it_has_behavior 'sortability' do
1047
1195
  # let(:sortable) { Entity.new }
1048
1196
  # end
@@ -1391,7 +1539,7 @@ module RSpec
1391
1539
  def requires=(paths)
1392
1540
  directories = ['lib', default_path].select { |p| File.directory? p }
1393
1541
  RSpec::Core::RubyProject.add_to_load_path(*directories)
1394
- paths.each { |path| require path }
1542
+ paths.each { |path| load_file_handling_errors(:require, path) }
1395
1543
  @requires += paths
1396
1544
  end
1397
1545
 
@@ -1432,7 +1580,7 @@ module RSpec
1432
1580
 
1433
1581
  files_to_run.uniq.each do |f|
1434
1582
  file = File.expand_path(f)
1435
- load file
1583
+ load_file_handling_errors(:load, file)
1436
1584
  loaded_spec_files << file
1437
1585
  end
1438
1586
 
@@ -1460,8 +1608,6 @@ module RSpec
1460
1608
  end
1461
1609
 
1462
1610
  # @private
1463
- # @macro [attach] delegate_to_ordering_manager
1464
- # @!method $1
1465
1611
  def self.delegate_to_ordering_manager(*methods)
1466
1612
  methods.each do |method|
1467
1613
  define_method method do |*args, &block|
@@ -1470,12 +1616,12 @@ module RSpec
1470
1616
  end
1471
1617
  end
1472
1618
 
1473
- # @macro delegate_to_ordering_manager
1619
+ # @!method seed=(value)
1474
1620
  #
1475
1621
  # Sets the seed value and sets the default global ordering to random.
1476
1622
  delegate_to_ordering_manager :seed=
1477
1623
 
1478
- # @macro delegate_to_ordering_manager
1624
+ # @!method seed
1479
1625
  # Seed for random ordering (default: generated randomly each run).
1480
1626
  #
1481
1627
  # When you run specs with `--order random`, RSpec generates a random seed
@@ -1489,7 +1635,7 @@ module RSpec
1489
1635
  # don't accidentally leave the seed encoded.
1490
1636
  delegate_to_ordering_manager :seed
1491
1637
 
1492
- # @macro delegate_to_ordering_manager
1638
+ # @!method order=(value)
1493
1639
  #
1494
1640
  # Sets the default global ordering strategy. By default this can be one
1495
1641
  # of `:defined`, `:random`, but is customizable through the
@@ -1499,7 +1645,8 @@ module RSpec
1499
1645
  # @see #register_ordering
1500
1646
  delegate_to_ordering_manager :order=
1501
1647
 
1502
- # @macro delegate_to_ordering_manager
1648
+ # @!method register_ordering(name)
1649
+ #
1503
1650
  # Registers a named ordering strategy that can later be
1504
1651
  # used to order an example group's subgroups by adding
1505
1652
  # `:order => <name>` metadata to the example group.
@@ -1594,7 +1741,7 @@ module RSpec
1594
1741
  # rspec.expose_current_running_example_as :example
1595
1742
  # end
1596
1743
  #
1597
- # describe MyClass do
1744
+ # RSpec.describe MyClass do
1598
1745
  # before do
1599
1746
  # # `example` can be used here because of the above config.
1600
1747
  # do_something if example.metadata[:type] == "foo"
@@ -1658,7 +1805,7 @@ module RSpec
1658
1805
  # mocks.patch_marshal_to_support_partial_doubles = false
1659
1806
  # end
1660
1807
  #
1661
- # config.mock_with :rspec do |expectations|
1808
+ # config.expect_with :rspec do |expectations|
1662
1809
  # expectations.syntax = :expect
1663
1810
  # end
1664
1811
  # end
@@ -1712,7 +1859,7 @@ module RSpec
1712
1859
  # require 'support/db'
1713
1860
  # end
1714
1861
  # end
1715
- def when_first_matching_example_defined(*filters, &block)
1862
+ def when_first_matching_example_defined(*filters)
1716
1863
  specified_meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
1717
1864
 
1718
1865
  callback = lambda do |example_or_group_meta|
@@ -1721,9 +1868,9 @@ module RSpec
1721
1868
  return unless example_or_group_meta.key?(:example_group)
1722
1869
 
1723
1870
  # Ensure the callback only fires once.
1724
- @derived_metadata_blocks.items_for(specified_meta).delete(callback)
1871
+ @derived_metadata_blocks.delete(callback, specified_meta)
1725
1872
 
1726
- block.call
1873
+ yield
1727
1874
  end
1728
1875
 
1729
1876
  @derived_metadata_blocks.append(callback, specified_meta)
@@ -1731,9 +1878,28 @@ module RSpec
1731
1878
 
1732
1879
  # @private
1733
1880
  def apply_derived_metadata_to(metadata)
1734
- @derived_metadata_blocks.items_for(metadata).each do |block|
1735
- 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
1736
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."
1737
1903
  end
1738
1904
 
1739
1905
  # Defines a `before` hook. See {Hooks#before} for full docs.
@@ -1749,6 +1915,12 @@ module RSpec
1749
1915
  handle_suite_hook(scope, meta) do
1750
1916
  @before_suite_hooks << Hooks::BeforeHook.new(block, {})
1751
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
+
1752
1924
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.before(scope, *meta, &block) }
1753
1925
  super(scope, *meta, &block)
1754
1926
  end
@@ -1772,6 +1944,12 @@ module RSpec
1772
1944
  handle_suite_hook(scope, meta) do
1773
1945
  @before_suite_hooks.unshift Hooks::BeforeHook.new(block, {})
1774
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
+
1775
1953
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.prepend_before(scope, *meta, &block) }
1776
1954
  super(scope, *meta, &block)
1777
1955
  end
@@ -1790,6 +1968,12 @@ module RSpec
1790
1968
  handle_suite_hook(scope, meta) do
1791
1969
  @after_suite_hooks.unshift Hooks::AfterHook.new(block, {})
1792
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
+
1793
1977
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.after(scope, *meta, &block) }
1794
1978
  super(scope, *meta, &block)
1795
1979
  end
@@ -1813,6 +1997,12 @@ module RSpec
1813
1997
  handle_suite_hook(scope, meta) do
1814
1998
  @after_suite_hooks << Hooks::AfterHook.new(block, {})
1815
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
+
1816
2006
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.append_after(scope, *meta, &block) }
1817
2007
  super(scope, *meta, &block)
1818
2008
  end
@@ -1822,6 +2012,12 @@ module RSpec
1822
2012
  #
1823
2013
  # See {Hooks#around} for full `around` hook docs.
1824
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
+
1825
2021
  add_hook_to_existing_matching_groups(meta, scope) { |g| g.around(scope, *meta, &block) }
1826
2022
  super(scope, *meta, &block)
1827
2023
  end
@@ -1857,8 +2053,38 @@ module RSpec
1857
2053
  @on_example_group_definition_callbacks ||= []
1858
2054
  end
1859
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
+
1860
2073
  private
1861
2074
 
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
2082
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
2083
+ relative_file = Metadata.relative_path(file)
2084
+ reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.")
2085
+ RSpec.world.wants_to_quit = true
2086
+ end
2087
+
1862
2088
  def handle_suite_hook(scope, meta)
1863
2089
  return nil unless scope == :suite
1864
2090
 
@@ -1882,6 +2108,12 @@ module RSpec
1882
2108
  hook.run(context)
1883
2109
  rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
1884
2110
  context.set_exception(ex)
2111
+
2112
+ # Do not run subsequent `before` hooks if one fails.
2113
+ # But for `after` hooks, we run them all so that all
2114
+ # cleanup bits get a chance to complete, minimizing the
2115
+ # chance that resources get left behind.
2116
+ break if hooks.equal?(@before_suite_hooks)
1885
2117
  end
1886
2118
  end
1887
2119
  end
@@ -1985,8 +2217,12 @@ module RSpec
1985
2217
  )
1986
2218
  end
1987
2219
 
2220
+ def output_wrapper
2221
+ @output_wrapper ||= OutputWrapper.new(output_stream)
2222
+ end
2223
+
1988
2224
  def output_to_tty?(output=output_stream)
1989
- tty? || (output.respond_to?(:tty?) && output.tty?)
2225
+ output.respond_to?(:tty?) && output.tty?
1990
2226
  end
1991
2227
 
1992
2228
  def conditionally_disable_mocks_monkey_patching