rspec-core 3.2.3 → 3.3.0

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 (55) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +75 -0
  5. data/README.md +137 -20
  6. data/lib/rspec/autorun.rb +1 -0
  7. data/lib/rspec/core.rb +8 -16
  8. data/lib/rspec/core/backtrace_formatter.rb +1 -3
  9. data/lib/rspec/core/bisect/coordinator.rb +66 -0
  10. data/lib/rspec/core/bisect/example_minimizer.rb +130 -0
  11. data/lib/rspec/core/bisect/runner.rb +139 -0
  12. data/lib/rspec/core/bisect/server.rb +61 -0
  13. data/lib/rspec/core/bisect/subset_enumerator.rb +39 -0
  14. data/lib/rspec/core/configuration.rb +134 -5
  15. data/lib/rspec/core/configuration_options.rb +21 -10
  16. data/lib/rspec/core/example.rb +84 -50
  17. data/lib/rspec/core/example_group.rb +46 -18
  18. data/lib/rspec/core/example_status_persister.rb +235 -0
  19. data/lib/rspec/core/filter_manager.rb +43 -28
  20. data/lib/rspec/core/flat_map.rb +2 -0
  21. data/lib/rspec/core/formatters.rb +30 -20
  22. data/lib/rspec/core/formatters/base_text_formatter.rb +1 -0
  23. data/lib/rspec/core/formatters/bisect_formatter.rb +68 -0
  24. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +115 -0
  25. data/lib/rspec/core/formatters/deprecation_formatter.rb +0 -1
  26. data/lib/rspec/core/formatters/documentation_formatter.rb +0 -4
  27. data/lib/rspec/core/formatters/exception_presenter.rb +389 -0
  28. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  29. data/lib/rspec/core/formatters/helpers.rb +22 -2
  30. data/lib/rspec/core/formatters/html_formatter.rb +1 -4
  31. data/lib/rspec/core/formatters/html_printer.rb +2 -6
  32. data/lib/rspec/core/formatters/json_formatter.rb +6 -4
  33. data/lib/rspec/core/formatters/snippet_extractor.rb +12 -7
  34. data/lib/rspec/core/hooks.rb +8 -2
  35. data/lib/rspec/core/memoized_helpers.rb +77 -17
  36. data/lib/rspec/core/metadata.rb +24 -10
  37. data/lib/rspec/core/metadata_filter.rb +16 -3
  38. data/lib/rspec/core/mutex.rb +63 -0
  39. data/lib/rspec/core/notifications.rb +84 -189
  40. data/lib/rspec/core/option_parser.rb +105 -32
  41. data/lib/rspec/core/ordering.rb +28 -25
  42. data/lib/rspec/core/profiler.rb +32 -0
  43. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +6 -1
  44. data/lib/rspec/core/rake_task.rb +6 -20
  45. data/lib/rspec/core/reentrant_mutex.rb +52 -0
  46. data/lib/rspec/core/reporter.rb +65 -17
  47. data/lib/rspec/core/runner.rb +38 -14
  48. data/lib/rspec/core/set.rb +49 -0
  49. data/lib/rspec/core/shared_example_group.rb +3 -1
  50. data/lib/rspec/core/shell_escape.rb +49 -0
  51. data/lib/rspec/core/version.rb +1 -1
  52. data/lib/rspec/core/world.rb +31 -20
  53. metadata +35 -7
  54. metadata.gz.sig +0 -0
  55. data/lib/rspec/core/backport_random.rb +0 -339
@@ -1,6 +1,5 @@
1
1
  require 'erb'
2
2
  require 'shellwords'
3
- require 'set'
4
3
 
5
4
  module RSpec
6
5
  module Core
@@ -53,12 +52,12 @@ module RSpec
53
52
  end
54
53
  end
55
54
 
