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
@@ -1,9 +1,13 @@
1
+ RSpec::Support.require_rspec_support 'recursive_const_methods'
2
+
1
3
  module RSpec
2
4
  module Core
5
+ # rubocop:disable Metrics/ClassLength
6
+
3
7
  # ExampleGroup and {Example} are the main structural elements of
4
- # rspec-core. Consider this example:
8
+ # rspec-core. Consider this example:
5
9
  #
6
- # describe Thing do
10
+ # RSpec.describe Thing do
7
11
  # it "does something" do
8
12
  # end
9
13
  # end
@@ -15,25 +19,28 @@ module RSpec
15
19
  #
16
20
  # Example group bodies (e.g. `describe` or `context` blocks) are evaluated
17
21
  # in the context of a new subclass of ExampleGroup. Individual examples are
18
- # evalutaed in the context of an instance of the specific ExampleGroup subclass
19
- # to which they belong.
22
+ # evaluated in the context of an instance of the specific ExampleGroup
23
+ # subclass to which they belong.
20
24
  #
21
25
  # Besides the class methods defined here, there are other interesting macros
22
- # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and {SharedExampleGroup}.
23
- # There are additional instance methods available to your examples defined in
24
- # {MemoizedHelpers} and {Pending}.
26
+ # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and
27
+ # {SharedExampleGroup}. There are additional instance methods available to
28
+ # your examples defined in {MemoizedHelpers} and {Pending}.
25
29
  class ExampleGroup
26
- extend Hooks
30
+ extend Hooks
27
31
 
28
32
  include MemoizedHelpers
29
- extend MemoizedHelpers::ClassMethods
33
+ extend MemoizedHelpers::ClassMethods
30
34
  include Pending
31
- extend SharedExampleGroup
35
+ extend SharedExampleGroup
32
36
 
33
- unless respond_to?(:define_singleton_method)
34
- # @private
35
- def self.define_singleton_method(*a, &b)
36
- (class << self; self; end).__send__(:define_method, *a, &b)
37
+ # Define a singleton method for the singleton class (remove the method if
38
+ # it's already been defined).
39
+ # @private
40
+ def self.idempotently_define_singleton_method(name, &definition)
41
+ (class << self; self; end).module_exec do
42
+ remove_method(name) if method_defined?(name) && instance_method(name).owner == self
43
+ define_method(name, &definition)
37
44
  end
38
45
  end
39
46
 
@@ -42,19 +49,33 @@ module RSpec
42
49
  # The [Metadata](Metadata) object associated with this group.
43
50
  # @see Metadata
44
51
  def self.metadata
45
- @metadata if defined?(@metadata)
52
+ @metadata ||= nil
53
+ end
54
+
55
+ # Temporarily replace the provided metadata.
56
+ # Intended primarily to allow an example group's singleton class
57
+ # to return the metadata of the example that it exists for. This
58
+ # is necessary for shared example group inclusion to work properly
59
+ # with singleton example groups.
60
+ # @private
61
+ def self.with_replaced_metadata(meta)
62
+ orig_metadata = metadata
63
+ @metadata = meta
64
+ yield
65
+ ensure
66
+ @metadata = orig_metadata
46
67
  end
47
68
 
48
69
  # @private
49
70
  # @return [Metadata] belonging to the parent of a nested {ExampleGroup}
50
71
  def self.superclass_metadata
51
- @superclass_metadata ||= self.superclass.respond_to?(:metadata) ? self.superclass.metadata : nil
72
+ @superclass_metadata ||= superclass.respond_to?(:metadata) ? superclass.metadata : nil
52
73
  end
53
74
 
54
75
  # @private
55
76
  def self.delegate_to_metadata(*names)
56
77
  names.each do |name|
57
- define_singleton_method(name) { metadata.fetch(name) }
78
+ idempotently_define_singleton_method(name) { metadata.fetch(name) }
58
79
  end
59
80
  end
60
81
 
@@ -69,13 +90,12 @@ module RSpec
69
90
  # Returns the class or module passed to the `describe` method (or alias).
70
91
  # Returns nil if the subject is not a class or module.
71
92
  # @example
72
- # describe Thing do
93
+ # RSpec.describe Thing do
73
94
  # it "does something" do
74
95
  # described_class == Thing
75
96
  # end
76
97
  # end
77
98
  #
78
- #
79
99
  def described_class
80
100
  self.class.described_class
81
101
  end
@@ -87,9 +107,19 @@ module RSpec
87
107
  # @private
88
108
  # @macro [attach] define_example_method
89
109
  # @!scope class
