rspec-core 3.7.1 → 3.9.3

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +116 -0
  5. data/README.md +18 -18
  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 +138 -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 +236 -79
  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 +18 -8
  18. data/lib/rspec/core/example_group.rb +33 -16
  19. data/lib/rspec/core/filter_manager.rb +1 -1
  20. data/lib/rspec/core/formatters.rb +14 -6
  21. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  22. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  23. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
  24. data/lib/rspec/core/formatters/deprecation_formatter.rb +3 -1
  25. data/lib/rspec/core/formatters/documentation_formatter.rb +35 -3
  26. data/lib/rspec/core/formatters/exception_presenter.rb +29 -6
  27. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  28. data/lib/rspec/core/formatters/html_printer.rb +0 -2
  29. data/lib/rspec/core/formatters/protocol.rb +17 -17
  30. data/lib/rspec/core/formatters/syntax_highlighter.rb +19 -19
  31. data/lib/rspec/core/hooks.rb +44 -24
  32. data/lib/rspec/core/invocations.rb +9 -7
  33. data/lib/rspec/core/memoized_helpers.rb +33 -14
  34. data/lib/rspec/core/metadata.rb +2 -3
  35. data/lib/rspec/core/option_parser.rb +10 -3
  36. data/lib/rspec/core/profiler.rb +3 -1
  37. data/lib/rspec/core/rake_task.rb +22 -2
  38. data/lib/rspec/core/reporter.rb +11 -6
  39. data/lib/rspec/core/runner.rb +25 -14
  40. data/lib/rspec/core/shared_example_group.rb +5 -5
  41. data/lib/rspec/core/shell_escape.rb +2 -2
  42. data/lib/rspec/core/version.rb +1 -1
  43. data/lib/rspec/core/world.rb +14 -1
  44. metadata +25 -15
  45. metadata.gz.sig +0 -0
  46. 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|
@@ -53,15 +67,17 @@ module RSpec
53
67
  end
54
68
 
55
69
  # @private
56
- def self.define_aliases(name, alias_name)
70
+ def self.define_alias(name, alias_name)
57
71
  alias_method alias_name, name
58
72
  alias_method "#{alias_name}=", "#{name}="
59
- define_predicate_for alias_name
73
+ define_predicate alias_name
60
74
  end
61
75
 
62
76
  # @private
63
- def self.define_predicate_for(*names)
64
- names.each { |name| alias_method "#{name}?", name }
77
+ def self.define_predicate(name)
78
+ define_method "#{name}?" do
79
+ !!send(name)
80
+ end
65
81
  end
66
82
 
67
83
  # @private
@@ -74,7 +90,7 @@ module RSpec
74
90
  add_read_only_setting name
75
91
 
76
92
  Array(opts[:alias_with]).each do |alias_name|
77
- define_aliases(name, alias_name)
93
+ define_alias(name, alias_name)
78
94
  end
79
95
  end
80
96
 
@@ -84,12 +100,11 @@ module RSpec
84
100
  def self.add_read_only_setting(name, opts={})
85
101
  raise "Use the instance add_setting method if you want to set a default" if opts.key?(:default)
86
102
  define_reader name
87
- define_predicate_for name
103
+ define_predicate name
88
104
  end
89
105
 
90
106
  # @macro [attach] add_setting
91
107
  # @!attribute [rw] $1
92
- # @!method $1=(value)
93
108
  #
94
109
  # @macro [attach] define_reader
95
110
  # @!attribute [r] $1
@@ -101,6 +116,7 @@ module RSpec
101
116
  #
102
117
  # @note Other scripts invoking `rspec` indirectly will ignore this
103
118
  # setting.
119
+ # @return [String]
104
120
  add_read_only_setting :default_path
105
121
  def default_path=(path)
106
122
  project_source_dirs << path
@@ -110,6 +126,7 @@ module RSpec
110
126
  # @macro add_setting
111
127
  # Run examples over DRb (default: `false`). RSpec doesn't supply the DRb
112
128
  # server, but you can use tools like spork.
129
+ # @return [Boolean]
113
130
  add_setting :drb
114
131
 
115
132
  # @macro add_setting
@@ -122,6 +139,7 @@ module RSpec
122
139
 
123
140
  # Indicates if the DSL has been exposed off of modules and `main`.
124
141
  # Default: true
142
+ # @return [Boolean]
125
143
  def expose_dsl_globally?