56
- UNFORCED_OPTIONS = [
55
+ UNFORCED_OPTIONS = Set.new([
57
56
  :requires, :profile, :drb, :libs, :files_or_directories_to_run,
58
57
  :full_description, :full_backtrace, :tty
59
- ].to_set
58
+ ])
60
59
 
61
- UNPROCESSABLE_OPTIONS = [:formatters].to_set
60
+ UNPROCESSABLE_OPTIONS = Set.new([:formatters])
62
61
 
63
62
  def force?(key)
64
63
  !UNFORCED_OPTIONS.include?(key)
@@ -82,7 +81,7 @@ module RSpec
82
81
 
83
82
  # `files_or_directories_to_run` uses `default_path` so it must be
84
83
  # set before it.
85
- :default_path,
84
+ :default_path, :only_failures,
86
85
 
87
86
  # These must be set before `requires` to support checking
88
87
  # `config.files_to_run` from within `spec_helper.rb` when a
@@ -120,11 +119,16 @@ module RSpec
120
119
  end
121
120
 
122
121
  def env_options
123
- ENV["SPEC_OPTS"] ? Parser.parse(Shellwords.split(ENV["SPEC_OPTS"])) : {}
122
+ return {} unless ENV['SPEC_OPTS']
123
+
124
+ parse_args_ignoring_files_or_dirs_to_run(
125
+ Shellwords.split(ENV["SPEC_OPTS"]),
126
+ "ENV['SPEC_OPTS']"
127
+ )
124
128
  end
125
129
 
126
130
  def command_line_options
127
- @command_line_options ||= Parser.parse(@args).merge :files_or_directories_to_run => @args
131
+ @command_line_options ||= Parser.parse(@args)
128
132
  end
129
133
 
130
134
  def custom_options
@@ -144,7 +148,14 @@ module RSpec
144
148
  end
145
149
 
146
150
  def options_from(path)
147
- Parser.parse(args_from_options_file(path))
151
+ args = args_from_options_file(path)
152
+ parse_args_ignoring_files_or_dirs_to_run(args, path)
153
+ end
154
+
155
+ def parse_args_ignoring_files_or_dirs_to_run(args, source)
156
+ options = Parser.parse(args, source)
157
+ options.delete(:files_or_directories_to_run)
158
+ options
148
159
  end
149
160
 
150
161
  def args_from_options_file(path)
@@ -162,11 +173,11 @@ module RSpec
162
173
  end
163
174
 
164
175
  def project_options_file
165
- ".rspec"
176
+ "./.rspec"
166
177
  end
167
178
 
168
179
  def local_options_file
169
- ".rspec-local"
180
+ "./.rspec-local"
170
181
  end
171
182
 
172
183
  def global_options_file
@@ -92,15 +92,32 @@ module RSpec
92
92
  inspect_output
93
93
  end
94
94
 
95
- # Returns the argument that can be passed to the `rspec` command to rerun this example.
96
- def rerun_argument
97
- loaded_spec_files = RSpec.configuration.loaded_spec_files
95
+ # Returns the location-based argument that can be passed to the `rspec` command to rerun this example.
96
+ def location_rerun_argument
97
+ @location_rerun_argument ||= begin
98
+ loaded_spec_files = RSpec.configuration.loaded_spec_files
98
99
 
99
- Metadata.ascending(metadata) do |meta|
100
- return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
100
+ Metadata.ascending(metadata) do |meta|
101
+ return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
102
+ end
101
103
  end
102
104
  end
103
105
 
106
+ # Returns the location-based argument that can be passed to the `rspec` command to rerun this example.
107
+ #
108
+ # @deprecated Use {#location_rerun_argument} instead.
109
+ # @note If there are multiple examples identified by this location, they will use {#id}
110
+ # to rerun instead, but this method will still return the location (that's why it is deprecated!).
111
+ def rerun_argument
112
+ location_rerun_argument
113
+ end
114
+
115
+ # @return [String] the unique id of this example. Pass
116
+ # this at the command line to re-run this exact example.
117
+ def id
118
+ @id ||= Metadata.id_from(metadata)
119
+ end
120
+
104
121
  # @attr_reader
