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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +2 -1
- data/Changelog.md +888 -2
- data/{License.txt → LICENSE.md} +6 -5
- data/README.md +165 -24
- data/lib/rspec/autorun.rb +1 -0
- data/lib/rspec/core/backtrace_formatter.rb +19 -20
- data/lib/rspec/core/bisect/coordinator.rb +62 -0
- data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
- data/lib/rspec/core/bisect/fork_runner.rb +138 -0
- data/lib/rspec/core/bisect/server.rb +61 -0
- data/lib/rspec/core/bisect/shell_command.rb +126 -0
- data/lib/rspec/core/bisect/shell_runner.rb +73 -0
- data/lib/rspec/core/bisect/utilities.rb +69 -0
- data/lib/rspec/core/configuration.rb +1287 -246
- data/lib/rspec/core/configuration_options.rb +95 -35
- data/lib/rspec/core/did_you_mean.rb +46 -0
- data/lib/rspec/core/drb.rb +21 -12
- data/lib/rspec/core/dsl.rb +10 -6
- data/lib/rspec/core/example.rb +305 -113
- data/lib/rspec/core/example_group.rb +431 -223
- data/lib/rspec/core/example_status_persister.rb +235 -0
- data/lib/rspec/core/filter_manager.rb +86 -115
- data/lib/rspec/core/flat_map.rb +6 -4
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/base_formatter.rb +14 -116
- data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
- data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
- data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
- data/lib/rspec/core/formatters/console_codes.rb +29 -18
- data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
- data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
- data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
- data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
- data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
- data/lib/rspec/core/formatters/helpers.rb +45 -15
- data/lib/rspec/core/formatters/html_formatter.rb +33 -28
- data/lib/rspec/core/formatters/html_printer.rb +30 -20
- data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
- data/lib/rspec/core/formatters/json_formatter.rb +18 -9
- data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
- data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
- data/lib/rspec/core/formatters/protocol.rb +182 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
- data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
- data/lib/rspec/core/formatters.rb +81 -41
- data/lib/rspec/core/hooks.rb +314 -244
- data/lib/rspec/core/invocations.rb +87 -0
- data/lib/rspec/core/memoized_helpers.rb +161 -51
- data/lib/rspec/core/metadata.rb +132 -61
- data/lib/rspec/core/metadata_filter.rb +224 -64
- data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
- data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
- data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
- data/lib/rspec/core/mocking_adapters/null.rb +2 -0
- data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
- data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
- data/lib/rspec/core/notifications.rb +192 -206
- data/lib/rspec/core/option_parser.rb +174 -69
- data/lib/rspec/core/ordering.rb +48 -35
- data/lib/rspec/core/output_wrapper.rb +29 -0
- data/lib/rspec/core/pending.rb +25 -33
- data/lib/rspec/core/profiler.rb +34 -0
- data/lib/rspec/core/project_initializer/.rspec +0 -2
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
- data/lib/rspec/core/project_initializer.rb +5 -3
- data/lib/rspec/core/rake_task.rb +99 -55
- data/lib/rspec/core/reporter.rb +128 -15
- data/lib/rspec/core/ruby_project.rb +14 -6
- data/lib/rspec/core/runner.rb +96 -45
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/set.rb +54 -0
- data/lib/rspec/core/shared_example_group.rb +133 -43
- data/lib/rspec/core/shell_escape.rb +49 -0
- data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/warnings.rb +6 -6
- data/lib/rspec/core/world.rb +172 -68
- data/lib/rspec/core.rb +66 -21
- data.tar.gz.sig +0 -0
- metadata +93 -69
- metadata.gz.sig +0 -0
- 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.
|
|
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
|
-
#
|
|
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
|
|
23
|
-
# There are additional instance methods available to
|
|
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
|
|
30
|
+
extend Hooks
|
|
27
31
|
|
|
28
32
|
include MemoizedHelpers
|
|
29
|
-
extend
|
|
33
|
+
extend MemoizedHelpers::ClassMethods
|
|
30
34
|
include Pending
|
|
31
|
-
extend
|
|
35
|
+
extend SharedExampleGroup
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
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 ||=
|
|
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
|
-
|
|
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
|
-
# @
|
|
91
|
-
# @
|
|
92
|
-
# @
|
|
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
|
-
|
|
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
|
-
|
|
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]
|
|
205
|
+
# @macro [attach] define_example_group_method
|
|
179
206
|
# @!scope class
|
|
180
|
-
# @
|
|
181
|
-
# @
|
|
182
|
-
#
|
|
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
|
-
|
|
207
|
-
thread_data = RSpec.
|
|
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
|
-
|
|
211
|
-
if
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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)
|
|
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
|
-
#
|
|
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
|
-
|
|
278
|
-
# Pass :caller so the :location metadata is set properly
|
|
279
|
-
#
|
|
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
|
|
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
|
|
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.
|
|
316
|
-
|
|
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
|
-
|
|
321
|
-
|
|
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,
|
|
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(
|
|
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
|
|
348
|
-
# before users create example groups and have
|
|
349
|
-
# the same module in a subclass of
|
|
350
|
-
# So we need to configure example groups
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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,
|
|
434
|
+
superclass_metadata, @user_metadata,
|
|
435
|
+
superclass.method(:next_runnable_index_for),
|
|
436
|
+
description, *args, &example_group_block
|
|
359
437
|
)
|
|
360
438
|
|
|
361
|
-
|
|
362
|
-
|
|
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 +
|
|
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] +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
|
|
430
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
463
|
-
|
|
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
|
-
|
|
467
|
-
|
|
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
|
-
|
|
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.
|
|
514
|
-
|
|
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.
|
|
519
|
-
|
|
680
|
+
def self.top_level_description
|
|
681
|
+
parent_groups.last.description
|
|
520
682
|
end
|
|
521
683
|
|
|
522
684
|
# @private
|
|
523
|
-
def self.
|
|
524
|
-
|
|
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.
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
|
536
|
-
|
|
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
|
|
541
|
-
|
|
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.
|
|
546
|
-
|
|
547
|
-
|
|
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
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
-
|
|
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
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
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
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
|
|
623
|
-
|
|
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
|
-
|
|
627
|
-
|
|
810
|
+
# @private
|
|
811
|
+
def self.current_backtrace
|
|
812
|
+
shared_example_group_inclusions.reverse
|
|
628
813
|
end
|
|
629
|
-
end
|
|
630
814
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
|
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
|
-
#
|
|
865
|
+
# Convert to CamelCase.
|
|
674
866
|
name = ' ' + group.description
|
|
675
|
-
name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/)
|
|
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!
|
|
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
|
|
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
|
-
|