90
- # @param name [String]
91
- # @param extra_options [Hash]
92
- # @param implementation [Block]
110
+ # @method $1
111
+ # @overload $1
112
+ # @overload $1(&example_implementation)
113
+ # @param example_implementation [Block] The implementation of the example.
114
+ # @overload $1(doc_string, *metadata)
115
+ # @param doc_string [String] The example's doc string.
116
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
117
+ # Symbols will be transformed into hash entries with `true` values.
118
+ # @overload $1(doc_string, *metadata, &example_implementation)
119
+ # @param doc_string [String] The example's doc string.
120
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
121
+ # Symbols will be transformed into hash entries with `true` values.
122
+ # @param example_implementation [Block] The implementation of the example.
93
123
  # @yield [Example] the example object
94
124
  # @example
95
125
  # $1 do
@@ -98,32 +128,29 @@ module RSpec
98
128
  # $1 "does something" do
99
129
  # end
100
130
  #
131
+ # $1 "does something", :slow, :uses_js do
132
+ # end
133
+ #
101
134
  # $1 "does something", :with => 'additional metadata' do
102
135
  # end
103
136
  #
104
137
  # $1 "does something" do |ex|
105
138
  # # ex is the Example object that contains metadata about the example
106
139
  # end
140
+ #
141
+ # @example
142
+ # $1 "does something", :slow, :load_factor => 100 do
143
+ # end
144
+ #
107
145
  def self.define_example_method(name, extra_options={})
108
- define_singleton_method(name) do |*all_args, &block|
146
+ idempotently_define_singleton_method(name) do |*all_args, &block|
109
147
  desc, *args = *all_args
148
+
110
149
  options = Metadata.build_hash_from(args)
111
150
  options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
112
151
  options.update(extra_options)
113
152
 
114
- # Metadata inheritance normally happens in `Example#initialize`,
115
- # but for `:pending` specifically we need it earlier.
116
- pending_metadata = options[:pending] || metadata[:pending]
117
-
118
- if pending_metadata
119
- options, block = ExampleGroup.pending_metadata_and_block_for(
120
- options.merge(:pending => pending_metadata),
121
- block
122
- )
123
- end
124
-
125
- examples << RSpec::Core::Example.new(self, desc, options, block)
126
- examples.last
153
+ RSpec::Core::Example.new(self, desc, options, block)
127
154
  end
128
155
  end
129
156
 
@@ -142,25 +169,25 @@ module RSpec
142
169
  # end
143
170
  define_example_method :specify
144
171
 
145
- # Shortcut to define an example with `:focus => true`
172
+ # Shortcut to define an example with `:focus => true`.
146
173
  # @see example
147
174
  define_example_method :focus, :focus => true
148
- # Shortcut to define an example with `:focus => true`
175
+ # Shortcut to define an example with `:focus => true`.
149
176
  # @see example
150
177
  define_example_method :fexample, :focus => true
151
- # Shortcut to define an example with `:focus => true`
178
+ # Shortcut to define an example with `:focus => true`.
152
179
  # @see example
153
180
  define_example_method :fit, :focus => true
154
- # Shortcut to define an example with `:focus => true`
181
+ # Shortcut to define an example with `:focus => true`.
155
182
  # @see example
156
183
  define_example_method :fspecify, :focus => true
157
- # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`
184
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`.
158
185
  # @see example
159
186
  define_example_method :xexample, :skip => 'Temporarily skipped with xexample'
160
- # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`
187
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`.
161
188
  # @see example
162
189
  define_example_method :xit, :skip => 'Temporarily skipped with xit'
163
- # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`
190
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`.
164
191
  # @see example
165
192
  define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify'
166
193
  # Shortcut to define an example with `:skip => true`
@@ -175,11 +202,16 @@ module RSpec
175
202
  # @!group Defining Example Groups
176
203
 
177
204
  # @private
178
- # @macro [attach] alias_example_group_to
205
+ # @macro [attach] define_example_group_method
179
206
  # @!scope class
180
- # @param name [String] The example group doc string
181
- # @param metadata [Hash] Additional metadata to attach to the example group
182
- # @yield The example group definition
207
+ # @overload $1
208
+ # @overload $1(&example_group_definition)
209
+ # @param example_group_definition [Block] The definition of the example group.
210
+ # @overload $1(doc_string, *metadata, &example_implementation)
211
+ # @param doc_string [String] The group's doc string.
212
+ # @param metadata [Array<Symbol>, Hash] Metadata for the group.
213
+ # Symbols will be transformed into hash entries with `true` values.
214
+ # @param example_group_definition [Block] The definition of the example group.
183
215
  #
184
216
  # Generates a subclass of this example group which inherits
185
217
  # everything except the examples themselves.
@@ -193,41 +225,50 @@ module RSpec
193
225
  # do_something_before
194
226
  # end
195
227
  #
228
+ # before(:example, :clean_env) do
229
+ # env.clear!
230
+ # end
231
+ #
196
232
  # let(:thing) { Thing.new }
197
233
  #
198
234
  # $1 "attribute (of something)" do
199
235
  # # examples in the group get the before hook
200
236
  # # declared above, and can access `thing`
201
237
  # end
238
+ #
239
+ # $1 "needs additional setup", :clean_env, :implementation => JSON do
240
+ # # specifies that hooks with matching metadata
241
+ # # should be be run additionally
242
+ # end
202
243
  # end
203
244
  #
204
245
  # @see DSL#describe
205
246
  def self.define_example_group_method(name, metadata={})
206
- define_singleton_method(name) do |*args, &example_group_block|
207
- thread_data = RSpec.thread_local_metadata
247
+ idempotently_define_singleton_method(name) do |*args, &example_group_block|
248
+ thread_data = RSpec::Support.thread_local_data
208
249
  top_level = self == ExampleGroup
209
250
 
210
- if top_level
211
- if thread_data[:in_example_group]
212
- raise "Creating an isolated context from within a context is " +
213
- "not allowed. Change `RSpec.#{name}` to `#{name}` or " +
214
- "move this to a top-level scope."
251
+ registration_collection =
252
+ if top_level
253
+ if thread_data[:in_example_group]
254
+ raise "Creating an isolated context from within a context is " \
255
+ "not allowed. Change `RSpec.#{name}` to `#{name}` or " \
256
+ "move this to a top-level scope."
257
+ end
258
+
259
+ thread_data[:in_example_group] = true
260
+ RSpec.world.example_groups
261
+ else
262
+ children
215
263
  end