126
144
  Core::DSL.exposed_globally?
127
145
  end
@@ -186,10 +204,33 @@ module RSpec
186
204
  only_failures? && !example_status_persistence_file_path
187
205
  end
188
206
 
189
- # @macro add_setting
207
+ # @macro define_reader
190
208
  # If specified, indicates the number of failures required before cleaning
191
- # up and exit (default: `nil`).
192
- add_setting :fail_fast
209
+ # up and exit (default: `nil`). Can also be `true` to fail and exit on first
210
+ # failure
211
+ define_reader :fail_fast
212
+
213
+ # @see fail_fast
214
+ def fail_fast=(value)
215
+ case value
216
+ when true, 'true'
217
+ @fail_fast = true
218
+ when false, 'false', 0
219
+ @fail_fast = false
220
+ when nil
221
+ @fail_fast = nil
222
+ else
223
+ @fail_fast = value.to_i
224
+
225
+ if value.to_i == 0
226
+ # TODO: in RSpec 4, consider raising an error here.
227
+ RSpec.warning "Cannot set `RSpec.configuration.fail_fast`" \
228
+ " to `#{value.inspect}`. Only `true`, `false`, `nil` and integers" \
229
+ " are valid values."
230
+ @fail_fast = true
231
+ end
232
+ end
233
+ end
193
234
 
194
235
  # @macro add_setting
195
236
  # Prints the formatter output of your suite without running any
@@ -198,24 +239,29 @@ module RSpec
198
239
 
199
240
  # @macro add_setting
200
241
  # The exit code to return if there are any failures (default: 1).
242
+ # @return [Integer]
201
243
  add_setting :failure_exit_code
202
244
 
203
245
  # @macro add_setting
204
246
  # Whether or not to fail when there are no RSpec examples (default: false).
247
+ # @return [Boolean]
205
248
  add_setting :fail_if_no_examples
206
249
 
207
250
  # @macro define_reader
208
251
  # Indicates files configured to be required.
252
+ # @return [Array<String>]
209
253
  define_reader :requires
210
254
 
211
255
  # @macro define_reader
212
256
  # Returns dirs that have been prepended to the load path by the `-I`
213
257
  # command line option.
258
+ # @return [Array<String>]
214
259
  define_reader :libs
215
260
 
216
261
  # @macro add_setting
217
262
  # Determines where RSpec will send its output.
218
263
  # Default: `$stdout`.
264
+ # @return [IO, String]
219
265
  define_reader :output_stream
220
266
 
221
267
  # Set the output stream for reporter.
@@ -234,6 +280,7 @@ module RSpec
234
280
 
235
281
  # @macro define_reader
236
282
  # Load files matching this pattern (default: `'**{,/*/**}/*_spec.rb'`).
283
+ # @return [String]
237
284
  define_reader :pattern
238
285
 
239
286
  # Set pattern to match files to load.
@@ -244,6 +291,7 @@ module RSpec
244
291
 
245
292
  # @macro define_reader
246
293
  # Exclude files matching this pattern.
294
+ # @return [String]
247
295
  define_reader :exclude_pattern
248
296
 
249
297
  # Set pattern to match files to exclude.
@@ -265,7 +313,9 @@ module RSpec
265
313
  # @macro add_setting
266
314
  # Report the times for the slowest examples (default: `false`).
267
315
  # Use this to specify the number of examples to include in the profile.
268
- add_setting :profile_examples
316
+ # @return [Boolean]
317
+ attr_writer :profile_examples
318
+ define_predicate :profile_examples
269
319
 
270
320
  # @macro add_setting
271
321
  # Run all examples if none match the configured filters
@@ -275,55 +325,56 @@ module RSpec
275
325
  add_setting :run_all_when_everything_filtered
276
326
 
277
327
  # @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]`
328
+ # Color to use to indicate success. Defaults to `:green` but can be set
329
+ # to one of the following: `[:black, :white, :red, :green, :yellow,
330
+ # :blue, :magenta, :cyan]`
331
+ # @return [Symbol]
282
332
  add_setting :success_color
283
333
 
284
334
  # @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]`
335
+ # Color to use to print pending examples. Defaults to `:yellow` but can
336
+ # be set to one of the following: `[:black, :white, :red, :green,
337
+ # :yellow, :blue, :magenta, :cyan]`
338
+ # @return [Symbol]
289
339
  add_setting :pending_color