105
122
  #
106
123
  # Returns the first exception raised in the context of running this
@@ -138,13 +155,24 @@ module RSpec
138
155
  @example_block = example_block
139
156
 
140
157
  @metadata = Metadata::ExampleHash.create(
141
- @example_group_class.metadata, user_metadata, description, example_block
158
+ @example_group_class.metadata, user_metadata,
159
+ example_group_class.method(:next_runnable_index_for),
160
+ description, example_block
142
161
  )
143
162
 
163
+ # This should perhaps be done in `Metadata::ExampleHash.create`,
164
+ # but the logic there has no knowledge of `RSpec.world` and we
165
+ # want to keep it that way. It's easier to just assign it here.
166
+ @metadata[:last_run_status] = RSpec.configuration.last_run_statuses[id]
167
+
144
168
  @example_group_instance = @exception = nil
145
169
  @clock = RSpec::Core::Time
170
+ @reporter = RSpec::Core::NullReporter
146
171
  end
147
172
 
173
+ # @return [RSpec::Core::Reporter] the current reporter for the example
174
+ attr_reader :reporter
175
+
148
176
  # Returns the example group class that provides the context for running
149
177
  # this example.
150
178
  def example_group
@@ -160,6 +188,7 @@ module RSpec
160
188
  # @param example_group_instance the instance of an ExampleGroup subclass
161
189
  def run(example_group_instance, reporter)
162
190
  @example_group_instance = example_group_instance
191
+ @reporter = reporter
163
192
  hooks.register_global_singleton_context_hooks(self, RSpec.configuration.hooks)
164
193
  RSpec.configuration.configure_example(self)
165
194
  RSpec.current_example = self
@@ -196,10 +225,7 @@ module RSpec
196
225
  rescue Exception => e
197
226
  set_exception(e)
198
227
  ensure
199
- ExampleGroup.each_instance_variable_for_example(@example_group_instance) do |ivar|
200
- @example_group_instance.instance_variable_set(ivar, nil)
201
- end
202
- @example_group_instance = nil
228
+ @example_group_instance = nil # if you love something... let it go
203
229
  end
204
230
 
205
231
  finish(reporter)
@@ -278,29 +304,55 @@ module RSpec
278
304
 
279
305
  # @private
280
306
  #
281
- # Used internally to set an exception in an after hook, which
282
- # captures the exception but doesn't raise it.
283
- def set_exception(exception, context=nil)
284
- if pending? && !(Pending::PendingExampleFixedError === exception)
285
- execution_result.pending_exception = exception
307
+ # The exception that will be displayed to the user -- either the failure of
308
+ # the example or the `pending_exception` if the example is pending.
309
+ def display_exception
310
+ @exception || execution_result.pending_exception
311
+ end
312
+
313
+ # @private
314
+ #
315
+ # Assigns the exception that will be displayed to the user -- either the failure of
316
+ # the example or the `pending_exception` if the example is pending.
317
+ def display_exception=(ex)
318
+ if pending? && !(Pending::PendingExampleFixedError === ex)
319
+ @exception = nil
320
+ execution_result.pending_fixed = false
321
+ execution_result.pending_exception = ex
286
322
  else
287
- if @exception
288
- # An error has already been set; we don't want to override it,
289
- # but we also don't want silence the error, so let's print it.
290
- msg = <<-EOS
323
+ @exception = ex
324
+ end
325
+ end
291
326
 
292
- An error occurred #{context}
293
- #{exception.class}: #{exception.message}
294
- occurred at #{exception.backtrace.first}
327
+ # rubocop:disable Style/AccessorMethodName
295
328
 