216
264
 
217
- thread_data[:in_example_group] = true
218
- end
219
-
220
265
  begin
221
-
222
266
  description = args.shift
223
267
  combined_metadata = metadata.dup
224
268
  combined_metadata.merge!(args.pop) if args.last.is_a? Hash
225
269
  args << combined_metadata
226
270
 
227
- subclass(self, description, args, &example_group_block).tap do |child|
228
- children << child
229
- end
230
-
271
+ subclass(self, description, args, registration_collection, &example_group_block)
231
272
  ensure
232
273
  thread_data.delete(:in_example_group) if top_level
233
274
  end
@@ -238,8 +279,8 @@ module RSpec
238
279
 
239
280
  define_example_group_method :example_group
240
281
 
241
- # An alias of `example_group`. Generally used when grouping
242
- # examples by a thing you are describing (e.g. an object, class or method).
282
+ # An alias of `example_group`. Generally used when grouping examples by a
283
+ # thing you are describing (e.g. an object, class or method).
243
284
  # @see example_group
244
285
  define_example_group_method :describe
245
286
 
@@ -274,12 +315,12 @@ module RSpec
274
315
  #
275
316
  # @see SharedExampleGroup
276
317
  def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
277
- define_singleton_method(new_name) do |name, *args, &customization_block|
278
- # Pass :caller so the :location metadata is set properly...
279
- # otherwise, it'll be set to the next line because that's
318
+ idempotently_define_singleton_method(new_name) do |name, *args, &customization_block|
319
+ # Pass :caller so the :location metadata is set properly.
320
+ # Otherwise, it'll be set to the next line because that's
280
321
  # the block's source_location.
281
- group = example_group("#{report_label} #{name}", :caller => caller) do
282
- find_and_eval_shared("examples", name, *args, &customization_block)
322
+ group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do
323
+ find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block)
283
324
  end
284
325
  group.metadata[:shared_group_name] = name
285
326
  group
@@ -295,39 +336,65 @@ module RSpec
295
336
 
296
337
  # Includes shared content mapped to `name` directly in the group in which
297
338
  # it is declared, as opposed to `it_behaves_like`, which creates a nested
298
- # group. If given a block, that block is also eval'd in the current context.
339
+ # group. If given a block, that block is also eval'd in the current
340
+ # context.
299
341
  #
300
342
  # @see SharedExampleGroup
301
343
  def self.include_context(name, *args, &block)
302
- find_and_eval_shared("context", name, *args, &block)
344
+ find_and_eval_shared("context", name, caller.first, *args, &block)
303
345
  end
304
346
 
305
347
  # Includes shared content mapped to `name` directly in the group in which
306
348
  # it is declared, as opposed to `it_behaves_like`, which creates a nested
307
- # group. If given a block, that block is also eval'd in the current context.
349
+ # group. If given a block, that block is also eval'd in the current
350
+ # context.
308
351
  #
309
352
  # @see SharedExampleGroup
310
353
  def self.include_examples(name, *args, &block)
311
- find_and_eval_shared("examples", name, *args, &block)
354
+ find_and_eval_shared("examples", name, caller.first, *args, &block)
312
355
  end
313
356
 
357
+ # Clear memoized values when adding/removing examples
314
358
  # @private
315
- def self.find_and_eval_shared(label, name, *args, &customization_block)
316
- unless shared_block = RSpec.world.shared_example_group_registry.find(parent_groups, name)
359
+ def self.reset_memoized
360
+ @descendant_filtered_examples = nil
361
+ @_descendants = nil
362
+ @parent_groups = nil
363
+ @declaration_locations = nil
364
+ end
365
+
366
+ # Adds an example to the example group
367
+ def self.add_example(example)
368
+ reset_memoized
369
+ examples << example
370
+ end
371
+
372
+ # Removes an example from the example group
373
+ def self.remove_example(example)
374
+ reset_memoized
375
+ examples.delete example
376
+ end
377
+
378
+ # @private
379
+ def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block)
380
+ shared_module = RSpec.world.shared_example_group_registry.find(parent_groups, name)
381
+
382
+ unless shared_module
317
383
  raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
