rspec-core 3.2.3 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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)