sspec-core 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.yardopts +8 -0
  4. data/Changelog.md +2232 -0
  5. data/LICENSE.md +26 -0
  6. data/README.md +384 -0
  7. data/exe/rspec +4 -0
  8. data/lib/rspec/autorun.rb +3 -0
  9. data/lib/rspec/core.rb +185 -0
  10. data/lib/rspec/core/backtrace_formatter.rb +65 -0
  11. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  12. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  13. data/lib/rspec/core/bisect/fork_runner.rb +134 -0
  14. data/lib/rspec/core/bisect/server.rb +61 -0
  15. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  16. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  17. data/lib/rspec/core/bisect/utilities.rb +58 -0
  18. data/lib/rspec/core/configuration.rb +2289 -0
  19. data/lib/rspec/core/configuration_options.rb +233 -0
  20. data/lib/rspec/core/drb.rb +113 -0
  21. data/lib/rspec/core/dsl.rb +98 -0
  22. data/lib/rspec/core/example.rb +653 -0
  23. data/lib/rspec/core/example_group.rb +885 -0
  24. data/lib/rspec/core/example_status_persister.rb +235 -0
  25. data/lib/rspec/core/filter_manager.rb +231 -0
  26. data/lib/rspec/core/flat_map.rb +20 -0
  27. data/lib/rspec/core/formatters.rb +269 -0
  28. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  29. data/lib/rspec/core/formatters/base_formatter.rb +70 -0
  30. data/lib/rspec/core/formatters/base_text_formatter.rb +75 -0
  31. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  32. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  33. data/lib/rspec/core/formatters/console_codes.rb +68 -0
  34. data/lib/rspec/core/formatters/deprecation_formatter.rb +223 -0
  35. data/lib/rspec/core/formatters/documentation_formatter.rb +70 -0
  36. data/lib/rspec/core/formatters/exception_presenter.rb +508 -0
  37. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  38. data/lib/rspec/core/formatters/helpers.rb +110 -0
  39. data/lib/rspec/core/formatters/html_formatter.rb +153 -0
  40. data/lib/rspec/core/formatters/html_printer.rb +414 -0
  41. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  42. data/lib/rspec/core/formatters/json_formatter.rb +102 -0
  43. data/lib/rspec/core/formatters/profile_formatter.rb +68 -0
  44. data/lib/rspec/core/formatters/progress_formatter.rb +29 -0
  45. data/lib/rspec/core/formatters/protocol.rb +182 -0
  46. data/lib/rspec/core/formatters/snippet_extractor.rb +134 -0
  47. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  48. data/lib/rspec/core/hooks.rb +624 -0
  49. data/lib/rspec/core/invocations.rb +87 -0
  50. data/lib/rspec/core/memoized_helpers.rb +554 -0
  51. data/lib/rspec/core/metadata.rb +499 -0
  52. data/lib/rspec/core/metadata_filter.rb +255 -0
  53. data/lib/rspec/core/minitest_assertions_adapter.rb +31 -0
  54. data/lib/rspec/core/mocking_adapters/flexmock.rb +31 -0
  55. data/lib/rspec/core/mocking_adapters/mocha.rb +57 -0
  56. data/lib/rspec/core/mocking_adapters/null.rb +14 -0
  57. data/lib/rspec/core/mocking_adapters/rr.rb +31 -0
  58. data/lib/rspec/core/mocking_adapters/rspec.rb +32 -0
  59. data/lib/rspec/core/notifications.rb +521 -0
  60. data/lib/rspec/core/option_parser.rb +309 -0
  61. data/lib/rspec/core/ordering.rb +158 -0
  62. data/lib/rspec/core/output_wrapper.rb +29 -0
  63. data/lib/rspec/core/pending.rb +165 -0
  64. data/lib/rspec/core/profiler.rb +34 -0
  65. data/lib/rspec/core/project_initializer.rb +48 -0
  66. data/lib/rspec/core/project_initializer/.rspec +1 -0
  67. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +100 -0
  68. data/lib/rspec/core/rake_task.rb +168 -0
  69. data/lib/rspec/core/reporter.rb +257 -0
  70. data/lib/rspec/core/ruby_project.rb +53 -0
  71. data/lib/rspec/core/runner.rb +199 -0
  72. data/lib/rspec/core/sandbox.rb +37 -0
  73. data/lib/rspec/core/set.rb +54 -0
  74. data/lib/rspec/core/shared_context.rb +55 -0
  75. data/lib/rspec/core/shared_example_group.rb +269 -0
  76. data/lib/rspec/core/shell_escape.rb +49 -0
  77. data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
  78. data/lib/rspec/core/version.rb +9 -0
  79. data/lib/rspec/core/warnings.rb +40 -0
  80. data/lib/rspec/core/world.rb +275 -0
  81. metadata +257 -0