318
384
  end
319
385
 
320
- module_exec(*args, &shared_block)
321
- module_exec(&customization_block) if customization_block
386
+ shared_module.include_in(
387
+ self, Metadata.relative_path(inclusion_location),
388
+ args, customization_block
389
+ )
322
390
  end
323
391
 
324
392
  # @!endgroup
325
393
 
326
394
  # @private
327
- def self.subclass(parent, description, args, &example_group_block)
395
+ def self.subclass(parent, description, args, registration_collection, &example_group_block)
328
396
  subclass = Class.new(parent)
329
- subclass.set_it_up(description, *args, &example_group_block)
330
- ExampleGroups.assign_const(subclass)
397
+ subclass.set_it_up(description, args, registration_collection, &example_group_block)
331
398
  subclass.module_exec(&example_group_block) if example_group_block
332
399
 
333
400
  # The LetDefinitions module must be included _after_ other modules
@@ -340,26 +407,43 @@ module RSpec
340
407
  end
341
408
 
342
409
  # @private
343
- def self.set_it_up(*args, &example_group_block)
410
+ def self.set_it_up(description, args, registration_collection, &example_group_block)
344
411
  # Ruby 1.9 has a bug that can lead to infinite recursion and a
345
412
  # SystemStackError if you include a module in a superclass after
346
413
  # including it in a subclass: https://gist.github.com/845896
347
- # To prevent this, we must include any modules in RSpec::Core::ExampleGroup
348
- # before users create example groups and have a chance to include
349
- # the same module in a subclass of RSpec::Core::ExampleGroup.
350
- # So we need to configure example groups here.
414
+ # To prevent this, we must include any modules in
415
+ # RSpec::Core::ExampleGroup before users create example groups and have
416
+ # a chance to include the same module in a subclass of
417
+ # RSpec::Core::ExampleGroup. So we need to configure example groups
418
+ # here.
351
419
  ensure_example_groups_are_configured
352
420
 
353
- description = args.shift
354
- user_metadata = Metadata.build_hash_from(args)
355
- args.unshift(description)
421
+ # Register the example with the group before creating the metadata hash.
422
+ # This is necessary since creating the metadata hash triggers
423
+ # `when_first_matching_example_defined` callbacks, in which users can
424
+ # load RSpec support code which defines hooks. For that to work, the
425
+ # examples and example groups must be registered at the time the
426
+ # support code is called or be defined afterwards.
427
+ # Begin defined beforehand but registered afterwards causes hooks to
428
+ # not be applied where they should.
429
+ registration_collection << self
430
+
431
+ @user_metadata = Metadata.build_hash_from(args)
356
432
 
357
433
  @metadata = Metadata::ExampleGroupHash.create(
358
- superclass_metadata, user_metadata, *args, &example_group_block
434
+ superclass_metadata, @user_metadata,
435
+ superclass.method(:next_runnable_index_for),
436
+ description, *args, &example_group_block
359
437
  )
360
438
 
361
- hooks.register_globals(self, RSpec.configuration.hooks)
362
- RSpec.world.configure_group(self)
439
+ config = RSpec.configuration
440
+ config.apply_derived_metadata_to(@metadata)
441
+
442
+ ExampleGroups.assign_const(self)
443
+
444
+ @currently_executing_a_context_hook = false
445
+
446
+ config.configure_group(self)
363
447
  end
364
448
 
365
449
  # @private
@@ -374,7 +458,8 @@ module RSpec
374
458
 
375
459
  # @private
376
460
  def self.descendant_filtered_examples
377
- @descendant_filtered_examples ||= filtered_examples + children.inject([]){|l,c| l + c.descendant_filtered_examples}
461
+ @descendant_filtered_examples ||= filtered_examples +
462
+ FlatMap.flat_map(children, &:descendant_filtered_examples)
378
463
  end
379
464
 
380
465
  # @private
@@ -382,19 +467,51 @@ module RSpec
382
467
  @children ||= []
383
468
  end
384
469
 
