rspec-core 3.7.1 → 3.9.3

Sign up to get free protection for your applications and to get access to all the features.
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}.")