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
@@ -4,23 +4,36 @@ require 'optparse'
4
4
  module RSpec::Core
5
5
  # @private
6
6
  class Parser
7
- def self.parse(args)
8
- new.parse(args)
7
+ def self.parse(args, source=nil)
8
+ new(args).parse(source)
9
9
  end
10
10
 
11
- def parse(args)
12
- return {} if args.empty?
11
+ attr_reader :original_args
12
+
13
+ def initialize(original_args)
14
+ @original_args = original_args
15
+ end
16
+
17
+ def parse(source=nil)
18
+ return { :files_or_directories_to_run => [] } if original_args.empty?
19
+ args = original_args.dup
13
20
 
14
21
  options = args.delete('--tty') ? { :tty => true } : {}
15
22
  begin
16
23
  parser(options).parse!(args)
17
24
  rescue OptionParser::InvalidOption => e
18
- abort "#{e.message}\n\nPlease use --help for a listing of valid options"
25
+ failure = e.message
26
+ failure << " (defined in #{source})" if source
27
+ abort "#{failure}\n\nPlease use --help for a listing of valid options"
19
28
  end
20
29
 
30
+ options[:files_or_directories_to_run] = args
21
31
  options
22
32
  end
23
33
 
34
+ private
35
+
36
+ # rubocop:disable MethodLength
24
37
  def parser(options)
25
38
  OptionParser.new do |parser|
26
39
  parser.banner = "Usage: rspec [options] [files or directories]\n\n"
@@ -51,12 +64,13 @@ module RSpec::Core
51
64
  options[:order] = "rand:#{seed}"
52
65
  end
53
66
 
54
- parser.on('--fail-fast', 'Abort the run on first failure.') do |_o|
55
- options[:fail_fast] = true
67
+ parser.on('--bisect[=verbose]', 'Repeatedly runs the suite in order to isolate the failures to the ',
68
+ ' smallest reproducible case.') do |argument|
69
+ bisect_and_exit(argument)
56
70
  end
57
71
 
58
- parser.on('--no-fail-fast', 'Do not abort the run on first failure.') do |_o|
59
- options[:fail_fast] = false
72
+ parser.on('--[no-]fail-fast', 'Abort the run on first failure.') do |value|
73
+ set_fail_fast(options, value)
60
74
  end
61
75
 
