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,70 @@
1
+ RSpec::Support.require_rspec_core "formatters/base_text_formatter"
2
+ RSpec::Support.require_rspec_core "formatters/console_codes"
3
+
4
+ module RSpec
5
+ module Core
6
+ module Formatters
7
+ # @private
8
+ class DocumentationFormatter < BaseTextFormatter
9
+ Formatters.register self, :example_group_started, :example_group_finished,
10
+ :example_passed, :example_pending, :example_failed
11
+
12
+ def initialize(output)
13
+ super
14
+ @group_level = 0
15
+ end
16
+
17
+ def example_group_started(notification)
18
+ output.puts if @group_level == 0
19
+ output.puts "#{current_indentation}#{notification.group.description.strip}"
20
+
21
+ @group_level += 1
22
+ end
23
+
24
+ def example_group_finished(_notification)
25
+ @group_level -= 1 if @group_level > 0
26
+ end
27
+
28
+ def example_passed(passed)
29
+ output.puts passed_output(passed.example)
30
+ end
31
+
32
+ def example_pending(pending)
33
+ output.puts pending_output(pending.example,
34
+ pending.example.execution_result.pending_message)
35
+ end
36
+
37
+ def example_failed(failure)
38
+ output.puts failure_output(failure.example)
39
+ end
40
+
41
+ private
42
+
43
+ def passed_output(example)
44
+ ConsoleCodes.wrap("#{current_indentation}#{example.description.strip}", :success)
45
+ end
46
+
47
+ def pending_output(example, message)
48
+ ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \
49
+ "(PENDING: #{message})",
50
+ :pending)
51
+ end
52
+
53
+ def failure_output(example)
54
+ ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \
55
+ "(FAILED - #{next_failure_index})",
56
+ :failure)
57
+ end
58
+
59
+ def next_failure_index
60
+ @next_failure_index ||= 0
61
+ @next_failure_index += 1
62
+ end
63
+
64
+ def current_indentation
65
+ ' ' * @group_level
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,508 @@
1
+ # encoding: utf-8
2
+ RSpec::Support.require_rspec_core "formatters/console_codes"
3
+ RSpec::Support.require_rspec_core "formatters/snippet_extractor"
4
+ RSpec::Support.require_rspec_core 'formatters/syntax_highlighter'
5
+ RSpec::Support.require_rspec_support "encoded_string"
6
+
7
+ module RSpec
8
+ module Core
9
+ module Formatters
10
+ # @private
11
+ class ExceptionPresenter
12
+ attr_reader :exception, :example, :description, :message_color,
13
+ :detail_formatter, :extra_detail_formatter, :backtrace_formatter
14
+ private :message_color, :detail_formatter, :extra_detail_formatter, :backtrace_formatter
15
+
16
+ def initialize(exception, example, options={})
17
+ @exception = exception
18
+ @example = example
19
+ @message_color = options.fetch(:message_color) { RSpec.configuration.failure_color }
20
+ @description = options.fetch(:description) { example.full_description }
21
+ @detail_formatter = options.fetch(:detail_formatter) { Proc.new {} }
22
+ @extra_detail_formatter = options.fetch(:extra_detail_formatter) { Proc.new {} }
23
+ @backtrace_formatter = options.fetch(:backtrace_formatter) { RSpec.configuration.backtrace_formatter }
24
+ @indentation = options.fetch(:indentation, 2)
25
+ @skip_shared_group_trace = options.fetch(:skip_shared_group_trace, false)
26
+ @failure_lines = options[:failure_lines]
27
+ end
28
+
29
+ def message_lines
30
+ add_shared_group_lines(failure_lines, Notifications::NullColorizer)
31
+ end
32
+
33
+ def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
34
+ add_shared_group_lines(failure_lines, colorizer).map do |line|
35
+ colorizer.wrap line, message_color
36
+ end
37
+ end
38
+
39
+ def formatted_backtrace(exception=@exception)
40
+ backtrace_formatter.format_backtrace(exception.backtrace, example.metadata) +
41
+ formatted_cause(exception)
42
+ end
43
+
44
+ if RSpec::Support::RubyFeatures.supports_exception_cause?
45
+ def formatted_cause(exception)
46
+ last_cause = final_exception(exception)
47
+ cause = []
48
+
49
+ if exception.cause
50
+ cause << '------------------'
51
+ cause << '--- Caused by: ---'
52
+ cause << "#{exception_class_name(last_cause)}:" unless exception_class_name(last_cause) =~ /RSpec/
53
+
54
+ encoded_string(last_cause.message.to_s).split("\n").each do |line|
55
+ cause << " #{line}"
56
+ end
57
+
58
+ cause << (" #{backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata).first}")
59
+ end
60
+
61
+ cause
62
+ end
63
+ else
64
+ # :nocov:
65
+ def formatted_cause(_)
66
+ []
67
+ end
68
+ # :nocov:
69
+ end
70
+
71
+ def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
72
+ formatted_backtrace.map do |backtrace_info|
73
+ colorizer.wrap "# #{backtrace_info}", RSpec.configuration.detail_color
74
+ end
75
+ end
76
+
77
+ def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes)
78
+ lines = fully_formatted_lines(failure_number, colorizer)
79
+ lines.join("\n") << "\n"
80
+ end
81
+
82
+ def fully_formatted_lines(failure_number, colorizer)
83
+ lines = [
84
+ encoded_description(description),
85
+ detail_formatter.call(example, colorizer),
86
+ formatted_message_and_backtrace(colorizer),
87
+ extra_detail_formatter.call(failure_number, colorizer),
88
+ ].compact.flatten
89
+
90
+ lines = indent_lines(lines, failure_number)
91
+ lines.unshift("")
92
+ lines
93
+ end
94
+
95
+ private
96
+
97
+ def final_exception(exception, previous=[])
98
+ cause = exception.cause
99
+ if cause && !previous.include?(cause)
100
+ previous << cause
101
+ final_exception(cause, previous)
102
+ else
103
+ exception
104
+ end
105
+ end
106
+
107
+ if String.method_defined?(:encoding)
108
+ def encoding_of(string)
109
+ string.encoding
110
+ end
111
+
112
+ def encoded_string(string)
113
+ RSpec::Support::EncodedString.new(string, Encoding.default_external)
114
+ end
115
+ else # for 1.8.7
116
+ # :nocov:
117
+ def encoding_of(_string)
118
+ end
119
+
120
+ def encoded_string(string)
121
+ RSpec::Support::EncodedString.new(string)
122
+ end
123
+ # :nocov:
124
+ end
125
+
126
+ def indent_lines(lines, failure_number)
127
+ alignment_basis = ' ' * @indentation
128
+ alignment_basis << "#{failure_number}) " if failure_number
129
+ indentation = ' ' * alignment_basis.length
130
+
131
+ lines.each_with_index.map do |line, index|
132
+ if index == 0
133
+ "#{alignment_basis}#{line}"
134
+ elsif line.empty?
135
+ line
136
+ else
137
+ "#{indentation}#{line}"
138
+ end
139
+ end
140
+ end
141
+
142
+ def exception_class_name(exception=@exception)
143
+ name = exception.class.name.to_s
144
+ name = "(anonymous error class)" if name == ''
145
+ name
146
+ end
147
+
148
+ def failure_lines
149
+ @failure_lines ||= [].tap do |lines|
150
+ lines.concat(failure_slash_error_lines)
151
+
152
+ sections = [failure_slash_error_lines, exception_lines]
153
+ if sections.any? { |section| section.size > 1 } && !exception_lines.first.empty?
154
+ lines << ''
155
+ end
156
+
157
+ lines.concat(exception_lines)
158
+ lines.concat(extra_failure_lines)
159
+ end
160
+ end
161
+
162
+ def failure_slash_error_lines
163
+ lines = read_failed_lines
164
+ if lines.count == 1
165
+ lines[0] = "Failure/Error: #{lines[0].strip}"
166
+ else
167
+ least_indentation = SnippetExtractor.least_indentation_from(lines)
168
+ lines = lines.map { |line| line.sub(/^#{least_indentation}/, ' ') }
169
+ lines.unshift('Failure/Error:')
170
+ end
171
+ lines
172
+ end
173
+
174
+ def exception_lines
175
+ lines = []
176
+ lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/
177
+ encoded_string(exception.message.to_s).split("\n").each do |line|
178
+ lines << (line.empty? ? line : " #{line}")
179
+ end
180
+ lines
181
+ end
182
+
183
+ def extra_failure_lines
184
+ @extra_failure_lines ||= begin
185
+ lines = Array(example.metadata[:extra_failure_lines])
186
+ unless lines.empty?
187
+ lines.unshift('')
188
+ lines.push('')
189
+ end
190
+ lines
191
+ end
192
+ end
193
+
194
+ def add_shared_group_lines(lines, colorizer)
195
+ return lines if @skip_shared_group_trace
196
+
197
+ example.metadata[:shared_group_inclusion_backtrace].each do |frame|
198
+ lines << colorizer.wrap(frame.description, RSpec.configuration.default_color)
199
+ end
200
+
201
+ lines
202
+ end
203
+
204
+ def read_failed_lines
205
+ matching_line = find_failed_line
206
+ unless matching_line
207
+ return ["Unable to find matching line from backtrace"]
208
+ end
209
+
210
+ file_and_line_number = matching_line.match(/(.+?):(\d+)(|:\d+)/)
211
+
212
+ unless file_and_line_number
213
+ return ["Unable to infer file and line number from backtrace"]
214
+ end
215
+
216
+ file_path, line_number = file_and_line_number[1..2]
217
+ max_line_count = RSpec.configuration.max_displayed_failure_line_count
218
+ lines = SnippetExtractor.extract_expression_lines_at(file_path, line_number.to_i, max_line_count)
219
+ RSpec.world.syntax_highlighter.highlight(lines)
220
+ rescue SnippetExtractor::NoSuchFileError
221
+ ["Unable to find #{file_path} to read failed line"]
222
+ rescue SnippetExtractor::NoSuchLineError
223
+ ["Unable to find matching line in #{file_path}"]
224
+ rescue SecurityError
225
+ ["Unable to read failed line"]
226
+ end
227
+
228
+ def find_failed_line
229
+ line_regex = RSpec.configuration.in_project_source_dir_regex
230
+ loaded_spec_files = RSpec.configuration.loaded_spec_files
231
+
232
+ exception_backtrace.find do |line|
233
+ next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
234
+ path = File.expand_path(line_path)
235
+ loaded_spec_files.include?(path) || path =~ line_regex
236
+ end || exception_backtrace.first
237
+ end
238
+
239
+ def formatted_message_and_backtrace(colorizer)
240
+ lines = colorized_message_lines(colorizer) + colorized_formatted_backtrace(colorizer)
241
+ encoding = encoding_of("")
242
+ lines.map do |line|
243
+ RSpec::Support::EncodedString.new(line, encoding)
244
+ end
245
+ end
246
+
247
+ if String.method_defined?(:encoding)
248
+ def encoded_description(description)
249
+ return if description.nil?
250
+ encoded_string(description)
251
+ end
252
+ else # for 1.8.7
253
+ def encoded_description(description)
254
+ description
255
+ end
256
+ end
257
+
258
+ def exception_backtrace
259
+ exception.backtrace || []
260
+ end
261
+
262
+ # @private
263
+ # Configuring the `ExceptionPresenter` with the right set of options to handle
264
+ # pending vs failed vs skipped and aggregated (or not) failures is not simple.
265
+ # This class takes care of building an appropriate `ExceptionPresenter` for the
266
+ # provided example.
267
+ class Factory
268
+ def build
269
+ ExceptionPresenter.new(@exception, @example, options)
270
+ end
271
+
272
+ private
273
+
274
+ def initialize(example)
275
+ @example = example
276
+ @execution_result = example.execution_result
277
+ @exception = if @execution_result.status == :pending
278
+ @execution_result.pending_exception
279
+ else
280
+ @execution_result.exception
281
+ end
282
+ end
283
+
284
+ def options
285
+ with_multiple_error_options_as_needed(@exception, pending_options || {})
286
+ end
287
+
288
+ def pending_options
289
+ if @execution_result.pending_fixed?
290
+ {
291
+ :description => "#{@example.full_description} FIXED",
292
+ :message_color => RSpec.configuration.fixed_color,
293
+ :failure_lines => [
294
+ "Expected pending '#{@execution_result.pending_message}' to fail. No error was raised."
295
+ ]
296
+ }
297
+ elsif @execution_result.status == :pending
298
+ {
299
+ :message_color => RSpec.configuration.pending_color,
300
+ :detail_formatter => PENDING_DETAIL_FORMATTER
301
+ }
302
+ end
303
+ end
304
+
305
+ def with_multiple_error_options_as_needed(exception, options)
306
+ return options unless multiple_exceptions_error?(exception)
307
+
308
+ options = options.merge(
309
+ :failure_lines => [],
310
+ :extra_detail_formatter => sub_failure_list_formatter(exception, options[:message_color]),
311
+ :detail_formatter => multiple_exception_summarizer(exception,
312
+ options[:detail_formatter],
313
+ options[:message_color])
314
+ )
315
+
316
+ return options unless exception.aggregation_metadata[:hide_backtrace]
317
+ options[:backtrace_formatter] = EmptyBacktraceFormatter
318
+ options
319
+ end
320
+
321
+ def multiple_exceptions_error?(exception)
322
+ MultipleExceptionError::InterfaceTag === exception
323
+ end
324
+
325
+ def multiple_exception_summarizer(exception, prior_detail_formatter, color)
326
+ lambda do |example, colorizer|
327
+ summary = if exception.aggregation_metadata[:hide_backtrace]
328
+ # Since the backtrace is hidden, the subfailures will come
329
+ # immediately after this, and using `:` will read well.
330
+ "Got #{exception.exception_count_description}:"
331
+ else
332
+ # The backtrace comes after this, so using a `:` doesn't make sense
333
+ # since the failures may be many lines below.
334
+ "#{exception.summary}."
335
+ end
336
+
337
+ summary = colorizer.wrap(summary, color || RSpec.configuration.failure_color)
338
+ return summary unless prior_detail_formatter
339
+ [
340
+ prior_detail_formatter.call(example, colorizer),
341
+ summary
342
+ ]
343
+ end
344
+ end
345
+
346
+ def sub_failure_list_formatter(exception, message_color)
347
+ common_backtrace_truncater = CommonBacktraceTruncater.new(exception)
348
+
349
+ lambda do |failure_number, colorizer|
350
+ FlatMap.flat_map(exception.all_exceptions.each_with_index) do |failure, index|
351
+ options = with_multiple_error_options_as_needed(
352
+ failure,
353
+ :description => nil,
354
+ :indentation => 0,
355
+ :message_color => message_color || RSpec.configuration.failure_color,
356
+ :skip_shared_group_trace => true
357
+ )
358
+
359
+ failure = common_backtrace_truncater.with_truncated_backtrace(failure)
360
+ presenter = ExceptionPresenter.new(failure, @example, options)
361
+ presenter.fully_formatted_lines(
362
+ "#{failure_number ? "#{failure_number}." : ''}#{index + 1}",
363
+ colorizer
364
+ )
365
+ end
366
+ end
367
+ end
368
+
369
+ # @private
370
+ # Used to prevent a confusing backtrace from showing up from the `aggregate_failures`
371
+ # block declared for `:aggregate_failures` metadata.
372
+ module EmptyBacktraceFormatter
373
+ def self.format_backtrace(*)
374
+ []
375
+ end
376
+ end
377
+
378
+ # @private
379
+ class CommonBacktraceTruncater
380
+ def initialize(parent)
381
+ @parent = parent
382
+ end
383
+
384
+ def with_truncated_backtrace(child)
385
+ child_bt = child.backtrace
386
+ parent_bt = @parent.backtrace
387
+ return child if child_bt.nil? || child_bt.empty? || parent_bt.nil?
388
+
389
+ index_before_first_common_frame = -1.downto(-child_bt.size).find do |index|
390
+ parent_bt[index] != child_bt[index]
391
+ end
392
+
393
+ return child if index_before_first_common_frame.nil?
394
+ return child if index_before_first_common_frame == -1
395
+
396
+ child = child.dup
397
+ child.set_backtrace(child_bt[0..index_before_first_common_frame])
398
+ child
399
+ end
400
+ end
401
+ end
402
+
403
+ # @private
404
+ PENDING_DETAIL_FORMATTER = Proc.new do |example, colorizer|
405
+ colorizer.wrap("# #{example.execution_result.pending_message}", :detail)
406
+ end
407
+ end
408
+ end
409
+
410
+ # Provides a single exception instance that provides access to
411
+ # multiple sub-exceptions. This is used in situations where a single
412
+ # individual spec has multiple exceptions, such as one in the `it` block
413
+ # and one in an `after` block.
414
+ class MultipleExceptionError < StandardError
415
+ # @private
416
+ # Used so there is a common module in the ancestor chain of this class
417
+ # and `RSpec::Expectations::MultipleExpectationsNotMetError`, which allows
418
+ # code to detect exceptions that are instances of either, without first
419
+ # checking to see if rspec-expectations is loaded.
420
+ module InterfaceTag
421
+ # Appends the provided exception to the list.
422
+ # @param exception [Exception] Exception to append to the list.
423
+ # @private
424
+ def add(exception)
425
+ # `PendingExampleFixedError` can be assigned to an example that initially has no
426
+ # failures, but when the `aggregate_failures` around hook completes, it notifies of
427
+ # a failure. If we do not ignore `PendingExampleFixedError` it would be surfaced to
428
+ # the user as part of a multiple exception error, which is undesirable. While it's
429
+ # pretty weird we handle this here, it's the best solution I've been able to come
430
+ # up with, and `PendingExampleFixedError` always represents the _lack_ of any exception
431
+ # so clearly when we are transitioning to a `MultipleExceptionError`, it makes sense to
432
+ # ignore it.
433
+ return if Pending::PendingExampleFixedError === exception
434
+
435
+ return if exception == self
436
+
437
+ all_exceptions << exception
438
+
439
+ if exception.class.name =~ /RSpec/
440
+ failures << exception
441
+ else
442
+ other_errors << exception
443
+ end
444
+ end
445
+
446
+ # Provides a way to force `ex` to be something that satisfies the multiple
447
+ # exception error interface. If it already satisfies it, it will be returned;
448
+ # otherwise it will wrap it in a `MultipleExceptionError`.
449
+ # @private
450
+ def self.for(ex)
451
+ return ex if self === ex
452
+ MultipleExceptionError.new(ex)
453
+ end
454
+ end
455
+
456
+ include InterfaceTag
457
+
458
+ # @return [Array<Exception>] The list of failures.
459
+ attr_reader :failures
460
+
461
+ # @return [Array<Exception>] The list of other errors.
462
+ attr_reader :other_errors
463
+
464
+ # @return [Array<Exception>] The list of failures and other exceptions, combined.
465
+ attr_reader :all_exceptions
466
+
467
+ # @return [Hash] Metadata used by RSpec for formatting purposes.
468
+ attr_reader :aggregation_metadata
469
+
470
+ # @return [nil] Provided only for interface compatibility with
471
+ # `RSpec::Expectations::MultipleExpectationsNotMetError`.
472
+ attr_reader :aggregation_block_label
473
+
474
+ # @param exceptions [Array<Exception>] The initial list of exceptions.
475
+ def initialize(*exceptions)
476
+ super()
477
+
478
+ @failures = []
479
+ @other_errors = []
480
+ @all_exceptions = []
481
+ @aggregation_metadata = { :hide_backtrace => true }
482
+ @aggregation_block_label = nil
483
+
484
+ exceptions.each { |e| add e }
485
+ end
486
+
487
+ # @return [String] Combines all the exception messages into a single string.
488
+ # @note RSpec does not actually use this -- instead it formats each exception
489
+ # individually.
490
+ def message
491
+ all_exceptions.map(&:message).join("\n\n")
492
+ end
493
+
494
+ # @return [String] A summary of the failure, including the block label and a count of failures.
495
+ def summary
496
+ "Got #{exception_count_description}"
497
+ end
498
+
499
+ # return [String] A description of the failure/error counts.
500
+ def exception_count_description
501
+ failure_count = Formatters::Helpers.pluralize(failures.size, "failure")
502
+ return failure_count if other_errors.empty?
503
+ error_count = Formatters::Helpers.pluralize(other_errors.size, "other error")
504
+ "#{failure_count} and #{error_count}"
505
+ end
506
+ end
507
+ end
508
+ end