rspec-core 3.0.4 → 3.12.2

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 (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +2 -1
  5. data/Changelog.md +888 -2
  6. data/{License.txt → LICENSE.md} +6 -5
  7. data/README.md +165 -24
  8. data/lib/rspec/autorun.rb +1 -0
  9. data/lib/rspec/core/backtrace_formatter.rb +19 -20
  10. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  11. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  12. data/lib/rspec/core/bisect/fork_runner.rb +138 -0
  13. data/lib/rspec/core/bisect/server.rb +61 -0
  14. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  15. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  16. data/lib/rspec/core/bisect/utilities.rb +69 -0
  17. data/lib/rspec/core/configuration.rb +1287 -246
  18. data/lib/rspec/core/configuration_options.rb +95 -35
  19. data/lib/rspec/core/did_you_mean.rb +46 -0
  20. data/lib/rspec/core/drb.rb +21 -12
  21. data/lib/rspec/core/dsl.rb +10 -6
  22. data/lib/rspec/core/example.rb +305 -113
  23. data/lib/rspec/core/example_group.rb +431 -223
  24. data/lib/rspec/core/example_status_persister.rb +235 -0
  25. data/lib/rspec/core/filter_manager.rb +86 -115
  26. data/lib/rspec/core/flat_map.rb +6 -4
  27. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  28. data/lib/rspec/core/formatters/base_formatter.rb +14 -116
  29. data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
  30. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  31. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  32. data/lib/rspec/core/formatters/console_codes.rb +29 -18
  33. data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
  34. data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
  35. data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
  36. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  37. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  38. data/lib/rspec/core/formatters/helpers.rb +45 -15
  39. data/lib/rspec/core/formatters/html_formatter.rb +33 -28
  40. data/lib/rspec/core/formatters/html_printer.rb +30 -20
  41. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  42. data/lib/rspec/core/formatters/json_formatter.rb +18 -9
  43. data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
  44. data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
  45. data/lib/rspec/core/formatters/protocol.rb +182 -0
  46. data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
  47. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  48. data/lib/rspec/core/formatters.rb +81 -41
  49. data/lib/rspec/core/hooks.rb +314 -244
  50. data/lib/rspec/core/invocations.rb +87 -0
  51. data/lib/rspec/core/memoized_helpers.rb +161 -51
  52. data/lib/rspec/core/metadata.rb +132 -61
  53. data/lib/rspec/core/metadata_filter.rb +224 -64
  54. data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
  55. data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
  56. data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
  57. data/lib/rspec/core/mocking_adapters/null.rb +2 -0
  58. data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
  59. data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
  60. data/lib/rspec/core/notifications.rb +192 -206
  61. data/lib/rspec/core/option_parser.rb +174 -69
  62. data/lib/rspec/core/ordering.rb +48 -35
  63. data/lib/rspec/core/output_wrapper.rb +29 -0
  64. data/lib/rspec/core/pending.rb +25 -33
  65. data/lib/rspec/core/profiler.rb +34 -0
  66. data/lib/rspec/core/project_initializer/.rspec +0 -2
  67. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
  68. data/lib/rspec/core/project_initializer.rb +5 -3
  69. data/lib/rspec/core/rake_task.rb +99 -55
  70. data/lib/rspec/core/reporter.rb +128 -15
  71. data/lib/rspec/core/ruby_project.rb +14 -6
  72. data/lib/rspec/core/runner.rb +96 -45
  73. data/lib/rspec/core/sandbox.rb +37 -0
  74. data/lib/rspec/core/set.rb +54 -0
  75. data/lib/rspec/core/shared_example_group.rb +133 -43
  76. data/lib/rspec/core/shell_escape.rb +49 -0
  77. data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
  78. data/lib/rspec/core/version.rb +1 -1
  79. data/lib/rspec/core/warnings.rb +6 -6
  80. data/lib/rspec/core/world.rb +172 -68
  81. data/lib/rspec/core.rb +66 -21
  82. data.tar.gz.sig +0 -0
  83. metadata +93 -69
  84. metadata.gz.sig +0 -0
  85. data/lib/rspec/core/backport_random.rb +0 -336
@@ -9,7 +9,7 @@ module RSpec
9
9
  #
10
10
  # This allows us to provide rich metadata about each individual
11
11
  # example without adding tons of methods directly to the ExampleGroup
12
- # that users may inadvertantly redefine.
12
+ # that users may inadvertently redefine.
13
13
  #
14
14
  # Useful for configuring logging and/or taking some action based
15
15
  # on the state of an example's metadata.
@@ -44,14 +44,15 @@ module RSpec
44
44
  class Example
45
45
  # @private
46
46
  #
47
- # Used to define methods that delegate to this example's metadata
47
+ # Used to define methods that delegate to this example's metadata.
48
48
  def self.delegate_to_metadata(key)
49
49
  define_method(key) { @metadata[key] }
50
50
  end
51
51
 
52
52
  # @return [ExecutionResult] represents the result of running this example.
53
53
  delegate_to_metadata :execution_result
54
- # @return [String] the relative path to the file where this example was defined.
54
+ # @return [String] the relative path to the file where this example was
55
+ # defined.
55
56
  delegate_to_metadata :file_path
56
57
  # @return [String] the full description (including the docstrings of
57
58
  # all parent example groups).
@@ -59,30 +60,101 @@ module RSpec
59
60
  # @return [String] the exact source location of this example in a form
60
61
  # like `./path/to/spec.rb:17`
61
62
  delegate_to_metadata :location
62
- # @return [Boolean] flag that indicates that the example is not expected to pass.
63
- # It will be run and will either have a pending result (if a failure occurs)
64
- # or a failed result (if no failure occurs).
63
+ # @return [Boolean] flag that indicates that the example is not expected
64
+ # to pass. It will be run and will either have a pending result (if a
65
+ # failure occurs) or a failed result (if no failure occurs).
65
66
  delegate_to_metadata :pending
66
67
  # @return [Boolean] flag that will cause the example to not run.
67
68
  # The {ExecutionResult} status will be `:pending`.
68
69
  delegate_to_metadata :skip
69
70
 
70
71
  # Returns the string submitted to `example` or its aliases (e.g.
71
- # `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to
72
- # do_something }`) it returns the message generated by the matcher if
73
- # there is one, otherwise returns a message including the location of the
74
- # example.
72
+ # `specify`, `it`, etc). If no string is submitted (e.g.
73
+ # `it { is_expected.to do_something }`) it returns the message generated
74
+ # by the matcher if there is one, otherwise returns a message including
75
+ # the location of the example.
75
76
  def description
76
- description = metadata[:description].to_s.empty? ?
77
- "example at #{location}" :
78
- metadata[:description]
77
+ description = if metadata[:description].to_s.empty?
78
+ location_description
79
+ else
80
+ metadata[:description]
81
+ end
82
+
79
83
  RSpec.configuration.format_docstrings_block.call(description)
80
84
  end
81
85
 
86
+ # Returns a description of the example that always includes the location.
87
+ def inspect_output
88
+ inspect_output = "\"#{description}\""
89
+ unless metadata[:description].to_s.empty?
90
+ inspect_output += " (#{location})"
91
+ end
92
+ inspect_output
93
+ end
94
+
95
+ # Returns the location-based argument that can be passed to the `rspec` command to rerun this example.
96
+ def location_rerun_argument
97
+ @location_rerun_argument ||= begin
98
+ loaded_spec_files = RSpec.configuration.loaded_spec_files
99
+
100
+ Metadata.ascending(metadata) do |meta|
101
+ return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
102
+ end
103
+ end
104
+ end
105
+
106
+ # Returns the location-based argument that can be passed to the `rspec` command to rerun this example.
107
+ #
108
+ # @deprecated Use {#location_rerun_argument} instead.
109
+ # @note If there are multiple examples identified by this location, they will use {#id}
110
+ # to rerun instead, but this method will still return the location (that's why it is deprecated!).
111
+ def rerun_argument
112
+ location_rerun_argument
113
+ end
114
+
115
+ # @return [String] the unique id of this example. Pass
116
+ # this at the command line to re-run this exact example.
117
+ def id
118
+ @id ||= Metadata.id_from(metadata)
119
+ end
120
+
121
+ # @private
122
+ def self.parse_id(id)
123
+ # http://rubular.com/r/OMZSAPcAfn
124
+ id.match(/\A(.*?)(?:\[([\d\s:,]+)\])?\z/).captures
125
+ end
126
+
127
+ # Duplicates the example and overrides metadata with the provided
128
+ # hash.
129
+ #
130
+ # @param metadata_overrides [Hash] the hash to override the example metadata
131
+ # @return [Example] a duplicate of the example with modified metadata
132
+ def duplicate_with(metadata_overrides={})
133
+ new_metadata = metadata.clone.merge(metadata_overrides)
134
+
135
+ RSpec::Core::Metadata::RESERVED_KEYS.each do |reserved_key|
136
+ new_metadata.delete reserved_key
137
+ end
138
+
139
+ # don't clone the example group because the new example
140
+ # must belong to the same example group (not a clone).
141
+ #
142
+ # block is nil in new_metadata so we have to get it from metadata.
143
+ Example.new(example_group, description.clone,
144
+ new_metadata, metadata[:block])
145
+ end
146
+
147
+ # @private
148
+ def update_inherited_metadata(updates)
149
+ metadata.update(updates) do |_key, existing_example_value, _new_inherited_value|
150
+ existing_example_value
151
+ end
152
+ end
153
+
82
154
  # @attr_reader
83
155
  #
84
156
  # Returns the first exception raised in the context of running this
85
- # example (nil if no exception is raised)
157
+ # example (nil if no exception is raised).
86
158
  attr_reader :exception
87
159
 
88
160
  # @attr_reader
@@ -102,31 +174,70 @@ module RSpec
102
174
  attr_accessor :clock
103
175
 
104
176
  # Creates a new instance of Example.
105
- # @param example_group_class [Class] the subclass of ExampleGroup in which this Example is declared
106
- # @param description [String] the String passed to the `it` method (or alias)
107
- # @param user_metadata [Hash] additional args passed to `it` to be used as metadata
108
- # @param example_block [Proc] the block of code that represents the example
177
+ # @param example_group_class [Class] the subclass of ExampleGroup in which
178
+ # this Example is declared
179
+ # @param description [String] the String passed to the `it` method (or
180
+ # alias)
181
+ # @param user_metadata [Hash] additional args passed to `it` to be used as
182
+ # metadata
183
+ # @param example_block [Proc] the block of code that represents the
184
+ # example
109
185
  # @api private
110
186
  def initialize(example_group_class, description, user_metadata, example_block=nil)
111
187
  @example_group_class = example_group_class
112
188
  @example_block = example_block
113
189
 
190
+ # Register the example with the group before creating the metadata hash.
191
+ # This is necessary since creating the metadata hash triggers
192
+ # `when_first_matching_example_defined` callbacks, in which users can
193
+ # load RSpec support code which defines hooks. For that to work, the
194
+ # examples and example groups must be registered at the time the
195
+ # support code is called or be defined afterwards.
196
+ # Begin defined beforehand but registered afterwards causes hooks to
197
+ # not be applied where they should.
198
+ example_group_class.examples << self
199
+
114
200
  @metadata = Metadata::ExampleHash.create(
115
- @example_group_class.metadata, user_metadata, description, example_block
201
+ @example_group_class.metadata, user_metadata,
202
+ example_group_class.method(:next_runnable_index_for),
203
+ description, example_block
116
204
  )
117
205
 
206
+ config = RSpec.configuration
207
+ config.apply_derived_metadata_to(@metadata)
208
+
209
+ # This should perhaps be done in `Metadata::ExampleHash.create`,
210
+ # but the logic there has no knowledge of `RSpec.world` and we
211
+ # want to keep it that way. It's easier to just assign it here.
212
+ @metadata[:last_run_status] = config.last_run_statuses[id]
213
+
118
214
  @example_group_instance = @exception = nil
119
215
  @clock = RSpec::Core::Time
216
+ @reporter = RSpec::Core::NullReporter
120
217
  end
121
218
 
219
+ # Provide a human-readable representation of this class
220
+ def inspect
221
+ "#<#{self.class.name} #{description.inspect}>"
222
+ end
223
+ alias to_s inspect
224
+
225
+ # @return [RSpec::Core::Reporter] the current reporter for the example
226
+ attr_reader :reporter
227
+
122
228
  # Returns the example group class that provides the context for running
123
229
  # this example.
124
230
  def example_group
125
231
  @example_group_class
126
232
  end
127
233
 
128
- alias_method :pending?, :pending
129
- alias_method :skipped?, :skip
234
+ def pending?
235
+ !!pending
236
+ end
237
+
238
+ def skipped?
239
+ !!skip
240
+ end
130
241
 
131
242
  # @api private
132
243
  # instance_execs the block passed to the constructor in the context of
@@ -134,50 +245,71 @@ module RSpec
134
245
  # @param example_group_instance the instance of an ExampleGroup subclass
135
246
  def run(example_group_instance, reporter)
136
247
  @example_group_instance = example_group_instance
248
+ @reporter = reporter
249
+ RSpec.configuration.configure_example(self, hooks)
137
250
  RSpec.current_example = self
138
251
 
139
252
  start(reporter)
253
+ Pending.mark_pending!(self, pending) if pending?
140
254
 
141
255
  begin
142
256
  if skipped?
143
257
  Pending.mark_pending! self, skip
144
258
  elsif !RSpec.configuration.dry_run?
145
- with_around_example_hooks do
259
+ with_around_and_singleton_context_hooks do
146
260
  begin
147
261
  run_before_example
262
+ RSpec.current_scope = :example
148
263
  @example_group_instance.instance_exec(self, &@example_block)
149
264
 
150
265
  if pending?
151
266
  Pending.mark_fixed! self
152
267
 
153
268
  raise Pending::PendingExampleFixedError,
154
- 'Expected example to fail since it is pending, but it passed.',
155
- [location]
269
+ 'Expected example to fail since it is pending, but it passed.',
270
+ [location]
156
271
  end
157
- rescue Pending::SkipDeclaredInExample
272
+ rescue Pending::SkipDeclaredInExample => _
273
+ # The "=> _" is normally useless but on JRuby it is a workaround
274
+ # for a bug that prevents us from getting backtraces:
275
+ # https://github.com/jruby/jruby/issues/4467
276
+ #
158
277
  # no-op, required metadata has already been set by the `skip`
159
278
  # method.
160
- rescue Exception => e
279
+ rescue AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt => e
161
280
  set_exception(e)
162
281
  ensure
282
+ RSpec.current_scope = :after_example_hook
163
283
  run_after_example
164
284
  end
165
285
  end
166
286
  end
167
- rescue Exception => e
287
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
168
288
  set_exception(e)
169
289
  ensure
170
- @example_group_instance.instance_variables.each do |ivar|
171
- @example_group_instance.instance_variable_set(ivar, nil)
172
- end
173
- @example_group_instance = nil
290
+ @example_group_instance = nil # if you love something... let it go
174
291
  end
175
292
 
176
293
  finish(reporter)
177
294
  ensure
295
+ execution_result.ensure_timing_set(clock)
178
296
  RSpec.current_example = nil
179
297
  end
180
298
 
299
+ if RSpec::Support::Ruby.jruby? || RUBY_VERSION.to_f < 1.9
300
+ # :nocov:
301
+ # For some reason, rescuing `Support::AllExceptionsExceptOnesWeMustNotRescue`
302
+ # in place of `Exception` above can cause the exit status to be the wrong
303
+ # thing. I have no idea why. See:
304
+ # https://github.com/rspec/rspec-core/pull/2063#discussion_r38284978
305
+ # @private
306
+ AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt = Exception
307
+ # :nocov:
308
+ else
309
+ # @private
310
+ AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt = Support::AllExceptionsExceptOnesWeMustNotRescue
311
+ end
312
+
181
313
  # Wraps both a `Proc` and an {Example} for use in {Hooks#around
182
314
  # around} hooks. In around hooks we need to yield this special
183
315
  # kind of object (rather than the raw {Example}) because when
@@ -190,7 +322,7 @@ module RSpec
190
322
  # if ex.metadata[:key] == :some_value && some_global_condition
191
323
  # raise "some message"
192
324
  # end
193
- # ex.run # run delegates to ex.call
325
+ # ex.run # run delegates to ex.call.
194
326
  # end
195
327
  # end
196
328
  #
@@ -201,67 +333,105 @@ module RSpec
201
333
  attr_reader :example
202
334
 
203
335
  Example.public_instance_methods(false).each do |name|
204
- unless name.to_sym == :run
205
- define_method(name) { |*a, &b| @example.__send__(name, *a, &b) }
206
- end
336
+ name_sym = name.to_sym
337
+ next if name_sym == :run || name_sym == :inspect || name_sym == :to_s
338
+
339
+ define_method(name) { |*a, &b| @example.__send__(name, *a, &b) }
207
340
  end
208
341
 
209
342
  Proc.public_instance_methods(false).each do |name|
343
+ name_sym = name.to_sym
344
+ next if name_sym == :call || name_sym == :inspect || name_sym == :to_s || name_sym == :to_proc
345
+
210
346
  define_method(name) { |*a, &b| @proc.__send__(name, *a, &b) }
211
347
  end
348
+
349
+ # Calls the proc and notes that the example has been executed.
350
+ def call(*args, &block)
351
+ @executed = true
352
+ @proc.call(*args, &block)
353
+ end
212
354
  alias run call
213
355
 
356
+ # Provides a wrapped proc that will update our `executed?` state when
357
+ # executed.
358
+ def to_proc
359
+ method(:call).to_proc
360
+ end
361
+
214
362
  def initialize(example, &block)
215
- @example = example
216
- @proc = block
363
+ @example = example
364
+ @proc = block
365
+ @executed = false
217
366
  end
218
367
 
219
368
  # @private
220
369
  def wrap(&block)
221
370
  self.class.new(example, &block)
222
371
  end
223
- end
224
372
 
225
- # @private
226
- def any_apply?(filters)
227
- MetadataFilter.any_apply?(filters, metadata)
373
+ # Indicates whether or not the around hook has executed the example.
374
+ def executed?
375
+ @executed
376
+ end
377
+
378
+ # @private
379
+ def inspect
380
+ @example.inspect.gsub('Example', 'Example::Procsy')
381
+ end
228
382
  end
229
383
 
230
384
  # @private
231
- def all_apply?(filters)
232
- MetadataFilter.all_apply?(filters, metadata) || @example_group_class.all_apply?(filters)
385
+ #
386
+ # The exception that will be displayed to the user -- either the failure of
387
+ # the example or the `pending_exception` if the example is pending.
388
+ def display_exception
389
+ @exception || execution_result.pending_exception
233
390
  end
234
391
 
235
392
  # @private
236
- def around_example_hooks
237
- @around_example_hooks ||= example_group.hooks.around_example_hooks_for(self)
393
+ #
394
+ # Assigns the exception that will be displayed to the user -- either the failure of
395
+ # the example or the `pending_exception` if the example is pending.
396
+ def display_exception=(ex)
397
+ if pending? && !(Pending::PendingExampleFixedError === ex)
398
+ @exception = nil
399
+ execution_result.pending_fixed = false
400
+ execution_result.pending_exception = ex
401
+ else
402
+ @exception = ex
403
+ end
238
404
  end
239
405
 
406
+ # rubocop:disable Naming/AccessorMethodName
407
+
240
408
  # @private
241
409
  #
242
410
  # Used internally to set an exception in an after hook, which
243
411
  # captures the exception but doesn't raise it.
244
- def set_exception(exception, context=nil)
245
- if pending? && !(Pending::PendingExampleFixedError === exception)
246
- execution_result.pending_exception = exception
247
- else
248
- if @exception
249
- # An error has already been set; we don't want to override it,
250
- # but we also don't want silence the error, so let's print it.
251
- msg = <<-EOS
412
+ def set_exception(exception)
413
+ return self.display_exception = exception unless display_exception
252
414
 
253
- An error occurred #{context}
254
- #{exception.class}: #{exception.message}
255
- occurred at #{exception.backtrace.first}
415
+ unless RSpec::Core::MultipleExceptionError === display_exception
416
+ self.display_exception = RSpec::Core::MultipleExceptionError.new(display_exception)
417
+ end
256
418
 
257
- EOS
258
- RSpec.configuration.reporter.message(msg)
259
- end
419
+ display_exception.add exception
420
+ end
260
421
 
261
- @exception ||= exception
262
- end
422
+ # @private
423
+ #
424
+ # Used to set the exception when `aggregate_failures` fails.
425
+ def set_aggregate_failures_exception(exception)
426
+ return set_exception(exception) unless display_exception
427
+
428
+ exception = RSpec::Core::MultipleExceptionError::InterfaceTag.for(exception)
429
+ exception.add display_exception
430
+ self.display_exception = exception
263
431
  end
264
432
 
433
+ # rubocop:enable Naming/AccessorMethodName
434
+
265
435
  # @private
266
436
  #
267
437
  # Used internally to set an exception and fail without actually executing
@@ -275,20 +445,13 @@ module RSpec
275
445
  # @private
276
446
  #
277
447
  # Used internally to skip without actually executing the example when
278
- # skip is used in before(:context)
448
+ # skip is used in before(:context).
279
449
  def skip_with_exception(reporter, exception)
280
450
  start(reporter)
281
451
  Pending.mark_skipped! self, exception.argument
282
452
  finish(reporter)
283
453
  end
284
454
 
285
- # @private
286
- def instance_exec_with_rescue(context, &block)
287
- @example_group_instance.instance_exec(self, &block)
288
- rescue Exception => e
289
- set_exception(e, context)
290
- end
291
-
292
455
  # @private
293
456
  def instance_exec(*args, &block)
294
457
  @example_group_instance.instance_exec(*args, &block)
@@ -296,14 +459,15 @@ module RSpec
296
459
 
297
460
  private
298
461
 
299
- def with_around_example_hooks(&block)
300
- if around_example_hooks.empty?
301
- yield
302
- else
303
- @example_group_class.hooks.run(:around, :example, self, Procsy.new(self, &block))
304
- end
305
- rescue Exception => e
306
- set_exception(e, "in an `around(:example)` hook")
462
+ def hooks
463
+ example_group_instance.singleton_class.hooks
464
+ end
465
+
466
+ def with_around_example_hooks
467
+ RSpec.current_scope = :before_example_hook
468
+ hooks.run(:around, :example, self) { yield }
469
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
470
+ set_exception(e)
307
471
  end
308
472
 
309
473
  def start(reporter)
@@ -315,50 +479,52 @@ module RSpec
315
479
  pending_message = execution_result.pending_message
316
480
 
317
481
  if @exception
318
- record_finished :failed
319
482
  execution_result.exception = @exception
483
+ record_finished :failed, reporter
320
484
  reporter.example_failed self
321
485
  false
322
486
  elsif pending_message
323
- record_finished :pending
324
487
  execution_result.pending_message = pending_message
488
+ record_finished :pending, reporter
325
489
  reporter.example_pending self
326
490
  true
327
491
  else
328
- record_finished :passed
492
+ record_finished :passed, reporter
329
493
  reporter.example_passed self
330
494
  true
331
495
  end
332
496
  end
333
497
 
334
- def record_finished(status)
498
+ def record_finished(status, reporter)
335
499
  execution_result.record_finished(status, clock.now)
500
+ reporter.example_finished(self)
336
501
  end
337
502
 
338
503
  def run_before_example
339
504
  @example_group_instance.setup_mocks_for_rspec
340
- @example_group_class.hooks.run(:before, :example, self)
505
+ hooks.run(:before, :example, self)
506
+ end
507
+
508
+ def with_around_and_singleton_context_hooks
509
+ singleton_context_hooks_host = example_group_instance.singleton_class
510
+ singleton_context_hooks_host.run_before_context_hooks(example_group_instance)
511
+ with_around_example_hooks { yield }
512
+ ensure
513
+ singleton_context_hooks_host.run_after_context_hooks(example_group_instance)
341
514
  end
342
515
 
343
516
  def run_after_example
344
- @example_group_class.hooks.run(:after, :example, self)
517
+ assign_generated_description if defined?(::RSpec::Matchers)
518
+ hooks.run(:after, :example, self)
345
519
  verify_mocks
346
- assign_generated_description if RSpec.configuration.expecting_with_rspec?
347
- rescue Exception => e
348
- set_exception(e, "in an `after(:example)` hook")
349
520
  ensure
350
521
  @example_group_instance.teardown_mocks_for_rspec
351
522
  end
352
523
 
353
524
  def verify_mocks
354
525
  @example_group_instance.verify_mocks_for_rspec if mocks_need_verification?
355
- rescue Exception => e
356
- if pending?
357
- execution_result.pending_fixed = false
358
- @exception = nil
359
- else
360
- set_exception(e)
361
- end
526
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
527
+ set_exception(e)
362
528
  end
363
529
 
364
530
  def mocks_need_verification?
@@ -366,22 +532,23 @@ module RSpec
366
532
  end
367
533
 
368
534
  def assign_generated_description
369
- if metadata[:description].empty? && (description = RSpec::Matchers.generated_description)
535
+ if metadata[:description].empty? && (description = generate_description)
370
536
  metadata[:description] = description
371
- metadata[:full_description] << description
537
+ metadata[:full_description] += description
372
538
  end
373
- rescue Exception => e
374
- set_exception(e, "while assigning the example description")
375
539
  ensure
376
540
  RSpec::Matchers.clear_generated_description
377
541
  end
378
542
 
379
- def skip_message
380
- if String === skip
381
- skip
382
- else
383
- Pending::NO_REASON_GIVEN
384
- end
543
+ def generate_description
544
+ RSpec::Matchers.generated_description
545
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
546
+ location_description + " (Got an error when generating description " \
547
+ "from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})"
548
+ end
549
+
550
+ def location_description
551
+ "example at #{location}"
385
552
  end
386
553
 
387
554
  # Represents the result of executing an example.
@@ -418,18 +585,39 @@ module RSpec
418
585
  # this indicates whether or not it now passes.
419
586
  attr_accessor :pending_fixed
420
587
 
421
- alias pending_fixed? pending_fixed
588
+ def pending_fixed?
589
+ !!pending_fixed
590
+ end
591
+
592
+ # @return [Boolean] Indicates if the example was completely skipped
593
+ # (typically done via `:skip` metadata or the `skip` method). Skipped examples
594
+ # will have a `:pending` result. A `:pending` result can also come from examples
595
+ # that were marked as `:pending`, which causes them to be run, and produces a
596
+ # `:failed` result if the example passes.
597
+ def example_skipped?
598
+ status == :pending && !pending_exception
599
+ end
422
600
 
423
601
  # @api private
424
602
  # Records the finished status of the example.
425
603
  def record_finished(status, finished_at)
426
- self.status = status
427
- self.finished_at = finished_at
428
- self.run_time = (finished_at - started_at).to_f
604
+ self.status = status
605
+ calculate_run_time(finished_at)
606
+ end
607
+
608
+ # @api private
609
+ # Populates finished_at and run_time if it has not yet been set
610
+ def ensure_timing_set(clock)
611
+ calculate_run_time(clock.now) unless finished_at
429
612
  end
430
613
 
431
614
  private
432
615
 
616
+ def calculate_run_time(finished_at)
617
+ self.finished_at = finished_at
618
+ self.run_time = (finished_at - started_at).to_f
619
+ end
620
+
433
621
  # For backwards compatibility we present `status` as a string
434
622
  # when presenting the legacy hash interface.
435
623
  def hash_for_delegation
@@ -451,7 +639,7 @@ module RSpec
451
639
  end
452
640
  end
453
641
 
454
- def issue_deprecation(method_name, *args)
642
+ def issue_deprecation(_method_name, *_args)
455
643
  RSpec.deprecate("Treating `metadata[:execution_result]` as a hash",
456
644
  :replacement => "the attributes methods to access the data")
457
645
  end
@@ -461,14 +649,18 @@ module RSpec
461
649
  # @private
462
650
  # Provides an execution context for before/after :suite hooks.
463
651
  class SuiteHookContext < Example
464
- def initialize
465
- super(AnonymousExampleGroup, "", {})
652
+ def initialize(hook_description, reporter)
653
+ super(AnonymousExampleGroup, hook_description, {})
654
+ @example_group_instance = AnonymousExampleGroup.new
655
+ @reporter = reporter
466
656
  end
467
657
 
468
- # To ensure we don't silence errors...
469
- def set_exception(exception, context=nil)
470
- raise exception
658
+ # rubocop:disable Naming/AccessorMethodName
659
+ def set_exception(exception)
660
+ reporter.notify_non_example_exception(exception, "An error occurred in #{description}.")
661
+ RSpec.world.wants_to_quit = true
471
662
  end
663
+ # rubocop:enable Naming/AccessorMethodName
472
664
  end
473
665
  end
474
666
  end