296
- EOS
297
- RSpec.configuration.reporter.message(msg)
298
- end
329
+ # @private
330
+ #
331
+ # Used internally to set an exception in an after hook, which
332
+ # captures the exception but doesn't raise it.
333
+ def set_exception(exception)
334
+ return self.display_exception = exception unless display_exception
299
335
 
300
- @exception ||= exception
336
+ unless RSpec::Core::MultipleExceptionError === display_exception
337
+ self.display_exception = RSpec::Core::MultipleExceptionError.new(display_exception)
301
338
  end
339
+
340
+ display_exception.add exception
341
+ end
342
+
343
+ # @private
344
+ #
345
+ # Used to set the exception when `aggregate_failures` fails.
346
+ def set_aggregate_failures_exception(exception)
347
+ return set_exception(exception) unless display_exception
348
+
349
+ exception = RSpec::Core::MultipleExceptionError::InterfaceTag.for(exception)
350
+ exception.add display_exception
351
+ self.display_exception = exception
302
352
  end
303
353
 
354
+ # rubocop:enable Style/AccessorMethodName
355
+
304
356
  # @private
305
357
  #
306
358
  # Used internally to set an exception and fail without actually executing
@@ -321,13 +373,6 @@ module RSpec
321
373
  finish(reporter)
322
374
  end
323
375
 
324
- # @private
325
- def instance_exec_with_rescue(context, &block)
326
- @example_group_instance.instance_exec(self, &block)
327
- rescue Exception => e
328
- set_exception(e, context)
329
- end
330
-
331
376
  # @private
332
377
  def instance_exec(*args, &block)
333
378
  @example_group_instance.instance_exec(*args, &block)
@@ -342,7 +387,7 @@ module RSpec
342
387
  def with_around_example_hooks
343
388
  hooks.run(:around, :example, self) { yield }
344
389
  rescue Exception => e
345
- set_exception(e, "in an `around(:example)` hook")
390
+ set_exception(e)
346
391
  end
347
392
 
348
393
  def start(reporter)
@@ -398,13 +443,7 @@ module RSpec
398
443
  def verify_mocks
399
444
  @example_group_instance.verify_mocks_for_rspec if mocks_need_verification?
400
445
  rescue Exception => e
401
- if pending?
402
- execution_result.pending_fixed = false
403
- execution_result.pending_exception = e
404
- @exception = nil
405
- else
406
- set_exception(e)
407
- end
446
+ set_exception(e)
408
447
  end
409
448
 
410
449
  def mocks_need_verification?
@@ -431,14 +470,6 @@ module RSpec
431
470
  "example at #{location}"
432
471
  end
433
472
 
434
- def skip_message
435
- if String === skip
436
- skip
437
- else
438
- Pending::NO_REASON_GIVEN
439
- end
440
- end
441
-
442
473
  # Represents the result of executing an example.
443
474
  # Behaves like a hash for backwards compatibility.
444
475
  class ExecutionResult
@@ -529,10 +560,13 @@ module RSpec
529
560
  super(AnonymousExampleGroup, "", {})
530
561
  end
531
562
 
563
+ # rubocop:disable Style/AccessorMethodName
564
+
532
565
  # To ensure we don't silence errors.
533
- def set_exception(exception, _context=nil)
566
+ def set_exception(exception)
534
567
  raise exception
535
568
  end
569
+ # rubocop:enable Style/AccessorMethodName
536
570
  end
537
571
  end
538
572
  end
@@ -142,8 +142,9 @@ module RSpec
142
142
  options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
143
143
  options.update(extra_options)
144
144
 
145
- examples << RSpec::Core::Example.new(self, desc, options, block)
146
- examples.last
145
+ example = RSpec::Core::Example.new(self, desc, options, block)
146
+ examples << example
147
+ example
147
148
  end
148
149
  end
149
150
 
@@ -230,7 +231,7 @@ module RSpec
230
231
  # @see DSL#describe
231
232
  def self.define_example_group_method(name, metadata={})