62
76
  parser.on('--failure-exit-code CODE', Integer,
@@ -78,9 +92,7 @@ module RSpec::Core
78
92
  end
79
93
 
80
94
  parser.on('--init', 'Initialize your project with RSpec.') do |_cmd|
81
- RSpec::Support.require_rspec_core "project_initializer"
82
- ProjectInitializer.new.run
83
- exit
95
+ initialize_project_and_exit
84
96
  end
85
97
 
86
98
  parser.separator("\n **** Output ****\n\n")
@@ -143,14 +155,29 @@ module RSpec::Core
143
155
 
144
156
  **** Filtering/tags ****
145
157
 
146
- In addition to the following options for selecting specific files, groups,
147
- or examples, you can select a single example by appending the line number to
158
+ In addition to the following options for selecting specific files, groups, or
159
+ examples, you can select individual examples by appending the line number(s) to
148
160
  the filename:
149
161
 
150
- rspec path/to/a_spec.rb:37
162
+ rspec path/to/a_spec.rb:37:87
163
+
164
+ You can also pass example ids enclosed in square brackets:
165
+
166
+ rspec path/to/a_spec.rb[1:5,1:6] # run the 5th and 6th examples/groups defined in the 1st group
151
167
 
152
168
  FILTERING
153
169
 
170
+ parser.on('--only-failures', "Filter to just the examples that failed the last time they ran.") do
171
+ configure_only_failures(options)
172
+ end
173
+
174
+ parser.on("--next-failure", "Apply `--only-failures` and abort after one failure.",
175
+ " (Equivalent to `--only-failures --fail-fast --order defined`)") do
176
+ configure_only_failures(options)
177
+ set_fail_fast(options, true)
178
+ options[:order] ||= 'defined'
179
+ end
180
+
154
181
  parser.on('-P', '--pattern PATTERN', 'Load files matching pattern (default: "spec/**/*_spec.rb").') do |o|
155
182
  options[:pattern] = o
156
183
  end
@@ -175,18 +202,19 @@ FILTERING
175
202
  name, value = tag.gsub(/^(~@|~|@)/, '').split(':', 2)
176
203
  name = name.to_sym
177
204
 
178
- options[filter_type] ||= {}
179
- options[filter_type][name] = case value
180
- when nil then true # The default value for tags is true
181
- when 'true' then true
182
- when 'false' then false
183
- when 'nil' then nil
184
- when /^:/ then value[1..-1].to_sym
185
- when /^\d+$/ then Integer(value)
186
- when /^\d+.\d+$/ then Float(value)
187
- else
188
- value
189
- end
205
+ parsed_value = case value
206
+ when nil then true # The default value for tags is true
207
+ when 'true' then true
208
+ when 'false' then false
209
+ when 'nil' then nil
210
+ when /^:/ then value[1..-1].to_sym
211
+ when /^\d+$/ then Integer(value)
212
+ when /^\d+.\d+$/ then Float(value)
213
+ else
214
+ value
215
+ end
216
+
217
+ add_tag_filter(options, filter_type, name, parsed_value)
190
218
  end
191
219
 
192
220
  parser.on('--default-path PATH', 'Set the default path where RSpec looks for examples (can',
@@ -197,8 +225,7 @@ FILTERING
197
225
  parser.separator("\n **** Utility ****\n\n")
198
226
 
199
227
  parser.on('-v', '--version', 'Display the version.') do
200
- puts RSpec::Core::Version::STRING
201
- exit
228
+ print_version_and_exit
202
229
  end
203
230
 
204
231
  # These options would otherwise be confusing to users, so we forcibly
@@ -210,9 +237,7 @@ FILTERING
210
237
  invalid_options = %w[-d --I]
211
238
 
212
239
  parser.on_tail('-h', '--help', "You're looking at it.") do
213
- # Removing the blank invalid options from the output.
214
- puts parser.to_s.gsub(/^\s+(#{invalid_options.join('|')})\s*$\n/, '')
215
- exit
240
+ print_help_and_exit(parser, invalid_options)
216
241
  end
217
242
 
218
243
  # This prevents usage of the invalid_options.
@@ -224,5 +249,53 @@ FILTERING
224
249
 
225
250
  end
226
251
  end
252
+ # rubocop:enable MethodLength
253
+
254
+ def add_tag_filter(options, filter_type, tag_name, value=true)
255
+ (options[filter_type] ||= {})[tag_name] = value
256
+ end
257
+
258
+ def set_fail_fast(options, value)
259
+ options[:fail_fast] = value
260
+ end
261
+
262
+ def configure_only_failures(options)
263
+ options[:only_failures] = true
264
+ add_tag_filter(options, :inclusion_filter, :last_run_status, 'failed')
265
+ end
266
+
267
+ def initialize_project_and_exit
268
+ RSpec::Support.require_rspec_core "project_initializer"
269
+ ProjectInitializer.new.run
270
+ exit
271
+ end
272
+
273
+ def bisect_and_exit(argument)
274
+ RSpec::Support.require_rspec_core "bisect/coordinator"
275
+
276
+ success = Bisect::Coordinator.bisect_with(
277
+ original_args,
278
+ RSpec.configuration,
279
+ bisect_formatter_for(argument)
280
+ )
281
+
282
+ exit(success ? 0 : 1)
283
+ end
284
+
285
+ def bisect_formatter_for(argument)
286
+ return Formatters::BisectDebugFormatter if argument == "verbose"
287
+ Formatters::BisectProgressFormatter
288
+ end
289
+
290
+ def print_version_and_exit
291
+ puts RSpec::Core::Version::STRING
292
+ exit
293
+ end
294
+
295
+ def print_help_and_exit(parser, invalid_options)
296
+ # Removing the blank invalid options from the output.
297
+ puts parser.to_s.gsub(/^\s+(#{invalid_options.join('|')})\s*$\n/, '')
298
+ exit
299
+ end
227
300
  end
228
301
  end
@@ -1,14 +1,5 @@
1
1
  module RSpec
2
2
  module Core
3
- if defined?(::Random)
4
- # @private
5
- RandomNumberGenerator = ::Random
6
- else
7
- RSpec::Support.require_rspec_core "backport_random"
8
- # @private
9
- RandomNumberGenerator = RSpec::Core::Backports::Random
10
- end
11
-
12
3
  # @private
13
4
  module Ordering
14
5
  # @private
@@ -33,26 +24,38 @@ module RSpec
33
24
 
34
25
  def order(items)
35
26
  @used = true
36
- rng = RandomNumberGenerator.new(@configuration.seed)
37
- shuffle items, rng
27
+
28
+ seed = @configuration.seed.to_s
29
+ items.sort_by { |item| jenkins_hash_digest(seed + item.id) }
38
30
  end
39
31
 
40
- if RUBY_VERSION > '1.9.3'
41
- def shuffle(list, rng)
42
- list.shuffle(:random => rng)
43
- end
44
- else
45
- def shuffle(list, rng)
46
- shuffled = list.dup
47
- shuffled.size.times do |i|
48
- j = i + rng.rand(shuffled.size - i)
49
- next if i == j
50
- shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
51
- end
52
-
53
- shuffled
32
+ private
33
+
34
+ # http://en.wikipedia.org/wiki/Jenkins_hash_function
35
+ # Jenkins provides a good distribution and is simpler than MD5.
36
+ # It's a bit slower than MD5 (primarily because `Digest::MD5` is
37
+ # implemented in C) but has the advantage of not requiring us
38
+ # to load another part of stdlib, which we try to minimize.
39
+ def jenkins_hash_digest(string)
40
+ hash = 0
41
+
42
+ string.each_byte do |byte|
43
+ hash += byte
44
+ hash &= MAX_32_BIT
45
+ hash += ((hash << 10) & MAX_32_BIT)
46
+ hash &= MAX_32_BIT
47
+ hash ^= hash >> 6
54
48
  end
49
+
50
+ hash += ((hash << 3) & MAX_32_BIT)
51
+ hash &= MAX_32_BIT
52
+ hash ^= hash >> 11
53
+ hash += ((hash << 15) & MAX_32_BIT)
54
+ hash &= MAX_32_BIT
55
+ hash
55
56
  end
57
+
58
+ MAX_32_BIT = 4_294_967_295
56
59
  end
57
60
 
58
61
  # @private
@@ -0,0 +1,32 @@
1
+ module RSpec
2
+ module Core
3
+ # @private
4
+ class Profiler
5
+ NOTIFICATIONS = [:example_group_started, :example_group_finished, :example_started]
6
+
7
+ def initialize
8
+ @example_groups = Hash.new { |h, k| h[k] = { :count => 0 } }
9
+ end
10
+
11
+ attr_reader :example_groups
12
+
13
+ def example_group_started(notification)
14
+ return unless notification.group.top_level?
15
+
16
+ @example_groups[notification.group][:start] = Time.now
17
+ @example_groups[notification.group][:description] = notification.group.top_level_description
18
+ end
19
+
20
+ def example_group_finished(notification)
21
+ return unless notification.group.top_level?
22
+
23
+ @example_groups[notification.group][:total_time] = Time.now - @example_groups[notification.group][:start]
24
+ end
25
+
26
+ def example_started(notification)
27
+ group = notification.example.example_group.parent_groups.last
28
+ @example_groups[group][:count] += 1
29
+ end
30
+ end
31
+ end
32
+ end
@@ -50,10 +50,15 @@ RSpec.configure do |config|
50
50
  config.filter_run :focus
51
51
  config.run_all_when_everything_filtered = true
52
52
 
53
+ # Allows RSpec to persist some state between runs in order to support
54
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
55
+ # you configure your source control system to ignore this file.
56
+ config.example_status_persistence_file_path = "spec/examples.txt"
57
+
53
58
  # Limits the available syntax to the non-monkey patched syntax that is
54
59
  # recommended. For more details, see:
55
60
  # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
56
- # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
61
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
57
62
  # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
58
63
  config.disable_monkey_patching!
59
64
 
@@ -1,6 +1,7 @@
1
1
  require 'rake'
2
2
  require 'rake/tasklib'
3
3
  require 'rspec/support/ruby_features'
4
+ require 'rspec/core/shell_escape'
4
5
 
5
6
  module RSpec
6
7
  module Core
@@ -9,6 +10,7 @@ module RSpec
9
10
  # @see Rakefile
10
11
  class RakeTask < ::Rake::TaskLib
11
12
  include ::Rake::DSL if defined?(::Rake::DSL)
13
+ include RSpec::Core::ShellEscape
12
14
 
13
15
  # Default path to the RSpec executable.
14
16
  DEFAULT_RSPEC_PATH = File.expand_path('../../../../exe/rspec', __FILE__)
@@ -63,16 +65,12 @@ module RSpec
63
65
  # @private
64
66
  def run_task(verbose)
65
67
  command = spec_command
68
+ puts command if verbose
66
69
 
67
- begin
68
- puts command if verbose
69
- success = system(command)
70
- rescue
71
- puts failure_message if failure_message
72
- end
73
-
74
- return unless fail_on_error && !success
70
+ return if system(command)
71
+ puts failure_message if failure_message
75
72
 
73
+ return unless fail_on_error
76
74
  $stderr.puts "#{command} failed" if verbose
77
75
  exit $?.exitstatus
78
76
  end
@@ -126,18 +124,6 @@ module RSpec
126
124
  end
127
125
  end
128
126
 
129
- if RSpec::Support::OS.windows?
130
- def escape(shell_command)
131
- "'#{shell_command.gsub("'", "\'")}'"
132
- end
133
- else
134
- require 'shellwords'
135
-
136
- def escape(shell_command)
137
- shell_command.shellescape
138
- end
139
- end
140
-
141
127
  def file_exclusion_specification
142
128
  " --exclude-pattern #{escape exclude_pattern}" if exclude_pattern
143
129
  end
@@ -0,0 +1,52 @@
1
+ module RSpec
2
+ module Core
3
+ # Allows a thread to lock out other threads from a critical section of code,
4
+ # while allowing the thread with the lock to reenter that section.
5
+ #
6
+ # Based on Monitor as of 2.2 - https://github.com/ruby/ruby/blob/eb7ddaa3a47bf48045d26c72eb0f263a53524ebc/lib/monitor.rb#L9
7
+ #
8
+ # Depends on Mutex, but Mutex is only available as part of core since 1.9.1:
9
+ # exists - http://ruby-doc.org/core-1.9.1/Mutex.html
10
+ # dne - http://ruby-doc.org/core-1.9.0/Mutex.html
11
+ #
12
+ # @private
13
+ class ReentrantMutex
14
+ def initialize
15
+ @owner = nil
16
+ @count = 0
17
+ @mutex = Mutex.new
18
+ end
19
+
20
+ def synchronize
21
+ enter
22
+ yield
23
+ ensure
24
+ exit
25
+ end
26
+
27
+ private
28
+
29
+ def enter
30
+ @mutex.lock if @owner != Thread.current
31
+ @owner = Thread.current
32
+ @count += 1
33
+ end
34
+
35
+ def exit
36
+ @count -= 1
37
+ return unless @count == 0
38
+ @owner = nil
39
+ @mutex.unlock
40
+ end
41
+ end
42
+
43
+ if defined? ::Mutex
44
+ # On 1.9 and up, this is in core, so we just use the real one
45
+ Mutex = ::Mutex
46
+ else # For 1.8.7
47
+ # :nocov:
48
+ RSpec::Support.require_rspec_core "mutex"
49
+ # :nocov:
50
+ end
51
+ end
52
+ end
@@ -2,6 +2,15 @@ module RSpec::Core
2
2
  # A reporter will send notifications to listeners, usually formatters for the
3
3
  # spec suite run.
4
4
  class Reporter
5
+ # @private
6
+ RSPEC_NOTIFICATIONS = Set.new(
7
+ [
8
+ :close, :deprecation, :deprecation_summary, :dump_failures, :dump_pending,
9
+ :dump_profile, :dump_summary, :example_failed, :example_group_finished,
10
+ :example_group_started, :example_passed, :example_pending, :example_started,
11
+ :message, :seed, :start, :start_dump, :stop
12
+ ])
13
+
5
14
  def initialize(configuration)
6
15
  @configuration = configuration
7
16
  @listeners = Hash.new { |h, k| h[k] = Set.new }
@@ -19,6 +28,13 @@ module RSpec::Core
19
28
  @examples = []
20
29
  @failed_examples = []
21
30
  @pending_examples = []
31
+ @profiler = Profiler.new if defined?(@profiler)
32
+ end
33
+
34
+ # @private
35
+ def setup_profiler
36
+ @profiler = Profiler.new
37
+ register_listener @profiler, *Profiler::NOTIFICATIONS
22
38
  end
23
39
 
24
40
  # Registers a listener to a list of notifications. The reporter will send
@@ -40,7 +56,6 @@ module RSpec::Core
40
56
  @listeners[notification].to_a
41
57
  end
42
58
 
43
- # @api
44
59
  # @overload report(count, &block)
45
60
  # @overload report(count, &block)
46
61
  # @param expected_example_count [Integer] the number of examples being run
@@ -73,11 +88,26 @@ module RSpec::Core
73
88
  notify :start, Notifications::StartNotification.new(expected_example_count, @load_time)
74
89
  end
75
90
 
76
- # @private
91
+ # @param message [#to_s] A message object to send to formatters
92
+ #
93
+ # Send a custom message to supporting formatters.
77
94
  def message(message)
78
95
  notify :message, Notifications::MessageNotification.new(message)
79
96
  end
80
97
 
98
+ # @param event [Symbol] Name of the custom event to trigger on formatters
99
+ # @param options [Hash] Hash of arguments to provide via `CustomNotification`
100
+ #
101
+ # Publish a custom event to supporting registered formatters.
102
+ # @see RSpec::Core::Notifications::CustomNotification
103
+ def publish(event, options={})
104
+ if RSPEC_NOTIFICATIONS.include? event
105
+ raise "RSpec::Core::Reporter#publish is intended for sending custom " \
106
+ "events not internal RSpec ones, please rename your custom event."
107
+ end
108
+ notify event, Notifications::CustomNotification.for(options)
109
+ end
110
+
81
111
  # @private
82
112
  def example_group_started(group)
83
113
  notify :example_group_started, Notifications::GroupNotification.new(group) unless group.descendant_filtered_examples.empty?
@@ -118,20 +148,28 @@ module RSpec::Core
118
148
 
119
149
  # @private
120
150
  def finish
121
- stop
122
- notify :start_dump, Notifications::NullNotification
123
- notify :dump_pending, Notifications::ExamplesNotification.new(self)
124
- notify :dump_failures, Notifications::ExamplesNotification.new(self)
125
- notify :deprecation_summary, Notifications::NullNotification
126
- unless mute_profile_output?
127
- notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples,
128
- @configuration.profile_examples)
151
+ close_after do
152
+ stop
153
+ notify :start_dump, Notifications::NullNotification
154
+ notify :dump_pending, Notifications::ExamplesNotification.new(self)
155
+ notify :dump_failures, Notifications::ExamplesNotification.new(self)
156
+ notify :deprecation_summary, Notifications::NullNotification
157
+ unless mute_profile_output?
158
+ notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples,
159
+ @configuration.profile_examples,
160
+ @profiler.example_groups)
161
+ end
162
+ notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples,
163
+ @pending_examples, @load_time)
164
+ notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?)
129
165
  end
130
- notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples,
131
- @pending_examples, @load_time)
132
- notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?)
166
+ end
167
+
168
+ # @private
169
+ def close_after
170
+ yield
133
171
  ensure
134
- notify :close, Notifications::NullNotification
172
+ close
135
173
  end
136
174
 
137
175
  # @private
@@ -147,8 +185,19 @@ module RSpec::Core
147
185
  end
148
186
  end
149
187
 
188
+ # @private
189
+ def abort_with(msg, exit_status)
190
+ message(msg)
191
+ close
192
+ exit!(exit_status)
193
+ end
194
+
150
195
  private
151
196
 
197
+ def close
198
+ notify :close, Notifications::NullNotification
199
+ end
200
+
152
201
  def mute_profile_output?
153
202
  # Don't print out profiled info if there are failures and `--fail-fast` is
154
203
  # used, it just clutters the output.
@@ -163,10 +212,9 @@ module RSpec::Core
163
212
  # @private
164
213
  # # Used in place of a {Reporter} for situations where we don't want reporting output.
165
214
  class NullReporter
166
- private
167
-
168
- def method_missing(*)
215
+ def self.method_missing(*)
169
216
  # ignore
170
217
  end
218
+ private_class_method :method_missing
171
219
  end
172
220
  end