470
+ # @private
471
+ # Traverses the tree of groups, starting with `self`, then the children, recursively.
472
+ # Halts the traversal of a branch of the tree as soon as the passed block returns true.
473
+ # Note that siblings groups and their sub-trees will continue to be explored.
474
+ # This is intended to make it easy to find the top-most group that satisfies some
475
+ # condition.
476
+ def self.traverse_tree_until(&block)
477
+ return if yield self
478
+
479
+ children.each do |child|
480
+ child.traverse_tree_until(&block)
481
+ end
482
+ end
483
+
484
+ # @private
485
+ def self.next_runnable_index_for(file)
486
+ if self == ExampleGroup
487
+ # We add 1 so the ids start at 1 instead of 0. This is
488
+ # necessary for this branch (but not for the other one)
489
+ # because we register examples and groups with the
490
+ # `children` and `examples` collection BEFORE this
491
+ # method is called as part of metadata hash creation,
492
+ # but the example group is recorded with
493
+ # `RSpec.world.example_group_counts_by_spec_file` AFTER
494
+ # the metadata hash is created and the group is returned
495
+ # to the caller.
496
+ RSpec.world.num_example_groups_defined_in(file) + 1
497
+ else
498
+ children.count + examples.count
499
+ end
500
+ end
501
+
385
502
  # @private
386
503
  def self.descendants
387
- @_descendants ||= [self] + children.inject([]) {|list, c| list + c.descendants}
504
+ @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants)
388
505
  end
389
506
 
390
507
  ## @private
391
508
  def self.parent_groups
392
- @parent_groups ||= ancestors.select {|a| a < RSpec::Core::ExampleGroup}
509
+ @parent_groups ||= ancestors.select { |a| a < RSpec::Core::ExampleGroup }
393
510
  end
394
511
 
395
512
  # @private
396
513
  def self.top_level?
397
- @top_level ||= superclass == ExampleGroup
514
+ superclass == ExampleGroup
398
515
  end
399
516
 
400
517
  # @private
@@ -402,7 +519,9 @@ module RSpec
402
519
  unless defined?(@@example_groups_configured)
403
520
  RSpec.configuration.configure_mock_framework
404
521
  RSpec.configuration.configure_expectation_framework
522
+ # rubocop:disable Style/ClassVars
405
523
  @@example_groups_configured = true
524
+ # rubocop:enable Style/ClassVars
406
525
  end
407
526
  end
408
527
 
@@ -413,58 +532,91 @@ module RSpec
413
532
 
414
533
  # @private
415
534
  def self.store_before_context_ivars(example_group_instance)
416
- return if example_group_instance.instance_variables.empty?
417
-
418
- example_group_instance.instance_variables.each { |ivar|
535
+ each_instance_variable_for_example(example_group_instance) do |ivar|
419
536
  before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
420
- }
537
+ end
538
+ end
539
+
540
+ # Returns true if a `before(:context)` or `after(:context)`
541
+ # hook is currently executing.
542
+ def self.currently_executing_a_context_hook?
543
+ @currently_executing_a_context_hook
421
544
  end
422
545
 
423
546
  # @private
424
547
  def self.run_before_context_hooks(example_group_instance)
425
- return if descendant_filtered_examples.empty?
426
- begin
427
- set_ivars(example_group_instance, superclass.before_context_ivars)
548
+ set_ivars(example_group_instance, superclass_before_context_ivars)
549
+
550
+ @currently_executing_a_context_hook = true
551
+
552
+ ContextHookMemoized::Before.isolate_for_context_hook(example_group_instance) do
553
+ hooks.run(:before, :context, example_group_instance)
554
+ end
555
+ ensure
556
+ store_before_context_ivars(example_group_instance)
557
+ @currently_executing_a_context_hook = false
558
+ end
428
559
 
429
- ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do
430
- hooks.run(:before, :context, example_group_instance)
560
+ if RUBY_VERSION.to_f >= 1.9
561
+ # @private
562
+ def self.superclass_before_context_ivars
563
+ superclass.before_context_ivars
564
+ end
565
+ else # 1.8.7
566
+ # :nocov:
567
+ # @private
568
+ def self.superclass_before_context_ivars
569
+ if superclass.respond_to?(:before_context_ivars)
570
+ superclass.before_context_ivars
571
+ else
572
+ # `self` must be the singleton class of an ExampleGroup instance.
573
+ # On 1.8.7, the superclass of a singleton class of an instance of A
574
+ # is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor
575
+ # is A, so we can mirror 1.8.7's behavior here. Note that we have to
576
+ # search for the first that responds to `before_context_ivars`
577
+ # in case a module has been included in the singleton class.
578
+ ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars
431
579
  end
432
- ensure
433
- store_before_context_ivars(example_group_instance)
434
580
  end
581
+ # :nocov:
435
582
  end
436
583
 
437
584
  # @private
438
585
  def self.run_after_context_hooks(example_group_instance)
439
- return if descendant_filtered_examples.empty?
440
586
  set_ivars(example_group_instance, before_context_ivars)
441
587
 
442
- ContextHookMemoizedHash::After.isolate_for_context_hook(example_group_instance) do
588
+ @currently_executing_a_context_hook = true
589
+
590
+ ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do
443
591
  hooks.run(:after, :context, example_group_instance)
444
592
  end
593
+ ensure
594
+ before_context_ivars.clear
595
+ @currently_executing_a_context_hook = false
445
596
  end
446
597
 