290
340
 
291
341
  # @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]`
342
+ # Color to use to indicate failure. Defaults to `:red` but can be set to
343
+ # one of the following: `[:black, :white, :red, :green, :yellow, :blue,
344
+ # :magenta, :cyan]`
345
+ # @return [Symbol]
296
346
  add_setting :failure_color
297
347
 
298
348
  # @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]`
349
+ # The default output color. Defaults to `:white` but can be set to one of
350
+ # the following: `[:black, :white, :red, :green, :yellow, :blue,
351
+ # :magenta, :cyan]`
352
+ # @return [Symbol]
303
353
  add_setting :default_color
304
354
 
305
355
  # @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]`
356
+ # Color used when a pending example is fixed. Defaults to `:blue` but can
357
+ # be set to one of the following: `[:black, :white, :red, :green,
358
+ # :yellow, :blue, :magenta, :cyan]`
359
+ # @return [Symbol]
310
360
  add_setting :fixed_color
311
361
 
312
362
  # @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]`
363
+ # Color used to print details. Defaults to `:cyan` but can be set to one
364
+ # of the following: `[:black, :white, :red, :green, :yellow, :blue,
365
+ # :magenta, :cyan]`
366
+ # @return [Symbol]
317
367
  add_setting :detail_color
318
368
 
319
369
  # @macro add_setting
320
370
  # Don't print filter info i.e. "Run options: include {:focus=>true}"
321
371
  # (default `false`).
372
+ # return [Boolean]
322
373
  add_setting :silence_filter_announcements
323
374
 
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.
375
+ # @deprecated This config option was added in RSpec 2 to pave the way
376
+ # for this being the default behavior in RSpec 3. Now this option is
377
+ # a no-op.
327
378
  def treat_symbols_as_metadata_keys_with_true_values=(_value)