@@ -0,0 +1,499 @@
1
+ module RSpec
2
+ module Core
3
+ # Each ExampleGroup class and Example instance owns an instance of
4
+ # Metadata, which is Hash extended to support lazy evaluation of values
5
+ # associated with keys that may or may not be used by any example or group.
6
+ #
7
+ # In addition to metadata that is used internally, this also stores
8
+ # user-supplied metadata, e.g.
9
+ #
10
+ # describe Something, :type => :ui do
11
+ # it "does something", :slow => true do
12
+ # # ...
13
+ # end
14
+ # end
15
+ #
16
+ # `:type => :ui` is stored in the Metadata owned by the example group, and
17
+ # `:slow => true` is stored in the Metadata owned by the example. These can
18
+ # then be used to select which examples are run using the `--tag` option on
19
+ # the command line, or several methods on `Configuration` used to filter a
20
+ # run (e.g. `filter_run_including`, `filter_run_excluding`, etc).
21
+ #
22
+ # @see Example#metadata
23
+ # @see ExampleGroup.metadata
24
+ # @see FilterManager
25
+ # @see Configuration#filter_run_including
26
+ # @see Configuration#filter_run_excluding
27
+ module Metadata
28
+ # Matches strings either at the beginning of the input or prefixed with a
29
+ # whitespace, containing the current path, either postfixed with the
30
+ # separator, or at the end of the string. Match groups are the character
31
+ # before and the character after the string if any.
32
+ #
33
+ # http://rubular.com/r/fT0gmX6VJX
34
+ # http://rubular.com/r/duOrD4i3wb
35
+ # http://rubular.com/r/sbAMHFrOx1
36
+ def self.relative_path_regex
37
+ @relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/
38
+ end
39
+
40
+ # @api private
41
+ #
42
+ # @param line [String] current code line
43
+ # @return [String] relative path to line
44
+ def self.relative_path(line)
45
+ line = line.sub(relative_path_regex, "\\1.\\2".freeze)
46
+ line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
47
+ return nil if line == '-e:1'.freeze
48
+ line
49
+ rescue SecurityError
50
+ # :nocov:
51
+ nil
52
+ # :nocov:
53
+ end
54
+
55
+ # @private
56
+ # Iteratively walks up from the given metadata through all
57
+ # example group ancestors, yielding each metadata hash along the way.
58
+ def self.ascending(metadata)
59
+ yield metadata
60
+ return unless (group_metadata = metadata.fetch(:example_group) { metadata[:parent_example_group] })
61
+
62
+ loop do
63
+ yield group_metadata
64
+ break unless (group_metadata = group_metadata[:parent_example_group])
65
+ end
66
+ end
67
+
68
+ # @private
69
+ # Returns an enumerator that iteratively walks up the given metadata through all
70
+ # example group ancestors, yielding each metadata hash along the way.
71
+ def self.ascend(metadata)
72
+ enum_for(:ascending, metadata)
73
+ end
74
+
75
+ # @private
76
+ # Used internally to build a hash from an args array.
77
+ # Symbols are converted into hash keys with a value of `true`.
78
+ # This is done to support simple tagging using a symbol, rather
79
+ # than needing to do `:symbol => true`.
80
+ def self.build_hash_from(args, warn_about_example_group_filtering=false)
81
+ hash = args.last.is_a?(Hash) ? args.pop : {}
82
+
83
+ hash[args.pop] = true while args.last.is_a?(Symbol)
84
+
85
+ if warn_about_example_group_filtering && hash.key?(:example_group)
86
+ RSpec.deprecate("Filtering by an `:example_group` subhash",
87
+ :replacement => "the subhash to filter directly")
88
+ end
89
+
90
+ hash
91
+ end
92
+
93
+ # @private
94
+ def self.deep_hash_dup(object)
95
+ return object.dup if Array === object
96
+ return object unless Hash === object
97
+
98
+ object.inject(object.dup) do |duplicate, (key, value)|
99
+ duplicate[key] = deep_hash_dup(value)
100
+ duplicate
101
+ end
102
+ end
103
+
104
+ # @private
105
+ def self.id_from(metadata)
106
+ "#{metadata[:rerun_file_path]}[#{metadata[:scoped_id]}]"
107
+ end
108
+
109
+ # @private
110
+ def self.location_tuple_from(metadata)
111
+ [metadata[:absolute_file_path], metadata[:line_number]]
112
+ end
113
+
114
+ # @private
115
+ # Used internally to populate metadata hashes with computed keys
116
+ # managed by RSpec.
117
+ class HashPopulator
118
+ attr_reader :metadata, :user_metadata, :description_args, :block
119
+
120
+ def initialize(metadata, user_metadata, index_provider, description_args, block)
121
+ @metadata = metadata
122
+ @user_metadata = user_metadata
123
+ @index_provider = index_provider
124
+ @description_args = description_args
125
+ @block = block
126
+ end
127
+
128
+ def populate
129
+ ensure_valid_user_keys
130
+
131
+ metadata[:block] = block
132
+ metadata[:description_args] = description_args
133
+ metadata[:description] = build_description_from(*metadata[:description_args])
134
+ metadata[:full_description] = full_description
135
+ metadata[:described_class] = described_class
136
+
137
+ populate_location_attributes
138
+ metadata.update(user_metadata)
139
+ RSpec.configuration.apply_derived_metadata_to(metadata)
140
+ end
141
+
142
+ private
143
+
144
+ def populate_location_attributes
145
+ backtrace = user_metadata.delete(:caller)
146
+
147
+ file_path, line_number = if backtrace
148
+ file_path_and_line_number_from(backtrace)
149
+ elsif block.respond_to?(:source_location)
150
+ block.source_location
151
+ else
152
+ file_path_and_line_number_from(caller)
153
+ end
154
+
155
+ relative_file_path = Metadata.relative_path(file_path)
156
+ absolute_file_path = File.expand_path(relative_file_path)
157
+ metadata[:file_path] = relative_file_path
158
+ metadata[:line_number] = line_number.to_i
159
+ metadata[:location] = "#{relative_file_path}:#{line_number}"
160
+ metadata[:absolute_file_path] = absolute_file_path
161
+ metadata[:rerun_file_path] ||= relative_file_path
162
+ metadata[:scoped_id] = build_scoped_id_for(absolute_file_path)
163
+ end
164
+
165
+ def file_path_and_line_number_from(backtrace)
166
+ first_caller_from_outside_rspec = backtrace.find { |l| l !~ CallerFilter::LIB_REGEX }
167
+ first_caller_from_outside_rspec ||= backtrace.first
168
+ /(.+?):(\d+)(?:|:\d+)/.match(first_caller_from_outside_rspec).captures
169
+ end
170
+
171
+ def description_separator(parent_part, child_part)
172
+ if parent_part.is_a?(Module) && /^(?:#|::|\.)/.match(child_part.to_s)
173
+ ''.freeze
174
+ else
175
+ ' '.freeze
176
+ end
177
+ end
178
+
179
+ def build_description_from(parent_description=nil, my_description=nil)
180
+ return parent_description.to_s unless my_description
181
+ return my_description.to_s if parent_description.to_s == ''
182
+ separator = description_separator(parent_description, my_description)
183
+ (parent_description.to_s + separator) << my_description.to_s
184
+ end
185
+
186
+ def build_scoped_id_for(file_path)
187
+ index = @index_provider.call(file_path).to_s
188
+ parent_scoped_id = metadata.fetch(:scoped_id) { return index }
189
+ "#{parent_scoped_id}:#{index}"
190
+ end
191
+
192
+ def ensure_valid_user_keys
193
+ RESERVED_KEYS.each do |key|
194
+ next unless user_metadata.key?(key)
195
+ raise <<-EOM.gsub(/^\s+\|/, '')
196
+ |#{"*" * 50}
197
+ |:#{key} is not allowed
198
+ |
199
+ |RSpec reserves some hash keys for its own internal use,
200
+ |including :#{key}, which is used on:
201
+ |
202
+ | #{CallerFilter.first_non_rspec_line}.
203
+ |
204
+ |Here are all of RSpec's reserved hash keys:
205
+ |
206
+ | #{RESERVED_KEYS.join("\n ")}
207
+ |#{"*" * 50}
208
+ EOM
209
+ end
210
+ end
211
+ end
212
+
213
+ # @private
214
+ class ExampleHash < HashPopulator
215
+ def self.create(group_metadata, user_metadata, index_provider, description, block)
216
+ example_metadata = group_metadata.dup
217
+ group_metadata = Hash.new(&ExampleGroupHash.backwards_compatibility_default_proc do |hash|
218
+ hash[:parent_example_group]
219
+ end)
220
+ group_metadata.update(example_metadata)
221
+
222
+ example_metadata[:execution_result] = Example::ExecutionResult.new
223
+ example_metadata[:example_group] = group_metadata
224
+ example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace
225
+ example_metadata.delete(:parent_example_group)
226
+
227
+ description_args = description.nil? ? [] : [description]
228
+ hash = new(example_metadata, user_metadata, index_provider, description_args, block)
229
+ hash.populate
230
+ hash.metadata
231
+ end
232
+
233
+ private
234
+
235
+ def described_class
236
+ metadata[:example_group][:described_class]
237
+ end
238
+
239
+ def full_description
240
+ build_description_from(
241
+ metadata[:example_group][:full_description],
242
+ metadata[:description]
243
+ )
244
+ end
245
+ end
246
+
247
+ # @private
248
+ class ExampleGroupHash < HashPopulator
249
+ def self.create(parent_group_metadata, user_metadata, example_group_index, *args, &block)
250
+ group_metadata = hash_with_backwards_compatibility_default_proc
251
+
252
+ if parent_group_metadata
253
+ group_metadata.update(parent_group_metadata)
254
+ group_metadata[:parent_example_group] = parent_group_metadata
255
+ end
256
+
257
+ hash = new(group_metadata, user_metadata, example_group_index, args, block)
258
+ hash.populate
259
+ hash.metadata
260
+ end
261
+
262
+ def self.hash_with_backwards_compatibility_default_proc
263
+ Hash.new(&backwards_compatibility_default_proc { |hash| hash })
264
+ end
265
+
266
+ def self.backwards_compatibility_default_proc(&example_group_selector)
267
+ Proc.new do |hash, key|
268
+ case key
269
+ when :example_group
270
+ # We commonly get here when rspec-core is applying a previously
271
+ # configured filter rule, such as when a gem configures:
272
+ #
273
+ # RSpec.configure do |c|
274
+ # c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ }
275
+ # end
276
+ #
277
+ # It's confusing for a user to get a deprecation at this point in
278
+ # the code, so instead we issue a deprecation from the config APIs
279
+ # that take a metadata hash, and MetadataFilter sets this thread
280
+ # local to silence the warning here since it would be so
281
+ # confusing.
282
+ unless RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations]
283
+ RSpec.deprecate("The `:example_group` key in an example group's metadata hash",
284
+ :replacement => "the example group's hash directly for the " \
285
+ "computed keys and `:parent_example_group` to access the parent " \
286
+ "example group metadata")
287
+ end
288
+
289
+ group_hash = example_group_selector.call(hash)
290
+ LegacyExampleGroupHash.new(group_hash) if group_hash
291
+ when :example_group_block
292
+ RSpec.deprecate("`metadata[:example_group_block]`",
293
+ :replacement => "`metadata[:block]`")
294
+ hash[:block]
295
+ when :describes
296
+ RSpec.deprecate("`metadata[:describes]`",
297
+ :replacement => "`metadata[:described_class]`")
298
+ hash[:described_class]
299
+ end
300
+ end
301
+ end
302
+
303
+ private
304
+
305
+ def described_class
306
+ candidate = metadata[:description_args].first
307
+ return candidate unless NilClass === candidate || String === candidate
308
+ parent_group = metadata[:parent_example_group]
309
+ parent_group && parent_group[:described_class]
310
+ end
311
+
312
+ def full_description
313
+ description = metadata[:description]
314
+ parent_example_group = metadata[:parent_example_group]
315
+ return description unless parent_example_group
316
+
317
+ parent_description = parent_example_group[:full_description]
318
+ separator = description_separator(parent_example_group[:description_args].last,
319
+ metadata[:description_args].first)
320
+
321
+ parent_description + separator + description
322
+ end
323
+ end
324
+
325
+ # @private
326
+ RESERVED_KEYS = [
327
+ :description,
328
+ :description_args,
329
+ :described_class,
330
+ :example_group,
331
+ :parent_example_group,
332
+ :execution_result,
333
+ :last_run_status,
334
+ :file_path,
335
+ :absolute_file_path,
336
+ :rerun_file_path,
337
+ :full_description,
338
+ :line_number,
339
+ :location,
340
+ :scoped_id,
341
+ :block,
342
+ :shared_group_inclusion_backtrace
343
+ ]
344
+ end
345
+
346
+ # Mixin that makes the including class imitate a hash for backwards
347
+ # compatibility. The including class should use `attr_accessor` to
348
+ # declare attributes.
349
+ # @private
350
+ module HashImitatable
351
+ def self.included(klass)
352
+ klass.extend ClassMethods
353
+ end
354
+
355
+ def to_h
356
+ hash = extra_hash_attributes.dup
357
+
358
+ self.class.hash_attribute_names.each do |name|
359
+ hash[name] = __send__(name)
360
+ end
361
+
362
+ hash
363
+ end
364
+
365
+ (Hash.public_instance_methods - Object.public_instance_methods).each do |method_name|
366
+ next if [:[], :[]=, :to_h].include?(method_name.to_sym)
367
+
368
+ define_method(method_name) do |*args, &block|
369
+ issue_deprecation(method_name, *args)
370
+
371
+ hash = hash_for_delegation
372
+ self.class.hash_attribute_names.each do |name|
373
+ hash.delete(name) unless instance_variable_defined?(:"@#{name}")
374
+ end
375
+
376
+ hash.__send__(method_name, *args, &block).tap do
377
+ # apply mutations back to the object
378
+ hash.each do |name, value|
379
+ if directly_supports_attribute?(name)
380
+ set_value(name, value)
381
+ else
382
+ extra_hash_attributes[name] = value
383
+ end
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ def [](key)
390
+ issue_deprecation(:[], key)
391
+
392
+ if directly_supports_attribute?(key)
393
+ get_value(key)
394
+ else
395
+ extra_hash_attributes[key]
396
+ end
397
+ end
398
+
399
+ def []=(key, value)
400
+ issue_deprecation(:[]=, key, value)
401
+
402
+ if directly_supports_attribute?(key)
403
+ set_value(key, value)
404
+ else
405
+ extra_hash_attributes[key] = value
406
+ end
407
+ end
408
+
409
+ private
410
+
411
+ def extra_hash_attributes
412
+ @extra_hash_attributes ||= {}
413
+ end
414
+
415
+ def directly_supports_attribute?(name)
416
+ self.class.hash_attribute_names.include?(name)
417
+ end
418
+
419
+ def get_value(name)
420
+ __send__(name)
421
+ end
422
+
423
+ def set_value(name, value)
424
+ __send__(:"#{name}=", value)
425
+ end
426
+
427
+ def hash_for_delegation
428
+ to_h
429
+ end
430
+
431
+ def issue_deprecation(_method_name, *_args)
432
+ # no-op by default: subclasses can override
433
+ end
434
+
435
+ # @private
436
+ module ClassMethods
437
+ def hash_attribute_names
438
+ @hash_attribute_names ||= []
439
+ end
440
+
441
+ def attr_accessor(*names)
442
+ hash_attribute_names.concat(names)
443
+ super
444
+ end
445
+ end
446
+ end
447
+
448
+ # @private
449
+ # Together with the example group metadata hash default block,
450
+ # provides backwards compatibility for the old `:example_group`
451
+ # key. In RSpec 2.x, the computed keys of a group's metadata
452
+ # were exposed from a nested subhash keyed by `[:example_group]`, and
453
+ # then the parent group's metadata was exposed by sub-subhash
454
+ # keyed by `[:example_group][:example_group]`.
455
+ #
456
+ # In RSpec 3, we reorganized this to that the computed keys are
457
+ # exposed directly of the group metadata hash (no nesting), and
458
+ # `:parent_example_group` returns the parent group's metadata.
459
+ #
460
+ # Maintaining backwards compatibility was difficult: we wanted
461
+ # `:example_group` to return an object that:
462
+ #
463
+ # * Exposes the top-level metadata keys that used to be nested
464
+ # under `:example_group`.
465
+ # * Supports mutation (rspec-rails, for example, assigns
466
+ # `metadata[:example_group][:described_class]` when you use
467
+ # anonymous controller specs) such that changes are written
468
+ # back to the top-level metadata hash.
469
+ # * Exposes the parent group metadata as
470
+ # `[:example_group][:example_group]`.
471
+ class LegacyExampleGroupHash
472
+ include HashImitatable
473
+
474
+ def initialize(metadata)
475
+ @metadata = metadata
476
+ parent_group_metadata = metadata.fetch(:parent_example_group) { {} }[:example_group]
477
+ self[:example_group] = parent_group_metadata if parent_group_metadata
478
+ end
479
+
480
+ def to_h
481
+ super.merge(@metadata)
482
+ end
483
+
484
+ private
485
+
486
+ def directly_supports_attribute?(name)
487
+ name != :example_group
488
+ end
489
+
490
+ def get_value(name)
491
+ @metadata[name]
492
+ end
493
+
494
+ def set_value(name, value)
495
+ @metadata[name] = value
496
+ end
497
+ end
498
+ end
499
+ end