447
- # Runs all the examples in this group
448
- def self.run(reporter)
449
- if RSpec.world.wants_to_quit
450
- RSpec.world.clear_remaining_example_groups if top_level?
451
- return
452
- end
598
+ # Runs all the examples in this group.
599
+ def self.run(reporter=RSpec::Core::NullReporter)
600
+ return if RSpec.world.wants_to_quit
453
601
  reporter.example_group_started(self)
454
602
 
603
+ should_run_context_hooks = descendant_filtered_examples.any?
455
604
  begin
456
- run_before_context_hooks(new)
605
+ RSpec.current_scope = :before_context_hook
606
+ run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks
457
607
  result_for_this_group = run_examples(reporter)
458
608
  results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
459
609
  result_for_this_group && results_for_descendants
460
610
  rescue Pending::SkipDeclaredInExample => ex
461
- for_filtered_examples(reporter) {|example| example.skip_with_exception(reporter, ex) }
462
- rescue Exception => ex
463
- RSpec.world.wants_to_quit = true if fail_fast?
464
- for_filtered_examples(reporter) {|example| example.fail_with_exception(reporter, ex) }
611
+ for_filtered_examples(reporter) { |example| example.skip_with_exception(reporter, ex) }
612
+ true
613
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
614
+ for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) }
615
+ RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met?
616
+ false
465
617
  ensure
466
- run_after_context_hooks(new)
467
- before_context_ivars.clear
618
+ RSpec.current_scope = :after_context_hook
619
+ run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
468
620
  reporter.example_group_finished(self)
469
621
  end
470
622
  end
@@ -489,10 +641,12 @@ module RSpec
489
641
  def self.run_examples(reporter)
490
642
  ordering_strategy.order(filtered_examples).map do |example|
491
643
  next if RSpec.world.wants_to_quit
492
- instance = new
644
+ instance = new(example.inspect_output)
493
645
  set_ivars(instance, before_context_ivars)
494
646
  succeeded = example.run(instance, reporter)
495
- RSpec.world.wants_to_quit = true if fail_fast? && !succeeded
647
+ if !succeeded && reporter.fail_fast_limit_met?
648
+ RSpec.world.wants_to_quit = true
649
+ end
496
650
  succeeded
497
651
  end.all?
498
652
  end
@@ -510,58 +664,111 @@ module RSpec
510
664
  end
511
665
 
512
666
  # @private
513
- def self.fail_fast?
514
- RSpec.configuration.fail_fast?
667
+ def self.declaration_locations
668
+ @declaration_locations ||= [Metadata.location_tuple_from(metadata)] +
669
+ examples.map { |e| Metadata.location_tuple_from(e.metadata) } +
670
+ FlatMap.flat_map(children, &:declaration_locations)
671
+ end
672
+
673
+ # @return [String] the unique id of this example group. Pass
674
+ # this at the command line to re-run this exact example group.
675
+ def self.id
676
+ Metadata.id_from(metadata)
515
677
  end
516
678
 
517
679
  # @private
518
- def self.any_apply?(filters)
519
- MetadataFilter.any_apply?(filters, metadata)
680
+ def self.top_level_description
681
+ parent_groups.last.description
520
682
  end
521
683
 
522
684
  # @private
523
- def self.all_apply?(filters)
524
- MetadataFilter.all_apply?(filters, metadata)
685
+ def self.set_ivars(instance, ivars)
686
+ ivars.each { |name, value| instance.instance_variable_set(name, value) }
687
+ end
688
+
689
+ if RUBY_VERSION.to_f < 1.9
690
+ # :nocov:
691
+ # @private
692
+ INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze
693
+ # :nocov:
694
+ else
695
+ # @private
696
+ INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output
525
697
  end
526
698
 
527
699
  # @private
528
- def self.declaration_line_numbers
529
- @declaration_line_numbers ||= [metadata[:line_number]] +
530
- examples.collect {|e| e.metadata[:line_number]} +
531
- children.inject([]) {|l,c| l + c.declaration_line_numbers}
700
+ def self.each_instance_variable_for_example(group)
701
+ group.instance_variables.each do |ivar|
702
+ yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE
703
+ end
532
704
  end
533
705
 
534
706
  # @private
535
- def self.top_level_description
536
- parent_groups.last.description
707
+ def initialize(inspect_output=nil)
708
+ @__inspect_output = inspect_output || '(no description provided)'
709
+ super() # no args get passed
537
710
  end
538
711
 
539
712
  # @private
540
- def self.set_ivars(instance, ivars)
541
- ivars.each {|name, value| instance.instance_variable_set(name, value)}
713
+ def inspect
714
+ "#<#{self.class} #{@__inspect_output}>"
715
+ end
716
+
717
+ unless method_defined?(:singleton_class) # for 1.8.7
718
+ # :nocov:
719
+ # @private
720
+ def singleton_class
721
+ class << self; self; end
722
+ end
723
+ # :nocov:
542
724
  end
