rspec-core 3.1.7 → 3.2.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 (51) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.yardopts +1 -0
  5. data/Changelog.md +84 -0
  6. data/README.md +10 -1
  7. data/lib/rspec/core.rb +28 -8
  8. data/lib/rspec/core/backport_random.rb +12 -9
  9. data/lib/rspec/core/configuration.rb +350 -112
  10. data/lib/rspec/core/configuration_options.rb +14 -7
  11. data/lib/rspec/core/dsl.rb +7 -4
  12. data/lib/rspec/core/example.rb +86 -50
  13. data/lib/rspec/core/example_group.rb +247 -86
  14. data/lib/rspec/core/filter_manager.rb +38 -93
  15. data/lib/rspec/core/flat_map.rb +4 -4
  16. data/lib/rspec/core/formatters.rb +10 -6
  17. data/lib/rspec/core/formatters/base_formatter.rb +7 -4
  18. data/lib/rspec/core/formatters/base_text_formatter.rb +12 -12
  19. data/lib/rspec/core/formatters/console_codes.rb +8 -7
  20. data/lib/rspec/core/formatters/deprecation_formatter.rb +5 -3
  21. data/lib/rspec/core/formatters/documentation_formatter.rb +10 -4
  22. data/lib/rspec/core/formatters/helpers.rb +6 -4
  23. data/lib/rspec/core/formatters/html_formatter.rb +13 -8
  24. data/lib/rspec/core/formatters/html_printer.rb +26 -10
  25. data/lib/rspec/core/formatters/profile_formatter.rb +10 -7
  26. data/lib/rspec/core/formatters/protocol.rb +27 -18
  27. data/lib/rspec/core/formatters/snippet_extractor.rb +14 -7
  28. data/lib/rspec/core/hooks.rb +252 -211
  29. data/lib/rspec/core/memoized_helpers.rb +16 -16
  30. data/lib/rspec/core/metadata.rb +67 -28
  31. data/lib/rspec/core/metadata_filter.rb +151 -24
  32. data/lib/rspec/core/minitest_assertions_adapter.rb +5 -2
  33. data/lib/rspec/core/mocking_adapters/flexmock.rb +1 -1
  34. data/lib/rspec/core/mocking_adapters/mocha.rb +8 -8
  35. data/lib/rspec/core/notifications.rb +155 -94
  36. data/lib/rspec/core/option_parser.rb +16 -10
  37. data/lib/rspec/core/pending.rb +11 -9
  38. data/lib/rspec/core/project_initializer.rb +1 -1
  39. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +10 -8
  40. data/lib/rspec/core/rake_task.rb +37 -52
  41. data/lib/rspec/core/reporter.rb +30 -7
  42. data/lib/rspec/core/ruby_project.rb +12 -4
  43. data/lib/rspec/core/runner.rb +5 -8
  44. data/lib/rspec/core/sandbox.rb +37 -0
  45. data/lib/rspec/core/shared_example_group.rb +41 -15
  46. data/lib/rspec/core/test_unit_assertions_adapter.rb +3 -3
  47. data/lib/rspec/core/version.rb +1 -1
  48. data/lib/rspec/core/warnings.rb +2 -2
  49. data/lib/rspec/core/world.rb +12 -28
  50. metadata +44 -31
  51. metadata.gz.sig +0 -0
@@ -84,15 +84,22 @@ module RSpec
84
84
  # set before it.
85
85
  :default_path,
86
86
 
87
- # These must be set before `requires` to support checking `config.files_to_run`
88
- # from within `spec_helper.rb` when a `-rspec_helper` option is used.
87
+ # These must be set before `requires` to support checking
88
+ # `config.files_to_run` from within `spec_helper.rb` when a
89
+ # `-rspec_helper` option is used.
89
90
  :files_or_directories_to_run, :pattern, :exclude_pattern,
90
91
 
91
- # In general, we want to require the specified files as early as possible.
92
- # The `--require` option is specifically intended to allow early requires.
93
- # For later requires, they can just put the require in their spec files, but
94
- # `--require` provides a unique opportunity for users to instruct RSpec to
95
- # load an extension file early for maximum flexibility.
92
+ # Necessary so that the `--seed` option is applied before requires,
93
+ # in case required files do something with the provided seed.
94
+ # (such as seed global randomization with it).
95
+ :order,
96
+
97
+ # In general, we want to require the specified files as early as
98
+ # possible. The `--require` option is specifically intended to allow
99
+ # early requires. For later requires, they can just put the require in
100
+ # their spec files, but `--require` provides a unique opportunity for
101
+ # users to instruct RSpec to load an extension file early for maximum
102
+ # flexibility.
96
103
  :requires
97
104
  ]
98
105
 
@@ -8,7 +8,7 @@ module RSpec
8
8
  # By default the methods `describe`, `context` and `example_group`
9
9
  # are exposed. These methods define a named context for one or
10
10
  # more examples. The given block is evaluated in the context of
