rspec-core 3.8.2

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