543
725
 
544
726
  # @private
545
- def self.pending_metadata_and_block_for(options, block)
546
- if String === options[:pending]
547
- reason = options[:pending]
548
- else
549
- options[:pending] = true
550
- reason = RSpec::Core::Pending::NO_REASON_GIVEN
727
+ def self.update_inherited_metadata(updates)
728
+ metadata.update(updates) do |key, existing_group_value, new_inherited_value|
729
+ @user_metadata.key?(key) ? existing_group_value : new_inherited_value
551
730
  end
552
731
 
553
- # Assign :caller so that the callback's source_location isn't used
554
- # as the example location.
555
- options[:caller] ||= Metadata.backtrace_from(block)
732
+ RSpec.configuration.configure_group(self)
733
+ examples.each { |ex| ex.update_inherited_metadata(updates) }
734
+ children.each { |group| group.update_inherited_metadata(updates) }
735
+ end
556
736
 
557
- # This will fail if no block is provided, which is effectively the
558
- # same as failing the example so it will be marked correctly as
559
- # pending.
560
- callback = Proc.new { pending(reason); instance_exec(&block) }
737
+ # Raised when an RSpec API is called in the wrong scope, such as `before`
738
+ # being called from within an example rather than from within an example
739
+ # group block.
740
+ WrongScopeError = Class.new(NoMethodError)
561
741
 
562
- return options, callback
742
+ def self.method_missing(name, *args)
743
+ if method_defined?(name)
744
+ raise WrongScopeError,
745
+ "`#{name}` is not available on an example group (e.g. a " \
746
+ "`describe` or `context` block). It is only available from " \
747
+ "within individual examples (e.g. `it` blocks) or from " \
748
+ "constructs that run in the scope of an example (e.g. " \
749
+ "`before`, `let`, etc)."
750
+ end
751
+
752
+ super
563
753
  end
754
+ private_class_method :method_missing
755
+
756
+ private
757
+
758
+ def method_missing(name, *args)
759
+ if self.class.respond_to?(name)
760
+ raise WrongScopeError,
761
+ "`#{name}` is not available from within an example (e.g. an " \
762
+ "`it` block) or from constructs that run in the scope of an " \
763
+ "example (e.g. `before`, `let`, etc). It is only available " \
764
+ "on an example group (e.g. a `describe` or `context` block)."
765
+ end
766
+
767
+ super(name, *args)
768
+ end
769
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
564
770
  end
771
+ # rubocop:enable Metrics/ClassLength
565
772
 
566
773
  # @private
567
774
  # Unnamed example group used by `SuiteHookContext`.
@@ -570,88 +777,67 @@ module RSpec
570
777
  {}
571
778
  end
572
779
  end
573
- end
574
780
 
575
- # Provides recursive constant lookup methods useful for
576
- # constant stubbing.
577
- # @private
578
- module RecursiveConstMethods
579
- # We only want to consider constants that are defined directly on a
580
- # particular module, and not include top-level/inherited constants.
581
- # Unfortunately, the constant API changed between 1.8 and 1.9, so
582
- # we need to conditionally define methods to ignore the top-level/inherited
583
- # constants.
584
- #
585
- # Given:
586
- # class A; B = 1; end
587
- # class C < A; end
588
- #
589
- # On 1.8:
590
- # - C.const_get("Hash") # => ::Hash
591
- # - C.const_defined?("Hash") # => false
592
- # - C.constants # => ["B"]
593
- # - None of these methods accept the extra `inherit` argument
594
- # On 1.9:
595
- # - C.const_get("Hash") # => ::Hash
596
- # - C.const_defined?("Hash") # => true
597
- # - C.const_get("Hash", false) # => raises NameError
598
- # - C.const_defined?("Hash", false) # => false
599
- # - C.constants # => [:B]
600
- # - C.constants(false) #=> []
601
- if Module.method(:const_defined?).arity == 1
602
- def const_defined_on?(mod, const_name)
603
- mod.const_defined?(const_name)
604
- end
605
-
606
- def get_const_defined_on(mod, const_name)
607
- if const_defined_on?(mod, const_name)
608
- return mod.const_get(const_name)
609
- end
781
+ # Contains information about the inclusion site of a shared example group.
782
+ class SharedExampleGroupInclusionStackFrame
783
+ # @return [String] the name of the shared example group
784
+ attr_reader :shared_group_name
785
+ # @return [String] the location where the shared example was included
786
+ attr_reader :inclusion_location
610
787
 
611
- raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
788
+ # @private
789
+ def initialize(shared_group_name, inclusion_location)
790
+ @shared_group_name = shared_group_name
791
+ @inclusion_location = inclusion_location
612
792
  end
613
793
 