328
379
  RSpec.deprecate(
329
380
  "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values=",
@@ -387,18 +438,53 @@ module RSpec
387
438
  end
388
439
 
389
440
  # Record the start time of the spec suite to measure load time.
441
+ # return [Time]
390
442
  add_setting :start_time
391
443
 
392
444
  # @macro add_setting
393
445
  # Use threadsafe options where available.
394
446
  # Currently this will place a mutex around memoized values such as let blocks.
447
+ # return [Boolean]
395
448
  add_setting :threadsafe
396
449
 
397
450
  # @macro add_setting
398
451
  # Maximum count of failed source lines to display in the failure reports.
399
452
  # (default `10`).
453
+ # return [Integer]
400
454
  add_setting :max_displayed_failure_line_count
401
455
 
456
+ # Determines which bisect runner implementation gets used to run subsets
457
+ # of the suite during a bisection. Your choices are:
458
+ #
459
+ # - `:shell`: Performs a spec run by shelling out, booting RSpec and your
460
+ # application environment each time. This runner is the most widely
461
+ # compatible runner, but is not as fast. On platforms that do not
462
+ # support forking, this is the default.
463
+ # - `:fork`: Pre-boots RSpec and your application environment in a parent
464
+ # process, and then forks a child process for each spec run. This runner
465
+ # tends to be significantly faster than the `:shell` runner but cannot
466
+ # be used in some situations. On platforms that support forking, this
467
+ # is the default. If you use this runner, you should ensure that all
468
+ # of your one-time setup logic goes in a `before(:suite)` hook instead
469
+ # of getting run at the top-level of a file loaded by `--require`.
470
+ #
471
+ # @note This option will only be used by `--bisect` if you set it in a file
472
+ # loaded via `--require`.
473
+ #
474
+ # @return [Symbol]
475
+ attr_reader :bisect_runner
476
+ def bisect_runner=(value)
477
+ if @bisect_runner_class && value != @bisect_runner
478
+ raise "`config.bisect_runner = #{value.inspect}` can no longer take " \
479
+ "effect as the #{@bisect_runner.inspect} bisect runnner is already " \
480
+ "in use. This config setting must be set in a file loaded by a " \
481
+ "`--require` option (passed at the CLI or in a `.rspec` file) for " \
482
+ "it to have any effect."
483
+ end
484
+
485
+ @bisect_runner = value
486
+ end
487
+
402
488
  # @private
403
489
  # @deprecated Use {#color_mode} = :on, instead of {#color} with {#tty}
404
490
  add_setting :tty
@@ -411,8 +497,7 @@ module RSpec
411
497
  # @private
412
498
  attr_reader :backtrace_formatter, :ordering_manager, :loaded_spec_files
413
499
 
414
- # rubocop:disable Metrics/AbcSize
415
- # rubocop:disable Metrics/MethodLength
500
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
416
501
 
417
502
  # Build an object to store runtime configuration options and set defaults
418
503
  def initialize
@@ -424,6 +509,9 @@ module RSpec
424
509
  @extend_modules = FilterableItemRepository::QueryOptimized.new(:any?)
425
510
  @prepend_modules = FilterableItemRepository::QueryOptimized.new(:any?)
426
511
 
512
+ @bisect_runner = RSpec::Support::RubyFeatures.fork_supported? ? :fork : :shell
513
+ @bisect_runner_class = nil
514
+
427
515
  @before_suite_hooks = []
428
516
  @after_suite_hooks = []
429
517
 
@@ -467,8 +555,7 @@ module RSpec
467
555
 
468
556
  define_built_in_hooks
469
557
  end
470
- # rubocop:enable Metrics/MethodLength
471
- # rubocop:enable Metrics/AbcSize
558
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
472
559
 
473
560
  # @private
474
561
  #
@@ -549,6 +636,7 @@ module RSpec
549
636
  end
550
637
 
551
638
  # Returns the configured mock framework adapter module.
639
+ # @return [Symbol]
552
640
  def mock_framework
553
641
  if @mock_framework.nil?
554
642
  begin
@@ -576,12 +664,13 @@ module RSpec
576
664
  # To override this behaviour and display a full backtrace, use
577
665
  # `--backtrace` on the command line, in a `.rspec` file, or in the
578
666
  # `rspec_options` attribute of RSpec's rake task.
667
+ # @return [Array<Regexp>]
579
668
  def backtrace_exclusion_patterns
580
669
  @backtrace_formatter.exclusion_patterns
581
670
  end
582
671
 
583
672
  # Set regular expressions used to exclude lines in backtrace.
584
- # @param patterns [Regexp] set the backtrace exlusion pattern
673
+ # @param patterns [Array<Regexp>] set backtrace_formatter exlusion_patterns
585
674
  def backtrace_exclusion_patterns=(patterns)
586
675
  @backtrace_formatter.exclusion_patterns = patterns
587
676
  end
@@ -594,12 +683,13 @@ module RSpec
594
683
  # will be included.
595
684
  #
596
685
  # You can modify the list via the getter, or replace it with the setter.
686
+ # @return [Array<Regexp>]
597
687
  def backtrace_inclusion_patterns
598
688
  @backtrace_formatter.inclusion_patterns
599
689
  end
600
690
 
601
691
  # Set regular expressions used to include lines in backtrace.
602
- # @attr patterns [Regexp] set backtrace_formatter inclusion_patterns
692
+ # @attr patterns [Array<Regexp>] set backtrace_formatter inclusion_patterns
603
693
  def backtrace_inclusion_patterns=(patterns)
604
694
  @backtrace_formatter.inclusion_patterns = patterns
605
695
  end
@@ -862,11 +952,11 @@ module RSpec
862
952
  # @overload add_formatter(formatter)
863
953
  # @overload add_formatter(formatter, output)
864
954
  #
865
- # @param formatter [Class, String] formatter to use. Can be any of the
955
+ # @param formatter [Class, String, Object] formatter to use. Can be any of the
866
956
  # string values supported from the CLI (`p`/`progress`,
867
- # `d`/`doc`/`documentation`, `h`/`html`, or `j`/`json`) or any
957
+ # `d`/`doc`/`documentation`, `h`/`html`, or `j`/`json`), any
868
958
  # class that implements the formatter protocol and has registered
869
- # itself with RSpec as a formatter.
959
+ # itself with RSpec as a formatter, or a formatter instance.
870
960
  # @param output [String, IO] where the formatter will write its output.
871
961
  # Can be an IO object or a string path to a file. If not provided,
872
962
  # the configured `output_stream` (`$stdout`, by default) will be used.
@@ -1042,7 +1132,7 @@ module RSpec
1042
1132
  #
1043
1133
  # # This lets you do this:
1044
1134
  #
1045
- # describe Thing do
1135
+ # RSpec.describe Thing do
1046
1136
  # pending "does something" do
1047
1137
  # thing = Thing.new
1048
1138
  # end
@@ -1050,7 +1140,7 @@ module RSpec
1050
1140
  #
1051
1141
  # # ... which is the equivalent of
1052
1142
  #
1053
- # describe Thing do
1143
+ # RSpec.describe Thing do
1054
1144
  # it "does something", :pending => true do
1055
1145
  # thing = Thing.new
1056
1146
  # end
@@ -1103,7 +1193,7 @@ module RSpec
1103
1193
  #
1104
1194
  # # allows the user to include a shared example group like:
1105
1195
  #
1106
- # describe Entity do
1196
+ # RSpec.describe Entity do
1107
1197
  # it_has_behavior 'sortability' do
1108
1198
  # let(:sortable) { Entity.new }
1109
1199
  # end
@@ -1263,6 +1353,12 @@ module RSpec
1263
1353
  # end
1264
1354
  # end
1265
1355
  #
1356
+ # module PreferencesHelpers
1357
+ # def preferences(user, preferences = {})
1358
+ # # ...
1359
+ # end
1360
+ # end
1361
+ #
1266
1362
  # module UserHelpers
1267
1363
  # def users(username)
1268
1364
  # # ...
@@ -1271,12 +1367,17 @@ module RSpec
1271
1367
  #
1272
1368
  # RSpec.configure do |config|
1273
1369
  # config.include(UserHelpers) # included in all groups
1370
+ #
1371
+ # # included in examples with `:preferences` metadata
1372
+ # config.include(PreferenceHelpers, :preferences)
1373
+ #
1374
+ # # included in examples with `:type => :request` metadata
1274
1375
  # config.include(AuthenticationHelpers, :type => :request)
1275
1376
  # end
1276
1377
  #
1277
- # describe "edit profile", :type => :request do
1378
+ # describe "edit profile", :preferences, :type => :request do
1278
1379
  # it "can be viewed by owning user" do
1279
- # login_as users(:jdoe)
1380
+ # login_as preferences(users(:jdoe), :lang => 'es')
1280
1381
  # get "/profiles/jdoe"
1281
1382
  # assert_select ".username", :text => 'jdoe'
1282
1383
  # end
@@ -1304,17 +1405,21 @@ module RSpec
1304
1405
  #
1305
1406
  # @example
1306
1407
  #
1307
- # RSpec.shared_context "example users" do
1408
+ # RSpec.shared_context "example admin user" do
1308
1409
  # let(:admin_user) { create_user(:admin) }
1410
+ # end
1411
+ #
1412
+ # RSpec.shared_context "example guest user" do
1309
1413
  # let(:guest_user) { create_user(:guest) }
1310
1414
  # end
1311
1415
  #
1312
1416
  # RSpec.configure do |config|
1313
- # config.include_context "example users", :type => :request
1417
+ # config.include_context "example guest user", :type => :request
1418
+ # config.include_context "example admin user", :admin, :type => :request
1314
1419
  # end
1315
1420
  #
1316
1421
  # RSpec.describe "The admin page", :type => :request do
1317
- # it "can be viewed by admins" do
1422
+ # it "can be viewed by admins", :admin do
1318
1423
  # login_with admin_user
1319
1424
  # get "/admin"
1320
1425
  # expect(response).to be_ok
@@ -1356,12 +1461,20 @@ module RSpec
1356
1461
  # end
1357
1462
  # end
1358
1463
  #
1464
+ # module PermissionHelpers
1465
+ # def define_permissions
1466
+ # # ...
1467
+ # end
1468
+ # end
1469
+ #
1359
1470
  # RSpec.configure do |config|
1360
1471
  # config.extend(UiHelpers, :type => :request)
1472
+ # config.extend(PermissionHelpers, :with_permissions, :type => :request)
1361
1473
  # end
1362
1474
  #
1363
- # describe "edit profile", :type => :request do
1475
+ # describe "edit profile", :with_permissions, :type => :request do
1364
1476
  # run_in_browser
1477
+ # define_permissions
1365
1478
  #
1366
1479
  # it "does stuff in the client" do
1367
1480
  # # ...
@@ -1452,7 +1565,7 @@ module RSpec
1452
1565
  def requires=(paths)
1453
1566
  directories = ['lib', default_path].select { |p| File.directory? p }
1454
1567
  RSpec::Core::RubyProject.add_to_load_path(*directories)
1455
- paths.each { |path| require path }
1568
+ paths.each { |path| load_file_handling_errors(:require, path) }
1456
1569
  @requires += paths
1457
1570
  end
1458
1571
 
@@ -1493,7 +1606,7 @@ module RSpec
1493
1606
 
1494
1607
  files_to_run.uniq.each do |f|
1495
1608
  file = File.expand_path(f)
1496
- load_spec_file_handling_errors(file)
1609
+ load_file_handling_errors(:load, file)
1497
1610
  loaded_spec_files << file
1498
1611
  end
1499
1612
 
@@ -1521,8 +1634,6 @@ module RSpec
1521
1634
  end
1522
1635
 
1523
1636
  # @private
1524
- # @macro [attach] delegate_to_ordering_manager
1525
- # @!method $1
1526
1637
  def self.delegate_to_ordering_manager(*methods)
1527
1638
  methods.each do |method|
1528
1639
  define_method method do |*args, &block|
@@ -1531,12 +1642,12 @@ module RSpec
1531
1642
  end
1532
1643
  end
1533
1644
 
1534
- # @macro delegate_to_ordering_manager
1645
+ # @!method seed=(value)
1535
1646
  #
1536
1647
  # Sets the seed value and sets the default global ordering to random.
1537
1648
  delegate_to_ordering_manager :seed=
1538
1649
 
1539
- # @macro delegate_to_ordering_manager
1650
+ # @!method seed
1540
1651
  # Seed for random ordering (default: generated randomly each run).
1541
1652
  #
1542
1653
  # When you run specs with `--order random`, RSpec generates a random seed
@@ -1550,7 +1661,7 @@ module RSpec
1550
1661
  # don't accidentally leave the seed encoded.
1551
1662
  delegate_to_ordering_manager :seed
1552
1663
 
1553
- # @macro delegate_to_ordering_manager
1664
+ # @!method order=(value)
1554
1665
  #
1555
1666
  # Sets the default global ordering strategy. By default this can be one
1556
1667
  # of `:defined`, `:random`, but is customizable through the
@@ -1560,7 +1671,8 @@ module RSpec
1560
1671
  # @see #register_ordering
1561
1672
  delegate_to_ordering_manager :order=
1562
1673
 
1563
- # @macro delegate_to_ordering_manager
1674
+ # @!method register_ordering(name)
1675
+ #
1564
1676
  # Registers a named ordering strategy that can later be
1565
1677
  # used to order an example group's subgroups by adding
1566
1678
  # `:order => <name>` metadata to the example group.
@@ -1655,7 +1767,7 @@ module RSpec
1655
1767
  # rspec.expose_current_running_example_as :example
1656
1768
  # end
1657
1769
  #
1658
- # describe MyClass do
1770
+ # RSpec.describe MyClass do
1659
1771
  # before do
1660
1772
  # # `example` can be used here because of the above config.
1661
1773
  # do_something if example.metadata[:type] == "foo"
@@ -1773,7 +1885,7 @@ module RSpec
1773
1885
  # require 'support/db'
1774
1886
  # end
1775
1887
  # end
1776
- def when_first_matching_example_defined(*filters, &block)
1888
+ def when_first_matching_example_defined(*filters)
1777
1889
  specified_meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
1778
1890
 
1779
1891
  callback = lambda do |example_or_group_meta|
@@ -1784,7 +1896,7 @@ module RSpec
1784
1896
  # Ensure the callback only fires once.
1785
1897
  @derived_metadata_blocks.delete(callback, specified_meta)
1786
1898
 
1787
- block.call
1899
+ yield
1788
1900
  end
1789
1901
 
1790
1902
  @derived_metadata_blocks.append(callback, specified_meta)
@@ -1792,16 +1904,36 @@ module RSpec
1792
1904
 
1793
1905
  # @private
1794
1906
  def apply_derived_metadata_to(metadata)
1795
- @derived_metadata_blocks.items_for(metadata).each do |block|
1796
- block.call(metadata)
1907
+ already_run_blocks = Set.new
1908
+
1909
+ # We loop and attempt to re-apply metadata blocks to support cascades
1910
+ # (e.g. where a derived bit of metadata triggers the application of
1911
+ # another piece of derived metadata, etc)
1912
+ #
1913
+ # We limit our looping to 200 times as a way to detect infinitely recursing derived metadata blocks.
1914
+ # It's hard to imagine a valid use case for a derived metadata cascade greater than 200 iterations.
1915
+ 200.times do
1916
+ return if @derived_metadata_blocks.items_for(metadata).all? do |block|
1917
+ already_run_blocks.include?(block).tap do |skip_block|
1918
+ block.call(metadata) unless skip_block
1919
+ already_run_blocks << block
1920
+ end
1921
+ end
1797
1922
  end
1923
+
1924
+ # If we got here, then `@derived_metadata_blocks.items_for(metadata).all?` never returned
1925
+ # `true` above and we treat this as an attempt to recurse infinitely. It's better to fail
1926
+ # with a clear # error than hang indefinitely, which is what would happen if we didn't limit
1927
+ # the looping above.
1928
+ raise SystemStackError, "Attempted to recursively derive metadata indefinitely."
1798
1929
  end
1799
1930
 
1800
1931
  # Defines a `before` hook. See {Hooks#before} for full docs.
1801
1932
  #
1802
1933
  # This method differs from {Hooks#before} in only one way: it supports
1803
1934
  # the `:suite` scope. Hooks with the `:suite` scope will be run once before
1804
- # the first example of the entire suite is executed.
1935
+ # the first example of the entire suite is executed. Conditions passed along
1936
+ # with `:suite` are effectively ignored.
1805
1937
  #
1806
1938
  # @see #prepend_before
1807
1939
  # @see #after
@@ -1830,7 +1962,8 @@ module RSpec
1830
1962
  #
1831
1963
  # This method differs from {Hooks#prepend_before} in only one way: it supports
1832
1964
  # the `:suite` scope. Hooks with the `:suite` scope will be run once before
1833
- # the first example of the entire suite is executed.
1965
+ # the first example of the entire suite is executed. Conditions passed along
1966
+ # with `:suite` are effectively ignored.
1834
1967
  #
1835
1968
  # @see #before
1836
1969
  # @see #after
@@ -1854,7 +1987,8 @@ module RSpec
1854
1987
  #
1855
1988
  # This method differs from {Hooks#after} in only one way: it supports
1856
1989
  # the `:suite` scope. Hooks with the `:suite` scope will be run once after
1857
- # the last example of the entire suite is executed.
1990
+ # the last example of the entire suite is executed. Conditions passed along
1991
+ # with `:suite` are effectively ignored.
1858
1992
  #
1859
1993
  # @see #append_after
1860
1994
  # @see #before
@@ -1883,7 +2017,8 @@ module RSpec
1883
2017
  #
1884
2018
  # This method differs from {Hooks#append_after} in only one way: it supports
1885
2019
  # the `:suite` scope. Hooks with the `:suite` scope will be run once after
1886
- # the last example of the entire suite is executed.
2020
+ # the last example of the entire suite is executed. Conditions passed along
2021
+ # with `:suite` are effectively ignored.
1887
2022
  #
1888
2023
  # @see #append_after
1889
2024
  # @see #before
@@ -1948,10 +2083,32 @@ module RSpec
1948
2083
  @on_example_group_definition_callbacks ||= []
1949
2084
  end
1950
2085
 
2086
+ # @private
2087
+ def bisect_runner_class
2088
+ @bisect_runner_class ||= begin
2089
+ case bisect_runner
2090
+ when :fork
2091
+ RSpec::Support.require_rspec_core 'bisect/fork_runner'
2092
+ Bisect::ForkRunner
2093
+ when :shell
2094
+ RSpec::Support.require_rspec_core 'bisect/shell_runner'
2095
+ Bisect::ShellRunner
2096
+ else
2097
+ raise "Unsupported value for `bisect_runner` (#{bisect_runner.inspect}). " \
2098
+ "Only `:fork` and `:shell` are supported."
2099
+ end
2100
+ end
2101
+ end
2102
+
1951
2103
  private
1952
2104
 
1953
- def load_spec_file_handling_errors(file)
1954
- load file
2105
+ def load_file_handling_errors(method, file)
2106
+ __send__(method, file)
2107
+ rescue LoadError => ex
2108
+ relative_file = Metadata.relative_path(file)
2109
+ suggestions = DidYouMean.new(relative_file).call
2110
+ reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.#{suggestions}")
2111
+ RSpec.world.wants_to_quit = true
1955
2112
  rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
1956
2113
  relative_file = Metadata.relative_path(file)
1957
2114
  reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.")