rspec-core 3.1.7 → 3.2.0

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