614
- def constants_defined_on(mod)
615
- mod.constants.select { |c| const_defined_on?(mod, c) }
616
- end
617
- else
618
- def const_defined_on?(mod, const_name)
619
- mod.const_defined?(const_name, false)
794
+ # @return [String] The {#inclusion_location}, formatted for display by a formatter.
795
+ def formatted_inclusion_location
796
+ @formatted_inclusion_location ||= begin
797
+ RSpec.configuration.backtrace_formatter.backtrace_line(
798
+ inclusion_location.sub(/(:\d+):in .+$/, '\1')
799
+ )
800
+ end
620
801
  end
621
802
 
622
- def get_const_defined_on(mod, const_name)
623
- mod.const_get(const_name, false)
803
+ # @return [String] Description of this stack frame, in the form used by
804
+ # RSpec's built-in formatters.
805
+ def description
806
+ @description ||= "Shared Example Group: #{shared_group_name.inspect} " \
807
+ "called from #{formatted_inclusion_location}"
624
808
  end
625
809
 
626
- def constants_defined_on(mod)
627
- mod.constants(false)
810
+ # @private
811
+ def self.current_backtrace
812
+ shared_example_group_inclusions.reverse
628
813
  end
629
- end
630
814
 
631
- def recursive_const_get(const_name)
632
- normalize_const_name(const_name).split('::').inject(Object) do |mod, name|
633
- get_const_defined_on(mod, name)
815
+ # @private
816
+ def self.with_frame(name, location)
817
+ current_stack = shared_example_group_inclusions
818
+ if current_stack.any? { |frame| frame.shared_group_name == name }
819
+ raise ArgumentError, "can't include shared examples recursively"
820
+ else
821
+ current_stack << new(name, location)
822
+ yield
823
+ end
824
+ ensure
825
+ current_stack.pop
634
826
  end
635
- end
636
827
 
637
- def recursive_const_defined?(const_name)
638
- normalize_const_name(const_name).split('::').inject([Object, '']) do |(mod, full_name), name|
639
- yield(full_name, name) if block_given? && !(Module === mod)
640
- return false unless const_defined_on?(mod, name)
641
- [get_const_defined_on(mod, name), [mod, name].join('::')]
828
+ # @private
829
+ def self.shared_example_group_inclusions
830
+ RSpec::Support.thread_local_data[:shared_example_group_inclusions] ||= []
642
831
  end
643
832
  end
644
-
645
- def normalize_const_name(const_name)
646
- const_name.sub(/\A::/, '')
647
- end
648
833
  end
649
834
 
650
835
  # @private
651
836
  #
652
- # Namespace for the example group subclasses generated by top-level `describe`.
837
+ # Namespace for the example group subclasses generated by top-level
838
+ # `describe`.
653
839
  module ExampleGroups
654
- extend RecursiveConstMethods
840
+ extend Support::RecursiveConstMethods
655
841
 
656
842
  def self.assign_const(group)
657
843
  base_name = base_name_for(group)
@@ -663,35 +849,57 @@ module RSpec
663
849
 
664
850
  def self.constant_scope_for(group)
665
851
  const_scope = group.superclass
666
- const_scope = self if const_scope == Core::ExampleGroup
852
+ const_scope = self if const_scope == ::RSpec::Core::ExampleGroup
667
853
  const_scope
668
854
  end
669
855
 
856
+ def self.remove_all_constants
857
+ constants.each do |constant|
858
+ __send__(:remove_const, constant)
859
+ end
860
+ end
861
+
670
862
  def self.base_name_for(group)
671
- return "Anonymous" if group.description.empty?
863
+ return "Anonymous".dup if group.description.empty?
672
864
 
673
- # convert to CamelCase
865
+ # Convert to CamelCase.
674
866
  name = ' ' + group.description
675
- name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) { $1.upcase }
867
+ name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
868
+ match = ::Regexp.last_match[1]
869
+ match.upcase!
870
+ match
871
+ end
676
872
 
677
- name.lstrip! # Remove leading whitespace
678
- name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
873
+ name.lstrip! # Remove leading whitespace
874
+ name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names
679
875
 
680
876
  # Ruby requires first const letter to be A-Z. Use `Nested`
681
877
  # as necessary to enforce that.
682
- name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
878
+ name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze)
683
879
 
684
880
  name
685
881
  end
686
882
 
883
+ if RUBY_VERSION == '1.9.2'
884
+ # :nocov:
885
+ class << self
886
+ alias _base_name_for base_name_for
887
+ def base_name_for(group)
888
+ _base_name_for(group) + '_'
889
+ end
890
+ end
891
+ private_class_method :_base_name_for
892
+ # :nocov:
893
+ end
894
+
687
895
  def self.disambiguate(name, const_scope)
688
896
  return name unless const_defined_on?(const_scope, name)
689
897
 
690
- # Add a trailing number if needed to disambiguate from an existing constant.
898
+ # Add a trailing number if needed to disambiguate from an existing
899
+ # constant.
691
900
  name << "_2"
692
901
  name.next! while const_defined_on?(const_scope, name)
693
902
  name
694
903
  end
695
904
  end
696
905
  end
697
-