11
- # a generated subclass of {RSpec::Core::ExampleGroup}
11
+ # a generated subclass of {RSpec::Core::ExampleGroup}.
12
12
  #
13
13
  # ## Examples:
14
14
  #
@@ -35,6 +35,8 @@ module RSpec
35
35
 
36
36
  # @private
37
37
  def self.expose_example_group_alias(name)
38
+ return if example_group_aliases.include?(name)
39
+
38
40
  example_group_aliases << name
39
41
 
40
42
  (class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block|
@@ -49,7 +51,7 @@ module RSpec
49
51
  attr_accessor :top_level
50
52
  end
51
53
 
52
- # Adds the describe method to Module and the top level binding
54
+ # Adds the describe method to Module and the top level binding.
53
55
  # @api private
54
56
  def self.expose_globally!
55
57
  return if exposed_globally?
@@ -61,7 +63,7 @@ module RSpec
61
63
  @exposed_globally = true
62
64
  end
63
65
 
64
- # Removes the describe method from Module and the top level binding
66
+ # Removes the describe method from Module and the top level binding.
65
67
  # @api private
66
68
  def self.remove_globally!
67
69
  return unless exposed_globally?
@@ -76,6 +78,7 @@ module RSpec
76
78
  # @private
77
79
  def self.expose_example_group_alias_globally(method_name)
78
80
  change_global_dsl do
81
+ remove_method(method_name) if method_defined?(method_name)
79
82
  define_method(method_name) { |*a, &b| ::RSpec.__send__(method_name, *a, &b) }
80
83
  end
81
84
  end
@@ -89,5 +92,5 @@ module RSpec
89
92
  end
90
93
  end
91
94
 
92
- # capture main without an eval
95
+ # Capture main without an eval.
93
96
  ::RSpec::Core::DSL.top_level = self
@@ -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,22 +60,22 @@ 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
77
  description = if metadata[:description].to_s.empty?
77
- "example at #{location}"
78
+ location_description
78
79
  else
79
80
  metadata[:description]
80
81
  end
@@ -82,10 +83,28 @@ module RSpec
82
83
  RSpec.configuration.format_docstrings_block.call(description)
83
84
  end
84
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 argument that can be passed to the `rspec` command to rerun this example.
96
+ def rerun_argument
97
+ loaded_spec_files = RSpec.configuration.loaded_spec_files
98
+
99
+ Metadata.ascending(metadata) do |meta|
100
+ return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
101
+ end
102
+ end
103
+
85
104
  # @attr_reader
86
105
  #
87
106
  # Returns the first exception raised in the context of running this
88
- # example (nil if no exception is raised)
107
+ # example (nil if no exception is raised).
89
108
  attr_reader :exception
90
109
 
91
110
  # @attr_reader
@@ -105,10 +124,14 @@ module RSpec
105
124
  attr_accessor :clock
106
125
 
107
126
  # Creates a new instance of Example.
108
- # @param example_group_class [Class] the subclass of ExampleGroup in which this Example is declared
109
- # @param description [String] the String passed to the `it` method (or alias)
110
- # @param user_metadata [Hash] additional args passed to `it` to be used as metadata
111
- # @param example_block [Proc] the block of code that represents the example
127
+ # @param example_group_class [Class] the subclass of ExampleGroup in which
128
+ # this Example is declared
129
+ # @param description [String] the String passed to the `it` method (or
130
+ # alias)
131
+ # @param user_metadata [Hash] additional args passed to `it` to be used as
132
+ # metadata
133
+ # @param example_block [Proc] the block of code that represents the
134
+ # example
112
135
  # @api private
113
136
  def initialize(example_group_class, description, user_metadata, example_block=nil)
114
137
  @example_group_class = example_group_class
@@ -137,6 +160,8 @@ module RSpec
137
160
  # @param example_group_instance the instance of an ExampleGroup subclass
138
161
  def run(example_group_instance, reporter)
139
162
  @example_group_instance = example_group_instance
163
+ hooks.register_global_singleton_context_hooks(self, RSpec.configuration.hooks)
164
+ RSpec.configuration.configure_example(self)
140
165
  RSpec.current_example = self
141
166
 
142
167
  start(reporter)
@@ -146,7 +171,7 @@ module RSpec
146
171
  if skipped?
147
172
  Pending.mark_pending! self, skip
148
173
  elsif !RSpec.configuration.dry_run?
149
- with_around_example_hooks do
174
+ with_around_and_singleton_context_hooks do
150
175
  begin
151
176
  run_before_example
152
177
  @example_group_instance.instance_exec(self, &@example_block)
@@ -171,7 +196,7 @@ module RSpec
171
196
  rescue Exception => e
172
197
  set_exception(e)
173
198
  ensure
174
- @example_group_instance.instance_variables.each do |ivar|
199
+ ExampleGroup.each_instance_variable_for_example(@example_group_instance) do |ivar|
175
200
  @example_group_instance.instance_variable_set(ivar, nil)
176
201
  end
177
202
  @example_group_instance = nil
@@ -194,7 +219,7 @@ module RSpec
194
219
  # if ex.metadata[:key] == :some_value && some_global_condition
195
220
  # raise "some message"
196
221
  # end
197
- # ex.run # run delegates to ex.call
222
+ # ex.run # run delegates to ex.call.
198
223
  # end
199
224
  # end
200
225
  #
@@ -223,7 +248,8 @@ module RSpec
223
248
  end
224
249
  alias run call
225
250
 
226
- # Provides a wrapped proc that will update our `executed?` state when executed.
251
+ # Provides a wrapped proc that will update our `executed?` state when
252
+ # executed.
227
253
  def to_proc
228
254
  method(:call).to_proc
229
255
  end
@@ -250,21 +276,6 @@ module RSpec
250
276
  end
251
277
  end
252
278
 
253
- # @private
254
- def any_apply?(filters)
255
- MetadataFilter.any_apply?(filters, metadata)
256
- end
257
-
258
- # @private
259
- def all_apply?(filters)
260
- MetadataFilter.all_apply?(filters, metadata) || @example_group_class.all_apply?(filters)
261
- end
262
-
263
- # @private
264
- def around_example_hooks
265
- @around_example_hooks ||= example_group.hooks.around_example_hooks_for(self)
266
- end
267
-
268
279
  # @private
269
280
  #
270
281
  # Used internally to set an exception in an after hook, which
@@ -303,7 +314,7 @@ module RSpec
303
314
  # @private
304
315
  #
305
316
  # Used internally to skip without actually executing the example when
306
- # skip is used in before(:context)
317
+ # skip is used in before(:context).
307
318
  def skip_with_exception(reporter, exception)
308
319
  start(reporter)
309
320
  Pending.mark_skipped! self, exception.argument
@@ -324,12 +335,12 @@ module RSpec
324
335
 
325
336
  private
326
337
 
327
- def with_around_example_hooks(&block)
328
- if around_example_hooks.empty?
329
- yield
330
- else
331
- @example_group_class.hooks.run(:around, :example, self, Procsy.new(self, &block))
332
- end
338
+ def hooks
339
+ example_group_instance.singleton_class.hooks
340
+ end
341
+
342
+ def with_around_example_hooks
343
+ hooks.run(:around, :example, self) { yield }
333
344
  rescue Exception => e
334
345
  set_exception(e, "in an `around(:example)` hook")
335
346
  end
@@ -365,15 +376,21 @@ module RSpec
365
376
 
366
377
  def run_before_example
367
378
  @example_group_instance.setup_mocks_for_rspec
368
- @example_group_class.hooks.run(:before, :example, self)
379
+ hooks.run(:before, :example, self)
380
+ end
381
+
382
+ def with_around_and_singleton_context_hooks
383
+ singleton_context_hooks_host = example_group_instance.singleton_class
384
+ singleton_context_hooks_host.run_before_context_hooks(example_group_instance)
385
+ with_around_example_hooks { yield }
386
+ ensure
387
+ singleton_context_hooks_host.run_after_context_hooks(example_group_instance)
369
388
  end
370
389
 
371
390
  def run_after_example
372
- @example_group_class.hooks.run(:after, :example, self)
391
+ assign_generated_description if defined?(::RSpec::Matchers)
392
+ hooks.run(:after, :example, self)
373
393
  verify_mocks
374
- assign_generated_description if RSpec.configuration.expecting_with_rspec?
375
- rescue Exception => e
376
- set_exception(e, "in an `after(:example)` hook")
377
394
  ensure
378
395
  @example_group_instance.teardown_mocks_for_rspec
379
396
  end
@@ -383,6 +400,7 @@ module RSpec
383
400
  rescue Exception => e
384
401
  if pending?
385
402
  execution_result.pending_fixed = false
403
+ execution_result.pending_exception = e
386
404
  @exception = nil
387
405
  else
388
406
  set_exception(e)
@@ -394,16 +412,25 @@ module RSpec
394
412
  end
395
413
 
396
414
  def assign_generated_description
397
- if metadata[:description].empty? && (description = RSpec::Matchers.generated_description)
415
+ if metadata[:description].empty? && (description = generate_description)
398
416
  metadata[:description] = description
399
417
  metadata[:full_description] << description
400
418
  end
401
- rescue Exception => e
402
- set_exception(e, "while assigning the example description")
403
419
  ensure
404
420
  RSpec::Matchers.clear_generated_description
405
421
  end
406
422
 
423
+ def generate_description
424
+ RSpec::Matchers.generated_description
425
+ rescue Exception => e
426
+ location_description + " (Got an error when generating description " \
427
+ "from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})"
428
+ end
429
+
430
+ def location_description
431
+ "example at #{location}"
432
+ end
433
+
407
434
  def skip_message
408
435
  if String === skip
409
436
  skip
@@ -448,6 +475,15 @@ module RSpec
448
475
 
449
476
  alias pending_fixed? pending_fixed
450
477
 
478
+ # @return [Boolean] Indicates if the example was completely skipped
479
+ # (typically done via `:skip` metadata or the `skip` method). Skipped examples
480
+ # will have a `:pending` result. A `:pending` result can also come from examples
481
+ # that were marked as `:pending`, which causes them to be run, and produces a
482
+ # `:failed` result if the example passes.
483
+ def example_skipped?
484
+ status == :pending && !pending_exception
485
+ end
486
+
451
487
  # @api private
452
488
  # Records the finished status of the example.
453
489
  def record_finished(status, finished_at)
@@ -493,7 +529,7 @@ module RSpec
493
529
  super(AnonymousExampleGroup, "", {})
494
530
  end
495
531
 
496
- # To ensure we don't silence errors...
532
+ # To ensure we don't silence errors.
497
533
  def set_exception(exception, _context=nil)
498
534
  raise exception
499
535
  end
@@ -3,7 +3,7 @@ RSpec::Support.require_rspec_support 'recursive_const_methods'
3
3
  module RSpec
4
4
  module Core
5
5
  # ExampleGroup and {Example} are the main structural elements of
6
- # rspec-core. Consider this example:
6
+ # rspec-core. Consider this example:
7
7
  #
8
8
  # describe Thing do
9
9
  # it "does something" do
@@ -17,13 +17,13 @@ module RSpec
17
17
  #
18
18
  # Example group bodies (e.g. `describe` or `context` blocks) are evaluated
19
19
  # in the context of a new subclass of ExampleGroup. Individual examples are
20
- # evaluated in the context of an instance of the specific ExampleGroup subclass
21
- # to which they belong.
20
+ # evaluated in the context of an instance of the specific ExampleGroup
21
+ # subclass to which they belong.
22
22
  #
23
23
  # Besides the class methods defined here, there are other interesting macros
24
- # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and {SharedExampleGroup}.
25
- # There are additional instance methods available to your examples defined in
26
- # {MemoizedHelpers} and {Pending}.
24
+ # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and
25
+ # {SharedExampleGroup}. There are additional instance methods available to
26
+ # your examples defined in {MemoizedHelpers} and {Pending}.
27
27
  class ExampleGroup
28
28
  extend Hooks
29
29
 
@@ -32,10 +32,11 @@ module RSpec
32
32
  include Pending
33
33
  extend SharedExampleGroup
34
34
 
35
- unless respond_to?(:define_singleton_method)
36
- # @private
37
- def self.define_singleton_method(*a, &b)
38
- (class << self; self; end).__send__(:define_method, *a, &b)
35
+ # @private
36
+ def self.idempotently_define_singleton_method(name, &definition)
37
+ (class << self; self; end).module_exec do
38
+ remove_method(name) if method_defined?(name)
39
+ define_method(name, &definition)
39
40
  end
40
41
  end
41
42
 
@@ -44,7 +45,21 @@ module RSpec
44
45
  # The [Metadata](Metadata) object associated with this group.
45
46
  # @see Metadata
46
47
  def self.metadata
47
- @metadata if defined?(@metadata)
48
+ @metadata ||= nil
49
+ end
50
+
51
+ # Temporarily replace the provided metadata.
52
+ # Intended primarily to allow an example group's singleton class
53
+ # to return the metadata of the example that it exists for. This
54
+ # is necessary for shared example group inclusion to work properly
55
+ # with singleton example groups.
56
+ # @private
57
+ def self.with_replaced_metadata(meta)
58
+ orig_metadata = metadata
59
+ @metadata = meta
60
+ yield
61
+ ensure
62
+ @metadata = orig_metadata
48
63
  end
49
64
 
50
65
  # @private
@@ -56,7 +71,7 @@ module RSpec
56
71
  # @private
57
72
  def self.delegate_to_metadata(*names)
58
73
  names.each do |name|
59
- define_singleton_method(name) { metadata.fetch(name) }
74
+ idempotently_define_singleton_method(name) { metadata.fetch(name) }
60
75
  end
61
76
  end
62
77
 
@@ -77,7 +92,6 @@ module RSpec
77
92
  # end
78
93
  # end
79
94
  #
80
- #
81
95
  def described_class
82
96
  self.class.described_class
83
97
  end
@@ -89,9 +103,20 @@ module RSpec
89
103
  # @private
90
104
  # @macro [attach] define_example_method
91
105
  # @!scope class
92
- # @param name [String]
93
- # @param extra_options [Hash]
94
- # @param implementation [Block]
106
+ # @overload $1
107
+ # @overload $1(&example_implementation)
108
+ # @param example_implementation [Block] The implementation of the example.
109
+ # @overload $1(doc_string, *metadata_keys, metadata={})
110
+ # @param doc_string [String] The example's doc string.
111
+ # @param metadata [Hash] Metadata for the example.
112
+ # @param metadata_keys [Array<Symbol>] Metadata tags for the example.
113
+ # Will be transformed into hash entries with `true` values.
114
+ # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
115
+ # @param doc_string [String] The example's doc string.
116
+ # @param metadata [Hash] Metadata for the example.
117
+ # @param metadata_keys [Array<Symbol>] Metadata tags for the example.
118
+ # Will be transformed into hash entries with `true` values.
119
+ # @param example_implementation [Block] The implementation of the example.
95
120
  # @yield [Example] the example object
96
121
  # @example
97
122
  # $1 do
@@ -100,6 +125,9 @@ module RSpec
100
125
  # $1 "does something" do
101
126
  # end
102
127
  #
128
+ # $1 "does something", :slow, :uses_js do
129
+ # end
130
+ #
103
131
  # $1 "does something", :with => 'additional metadata' do
104
132
  # end
105
133
  #
@@ -107,7 +135,7 @@ module RSpec
107
135
  # # ex is the Example object that contains metadata about the example
108
136
  # end
109
137
  def self.define_example_method(name, extra_options={})
110
- define_singleton_method(name) do |*all_args, &block|
138
+ idempotently_define_singleton_method(name) do |*all_args, &block|
111
139
  desc, *args = *all_args
112
140
 
113
141
  options = Metadata.build_hash_from(args)
@@ -134,25 +162,25 @@ module RSpec
134
162
  # end
135
163
  define_example_method :specify
136
164
 
137
- # Shortcut to define an example with `:focus => true`
165
+ # Shortcut to define an example with `:focus => true`.
138
166
  # @see example
139
167
  define_example_method :focus, :focus => true
140
- # Shortcut to define an example with `:focus => true`
168
+ # Shortcut to define an example with `:focus => true`.
141
169
  # @see example
142
170
  define_example_method :fexample, :focus => true
143
- # Shortcut to define an example with `:focus => true`
171
+ # Shortcut to define an example with `:focus => true`.
144
172
  # @see example
145
173
  define_example_method :fit, :focus => true
146
- # Shortcut to define an example with `:focus => true`
174
+ # Shortcut to define an example with `:focus => true`.
147
175
  # @see example
148
176
  define_example_method :fspecify, :focus => true
149
- # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`
177
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`.
150
178
  # @see example
151
179
  define_example_method :xexample, :skip => 'Temporarily skipped with xexample'
152
- # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`
180
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`.
153
181
  # @see example
154
182
  define_example_method :xit, :skip => 'Temporarily skipped with xit'
155
- # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`
183
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`.
156
184
  # @see example
157
185
  define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify'
158
186
  # Shortcut to define an example with `:skip => true`
@@ -167,11 +195,17 @@ module RSpec
167
195
  # @!group Defining Example Groups
168
196
 
169
197
  # @private
170
- # @macro [attach] alias_example_group_to
198
+ # @macro [attach] define_example_group_method
171
199
  # @!scope class
172
- # @param name [String] The example group doc string
173
- # @param metadata [Hash] Additional metadata to attach to the example group
174
- # @yield The example group definition
200
+ # @overload $1
201
+ # @overload $1(&example_group_definition)
202
+ # @param example_group_definition [Block] The definition of the example group.
203
+ # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
204
+ # @param doc_string [String] The group's doc string.
205
+ # @param metadata [Hash] Metadata for the group.
206
+ # @param metadata_keys [Array<Symbol>] Metadata tags for the group.
207
+ # Will be transformed into hash entries with `true` values.
208
+ # @param example_group_definition [Block] The definition of the example group.
175
209
  #
176
210
  # Generates a subclass of this example group which inherits
177
211
  # everything except the examples themselves.
@@ -195,7 +229,7 @@ module RSpec
195
229
  #
196
230
  # @see DSL#describe
197
231
  def self.define_example_group_method(name, metadata={})
198
- define_singleton_method(name) do |*args, &example_group_block|
232
+ idempotently_define_singleton_method(name) do |*args, &example_group_block|
199
233
  thread_data = RSpec.thread_local_metadata
200
234
  top_level = self == ExampleGroup
201
235
 
@@ -230,8 +264,8 @@ module RSpec
230
264
 
231
265
  define_example_group_method :example_group
232
266
 
233
- # An alias of `example_group`. Generally used when grouping
234
- # examples by a thing you are describing (e.g. an object, class or method).
267
+ # An alias of `example_group`. Generally used when grouping examples by a
268
+ # thing you are describing (e.g. an object, class or method).
235
269
  # @see example_group
236
270
  define_example_group_method :describe
237
271
 
@@ -266,12 +300,12 @@ module RSpec
266
300
  #
267
301
  # @see SharedExampleGroup
268
302
  def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
269
- define_singleton_method(new_name) do |name, *args, &customization_block|
270
- # Pass :caller so the :location metadata is set properly...
271
- # otherwise, it'll be set to the next line because that's
303
+ idempotently_define_singleton_method(new_name) do |name, *args, &customization_block|
304
+ # Pass :caller so the :location metadata is set properly.
305
+ # Otherwise, it'll be set to the next line because that's
272
306
  # the block's source_location.
273
- group = example_group("#{report_label} #{name}", :caller => caller) do
274
- find_and_eval_shared("examples", name, *args, &customization_block)
307
+ group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do
308
+ find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block)
275
309
  end
276
310
  group.metadata[:shared_group_name] = name
277
311
  group
@@ -287,32 +321,36 @@ module RSpec
287
321
 
288
322
  # Includes shared content mapped to `name` directly in the group in which
289
323
  # it is declared, as opposed to `it_behaves_like`, which creates a nested
290
- # group. If given a block, that block is also eval'd in the current context.
324
+ # group. If given a block, that block is also eval'd in the current
325
+ # context.
291
326
  #
292
327
  # @see SharedExampleGroup
293
328
  def self.include_context(name, *args, &block)
294
- find_and_eval_shared("context", name, *args, &block)
329
+ find_and_eval_shared("context", name, caller.first, *args, &block)
295
330
  end
296
331
 
297
332
  # Includes shared content mapped to `name` directly in the group in which
298
333
  # it is declared, as opposed to `it_behaves_like`, which creates a nested
299
- # group. If given a block, that block is also eval'd in the current context.
334
+ # group. If given a block, that block is also eval'd in the current
335
+ # context.
300
336
  #
301
337
  # @see SharedExampleGroup
302
338
  def self.include_examples(name, *args, &block)
303
- find_and_eval_shared("examples", name, *args, &block)
339
+ find_and_eval_shared("examples", name, caller.first, *args, &block)
304
340
  end
305
341
 
306
342
  # @private
307
- def self.find_and_eval_shared(label, name, *args, &customization_block)
343
+ def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block)
308
344
  shared_block = RSpec.world.shared_example_group_registry.find(parent_groups, name)
309
345
 
310
346
  unless shared_block
311
347
  raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
312
348
  end
313
349
 
314
- module_exec(*args, &shared_block)
315
- module_exec(&customization_block) if customization_block
350
+ SharedExampleGroupInclusionStackFrame.with_frame(name, Metadata.relative_path(inclusion_location)) do
351
+ module_exec(*args, &shared_block)
352
+ module_exec(&customization_block) if customization_block
353
+ end
316
354
  end
317
355
 
318
356
  # @!endgroup
@@ -338,10 +376,11 @@ module RSpec
338
376
  # Ruby 1.9 has a bug that can lead to infinite recursion and a
339
377
  # SystemStackError if you include a module in a superclass after
340
378
  # including it in a subclass: https://gist.github.com/845896
341
- # To prevent this, we must include any modules in RSpec::Core::ExampleGroup
342
- # before users create example groups and have a chance to include
343
- # the same module in a subclass of RSpec::Core::ExampleGroup.
344
- # So we need to configure example groups here.
379
+ # To prevent this, we must include any modules in
380
+ # RSpec::Core::ExampleGroup before users create example groups and have
381
+ # a chance to include the same module in a subclass of
382
+ # RSpec::Core::ExampleGroup. So we need to configure example groups
383
+ # here.
345
384
  ensure_example_groups_are_configured
346
385
 
347
386
  description = args.shift
@@ -353,7 +392,7 @@ module RSpec
353
392
  )
354
393
 
355
394
  hooks.register_globals(self, RSpec.configuration.hooks)
356
- RSpec.world.configure_group(self)
395
+ RSpec.configuration.configure_group(self)
357
396
  end
358
397
 
359
398
  # @private
@@ -368,7 +407,8 @@ module RSpec
368
407
 
369
408
  # @private
370
409
  def self.descendant_filtered_examples
371
- @descendant_filtered_examples ||= filtered_examples + children.inject([]) { |a, e| a + e.descendant_filtered_examples }
410
+ @descendant_filtered_examples ||= filtered_examples +
411
+ FlatMap.flat_map(children, &:descendant_filtered_examples)
372
412
  end
373
413
 
374
414
  # @private
@@ -378,7 +418,7 @@ module RSpec
378
418
 
379
419
  # @private
380
420
  def self.descendants
381
- @_descendants ||= [self] + children.inject([]) { |a, e| a + e.descendants }
421
+ @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants)
382
422
  end
383
423
 
384
424
  ## @private
@@ -409,47 +449,66 @@ module RSpec
409
449
 
410
450
  # @private
411
451
  def self.store_before_context_ivars(example_group_instance)
412
- return if example_group_instance.instance_variables.empty?
413
-
414
- example_group_instance.instance_variables.each do |ivar|
452
+ each_instance_variable_for_example(example_group_instance) do |ivar|
415
453
  before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
416
454
  end
417
455
  end
418
456
 
419
457
  # @private
420
458
  def self.run_before_context_hooks(example_group_instance)
421
- return if descendant_filtered_examples.empty?
422
- begin
423
- set_ivars(example_group_instance, superclass.before_context_ivars)
459
+ set_ivars(example_group_instance, superclass_before_context_ivars)
460
+
461
+ ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do
462
+ hooks.run(:before, :context, example_group_instance)
463
+ end
464
+ ensure
465
+ store_before_context_ivars(example_group_instance)
466
+ end
424
467
 
425
- ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do
426
- hooks.run(:before, :context, example_group_instance)
468
+ if RUBY_VERSION.to_f >= 1.9
469
+ # @private
470
+ def self.superclass_before_context_ivars
471
+ superclass.before_context_ivars
472
+ end
473
+ else # 1.8.7
474
+ # @private
475
+ def self.superclass_before_context_ivars
476
+ if superclass.respond_to?(:before_context_ivars)
477
+ superclass.before_context_ivars
478
+ else
479
+ # `self` must be the singleton class of an ExampleGroup instance.
480
+ # On 1.8.7, the superclass of a singleton class of an instance of A
481
+ # is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor
482
+ # is A, so we can mirror 1.8.7's behavior here. Note that we have to
483
+ # search for the first that responds to `before_context_ivars`
484
+ # in case a module has been included in the singleton class.
485
+ ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars
427
486
  end
428
- ensure
429
- store_before_context_ivars(example_group_instance)
430
487
  end
431
488
  end
432
489
 
433
490
  # @private
434
491
  def self.run_after_context_hooks(example_group_instance)
435
- return if descendant_filtered_examples.empty?
436
492
  set_ivars(example_group_instance, before_context_ivars)
437
493
 
438
494
  ContextHookMemoizedHash::After.isolate_for_context_hook(example_group_instance) do
439
495
  hooks.run(:after, :context, example_group_instance)
440
496
  end
497
+ ensure
498
+ before_context_ivars.clear
441
499
  end
442
500
 
443
- # Runs all the examples in this group
444
- def self.run(reporter)
501
+ # Runs all the examples in this group.
502
+ def self.run(reporter=RSpec::Core::NullReporter.new)
445
503
  if RSpec.world.wants_to_quit
446
504
  RSpec.world.clear_remaining_example_groups if top_level?
447
505
  return
448
506
  end
449
507
  reporter.example_group_started(self)
450
508
 
509
+ should_run_context_hooks = descendant_filtered_examples.any?
451
510
  begin
452
- run_before_context_hooks(new)
511
+ run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks
453
512
  result_for_this_group = run_examples(reporter)
454
513
  results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
455
514
  result_for_this_group && results_for_descendants
@@ -459,8 +518,7 @@ module RSpec
459
518
  RSpec.world.wants_to_quit = true if fail_fast?
460
519
  for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) }
461
520
  ensure
462
- run_after_context_hooks(new)
463
- before_context_ivars.clear
521
+ run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
464
522
  reporter.example_group_finished(self)
465
523
  end
466
524
  end
@@ -485,7 +543,7 @@ module RSpec
485
543
  def self.run_examples(reporter)
486
544
  ordering_strategy.order(filtered_examples).map do |example|
487
545
  next if RSpec.world.wants_to_quit
488
- instance = new
546
+ instance = new(example.inspect_output)
489
547
  set_ivars(instance, before_context_ivars)
490
548
  succeeded = example.run(instance, reporter)
491
549
  RSpec.world.wants_to_quit = true if fail_fast? && !succeeded
@@ -510,21 +568,11 @@ module RSpec
510
568
  RSpec.configuration.fail_fast?
511
569
  end
512
570
 
513
- # @private
514
- def self.any_apply?(filters)
515
- MetadataFilter.any_apply?(filters, metadata)
516
- end
517
-
518
- # @private
519
- def self.all_apply?(filters)
520
- MetadataFilter.all_apply?(filters, metadata)
521
- end
522
-
523
571
  # @private
524
572
  def self.declaration_line_numbers
525
573
  @declaration_line_numbers ||= [metadata[:line_number]] +
526
574
  examples.map { |e| e.metadata[:line_number] } +
527
- children.inject([]) { |a, e| a + e.declaration_line_numbers }
575
+ FlatMap.flat_map(children, &:declaration_line_numbers)
528
576
  end
529
577
 
530
578
  # @private
@@ -536,6 +584,70 @@ module RSpec
536
584
  def self.set_ivars(instance, ivars)
537
585
  ivars.each { |name, value| instance.instance_variable_set(name, value) }
538
586
  end
587
+
588
+ if RUBY_VERSION.to_f < 1.9
589
+ # @private
590
+ INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze
591
+ else
592
+ # @private
593
+ INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output
594
+ end
595
+
596
+ # @private
597
+ def self.each_instance_variable_for_example(group)
598
+ group.instance_variables.each do |ivar|
599
+ yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE
600
+ end
601
+ end
602
+
603
+ def initialize(inspect_output=nil)
604
+ @__inspect_output = inspect_output || '(no description provided)'
605
+ end
606
+
607
+ # @private
608
+ def inspect
609
+ "#<#{self.class} #{@__inspect_output}>"
610
+ end
611
+
612
+ unless method_defined?(:singleton_class) # for 1.8.7
613
+ # @private
614
+ def singleton_class
615
+ class << self; self; end
616
+ end
617
+ end
618
+
619
+ # Raised when an RSpec API is called in the wrong scope, such as `before`
620
+ # being called from within an example rather than from within an example
621
+ # group block.
622
+ WrongScopeError = Class.new(NoMethodError)
623
+
624
+ def self.method_missing(name, *args)
625
+ if method_defined?(name)
626
+ raise WrongScopeError,
627
+ "`#{name}` is not available on an example group (e.g. a " \
628
+ "`describe` or `context` block). It is only available from " \
629
+ "within individual examples (e.g. `it` blocks) or from " \
630
+ "constructs that run in the scope of an example (e.g. " \
631
+ "`before`, `let`, etc)."
632
+ end
633
+
634
+ super
635
+ end
636
+ private_class_method :method_missing
637
+
638
+ private
639
+
640
+ def method_missing(name, *args)
641
+ if self.class.respond_to?(name)
642
+ raise WrongScopeError,
643
+ "`#{name}` is not available from within an example (e.g. an " \
644
+ "`it` block) or from constructs that run in the scope of an " \
645
+ "example (e.g. `before`, `let`, etc). It is only available " \
646
+ "on an example group (e.g. a `describe` or `context` block)."
647
+ end
648
+
649
+ super
650
+ end
539
651
  end
540
652
 
541
653
  # @private
@@ -545,11 +657,55 @@ module RSpec
545
657
  {}
546
658
  end
547
659
  end
660
+
661
+ # Contains information about the inclusion site of a shared example group.
662
+ class SharedExampleGroupInclusionStackFrame
663
+ # @return [String] the name of the shared example group
664
+ attr_reader :shared_group_name
665
+ # @return [String] the location where the shared example was included
666
+ attr_reader :inclusion_location
667
+
668
+ def initialize(shared_group_name, inclusion_location)
669
+ @shared_group_name = shared_group_name
670
+ @inclusion_location = inclusion_location
671
+ end
672
+
673
+ # @return [String] The {#inclusion_location}, formatted for display by a formatter.
674
+ def formatted_inclusion_location
675
+ @formatted_inclusion_location ||= begin
676
+ RSpec.configuration.backtrace_formatter.backtrace_line(
677
+ inclusion_location.sub(/(:\d+):in .+$/, '\1')
678
+ )
679
+ end
680
+ end
681
+
682
+ # @return [String] Description of this stack frame, in the form used by
683
+ # RSpec's built-in formatters.
684
+ def description
685
+ @description ||= "Shared Example Group: #{shared_group_name.inspect} " \
686
+ "called from #{formatted_inclusion_location}"
687
+ end
688
+
689
+ # @private
690
+ def self.current_backtrace
691
+ RSpec.thread_local_metadata[:shared_example_group_inclusions].reverse
692
+ end
693
+
694
+ # @private
695
+ def self.with_frame(name, location)
696
+ current_stack = RSpec.thread_local_metadata[:shared_example_group_inclusions]
697
+ current_stack << new(name, location)
698
+ yield
699
+ ensure
700
+ current_stack.pop
701
+ end
702
+ end
548
703
  end
549
704
 
550
705
  # @private
551
706
  #
552
- # Namespace for the example group subclasses generated by top-level `describe`.
707
+ # Namespace for the example group subclasses generated by top-level
708
+ # `describe`.
553
709
  module ExampleGroups
554
710
  extend Support::RecursiveConstMethods
555
711
 
@@ -570,16 +726,20 @@ module RSpec
570
726
  def self.base_name_for(group)
571
727
  return "Anonymous" if group.description.empty?
572
728
 
573
- # convert to CamelCase
574
- name = ' ' + group.description
575
- name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) { Regexp.last_match[1].upcase }
729
+ # Convert to CamelCase.
730
+ name = ' ' << group.description
731
+ name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
732
+ match = ::Regexp.last_match[1]
733
+ match.upcase!
734
+ match
735
+ end
576
736
 
577
- name.lstrip! # Remove leading whitespace
578
- name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
737
+ name.lstrip! # Remove leading whitespace
738
+ name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names
579
739
 
580
740
  # Ruby requires first const letter to be A-Z. Use `Nested`
581
741
  # as necessary to enforce that.
582
- name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
742
+ name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze)
583
743
 
584
744
  name
585
745
  end
@@ -597,7 +757,8 @@ module RSpec
597
757
  def self.disambiguate(name, const_scope)
598
758
  return name unless const_defined_on?(const_scope, name)
599
759
 
600
- # Add a trailing number if needed to disambiguate from an existing constant.
760
+ # Add a trailing number if needed to disambiguate from an existing
761
+ # constant.
601
762
  name << "_2"
602
763
  name.next! while const_defined_on?(const_scope, name)
603
764
  name