rspec-core 3.7.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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}.")