232
233
  idempotently_define_singleton_method(name) do |*args, &example_group_block|
233
- thread_data = RSpec.thread_local_metadata
234
+ thread_data = RSpec::Support.thread_local_data
234
235
  top_level = self == ExampleGroup
235
236
 
236
237
  if top_level
@@ -359,7 +360,6 @@ module RSpec
359
360
  def self.subclass(parent, description, args, &example_group_block)
360
361
  subclass = Class.new(parent)
361
362
  subclass.set_it_up(description, *args, &example_group_block)
362
- ExampleGroups.assign_const(subclass)
363
363
  subclass.module_exec(&example_group_block) if example_group_block
364
364
 
365
365
  # The LetDefinitions module must be included _after_ other modules
@@ -372,7 +372,7 @@ module RSpec
372
372
  end
373
373
 
374
374
  # @private
375
- def self.set_it_up(*args, &example_group_block)
375
+ def self.set_it_up(description, *args, &example_group_block)
376
376
  # Ruby 1.9 has a bug that can lead to infinite recursion and a
377
377
  # SystemStackError if you include a module in a superclass after
378
378
  # including it in a subclass: https://gist.github.com/845896
@@ -383,13 +383,14 @@ module RSpec
383
383
  # here.
384
384
  ensure_example_groups_are_configured
385
385
 
386
- description = args.shift
387
386
  user_metadata = Metadata.build_hash_from(args)
388
- args.unshift(description)
389
387
 
390
388
  @metadata = Metadata::ExampleGroupHash.create(
391
- superclass_metadata, user_metadata, *args, &example_group_block
389
+ superclass_metadata, user_metadata,
390
+ superclass.method(:next_runnable_index_for),
391
+ description, *args, &example_group_block
392
392
  )
393
+ ExampleGroups.assign_const(self)
393
394
 
394
395
  hooks.register_globals(self, RSpec.configuration.hooks)
395
396
  RSpec.configuration.configure_group(self)
@@ -416,6 +417,15 @@ module RSpec
416
417
  @children ||= []
417
418
  end
418
419
 
420
+ # @private
421
+ def self.next_runnable_index_for(file)
422
+ if self == ExampleGroup
423
+ RSpec.world.num_example_groups_defined_in(file)
424
+ else
425
+ children.count + examples.count
426
+ end + 1
427
+ end
428
+
419
429
  # @private
420
430
  def self.descendants
421
431
  @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants)
@@ -428,7 +438,7 @@ module RSpec
428
438
 
429
439
  # @private
430
440
  def self.top_level?
431
- @top_level ||= superclass == ExampleGroup
441
+ superclass == ExampleGroup
432
442
  end
433
443
 
434
444
  # @private
@@ -458,7 +468,7 @@ module RSpec
458
468
  def self.run_before_context_hooks(example_group_instance)
459
469
  set_ivars(example_group_instance, superclass_before_context_ivars)
460
470
 
461
- ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do
471
+ ContextHookMemoized::Before.isolate_for_context_hook(example_group_instance) do
462
472
  hooks.run(:before, :context, example_group_instance)
463
473
  end
464
474
  ensure
@@ -471,6 +481,7 @@ module RSpec
471
481
  superclass.before_context_ivars
472
482
  end
473
483
  else # 1.8.7
484
+ # :nocov:
474
485
  # @private
475
486
  def self.superclass_before_context_ivars
476
487
  if superclass.respond_to?(:before_context_ivars)
@@ -485,13 +496,14 @@ module RSpec
485
496
  ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars
486
497
  end
487
498
  end
499
+ # :nocov:
488
500
  end
489
501
 
490
502
  # @private
491
503
  def self.run_after_context_hooks(example_group_instance)
492
504
  set_ivars(example_group_instance, before_context_ivars)
493
505
 
494
- ContextHookMemoizedHash::After.isolate_for_context_hook(example_group_instance) do
506
+ ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do
495
507
  hooks.run(:after, :context, example_group_instance)
496
508
  end
497
509
  ensure
@@ -499,11 +511,8 @@ module RSpec
499
511
  end
500
512
 
501
513
  # Runs all the examples in this group.
502
- def self.run(reporter=RSpec::Core::NullReporter.new)
503
- if RSpec.world.wants_to_quit
504
- RSpec.world.clear_remaining_example_groups if top_level?
505
- return
506
- end
514
+ def self.run(reporter=RSpec::Core::NullReporter)
515
+ return if RSpec.world.wants_to_quit
507
516
  reporter.example_group_started(self)
508
517
 
509
518
  should_run_context_hooks = descendant_filtered_examples.any?
@@ -518,6 +527,7 @@ module RSpec
518
527
  rescue Exception => ex
519
528
  RSpec.world.wants_to_quit = true if fail_fast?
520
529
  for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) }
530
+ false
521
531
  ensure
522
532
  run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
523
533
  reporter.example_group_finished(self)
@@ -576,6 +586,12 @@ module RSpec
576
586
  FlatMap.flat_map(children, &:declaration_line_numbers)
577
587
  end
578
588
 
589
+ # @return [String] the unique id of this example group. Pass
590
+ # this at the command line to re-run this exact example group.
591
+ def self.id
592
+ Metadata.id_from(metadata)
593
+ end
594
+
579
595
  # @private
580
596
  def self.top_level_description
581
597
  parent_groups.last.description
@@ -587,8 +603,10 @@ module RSpec
587
603
  end
588
604
 
589
605
  if RUBY_VERSION.to_f < 1.9
606
+ # :nocov:
590
607
  # @private
591
608
  INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze
609
+ # :nocov:
592
610
  else
593
611
  # @private
594
612
  INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output
@@ -603,6 +621,7 @@ module RSpec
603
621
 
604
622
  def initialize(inspect_output=nil)
605
623
  @__inspect_output = inspect_output || '(no description provided)'
624
+ super() # no args get passed
606
625
  end
607
626
 
608
627
  # @private
@@ -611,10 +630,12 @@ module RSpec
611
630
  end
612
631
 
613
632
  unless method_defined?(:singleton_class) # for 1.8.7
633
+ # :nocov:
614
634
  # @private
615
635
  def singleton_class
616
636
  class << self; self; end
617
637
  end
638
+ # :nocov:
618
639
  end
619
640
 
620
641
  # Raised when an RSpec API is called in the wrong scope, such as `before`
@@ -689,17 +710,22 @@ module RSpec
689
710
 
690
711
  # @private
691
712
  def self.current_backtrace
692
- RSpec.thread_local_metadata[:shared_example_group_inclusions].reverse
713
+ shared_example_group_inclusions.reverse
693
714
  end
694
715
 
695
716
  # @private
696
717
  def self.with_frame(name, location)
697
- current_stack = RSpec.thread_local_metadata[:shared_example_group_inclusions]
718
+ current_stack = shared_example_group_inclusions
698
719
  current_stack << new(name, location)
699
720
  yield
700
721
  ensure
701
722
  current_stack.pop
702
723
  end
724
+
725
+ # @private
726
+ def self.shared_example_group_inclusions
727
+ RSpec::Support.thread_local_data[:shared_example_group_inclusions] ||= []
728
+ end
703
729
  end
704
730
  end
705
731
 
@@ -746,6 +772,7 @@ module RSpec
746
772
  end
747
773
 
748
774
  if RUBY_VERSION == '1.9.2'
775
+ # :nocov:
749
776
  class << self
750
777
  alias _base_name_for base_name_for
751
778
  def base_name_for(group)
@@ -753,6 +780,7 @@ module RSpec
753
780
  end
754
781
  end
755
782
  private_class_method :_base_name_for
783
+ # :nocov:
756
784
  end
757
785
 
758
786
  def self.disambiguate(name, const_scope)