rspec-core 2.11.1 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (222) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +3 -1
  5. data/Changelog.md +1814 -29
  6. data/{License.txt → LICENSE.md} +6 -4
  7. data/README.md +197 -48
  8. data/exe/rspec +2 -23
  9. data/lib/rspec/autorun.rb +1 -0
  10. data/lib/rspec/core/backtrace_formatter.rb +65 -0
  11. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  12. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  13. data/lib/rspec/core/bisect/fork_runner.rb +138 -0
  14. data/lib/rspec/core/bisect/server.rb +61 -0
  15. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  16. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  17. data/lib/rspec/core/bisect/utilities.rb +69 -0
  18. data/lib/rspec/core/configuration.rb +1846 -407
  19. data/lib/rspec/core/configuration_options.rb +154 -50
  20. data/lib/rspec/core/did_you_mean.rb +46 -0
  21. data/lib/rspec/core/drb.rb +120 -0
  22. data/lib/rspec/core/dsl.rb +90 -18
  23. data/lib/rspec/core/example.rb +488 -152
  24. data/lib/rspec/core/example_group.rb +733 -294
  25. data/lib/rspec/core/example_status_persister.rb +235 -0
  26. data/lib/rspec/core/filter_manager.rb +175 -147
  27. data/lib/rspec/core/flat_map.rb +20 -0
  28. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  29. data/lib/rspec/core/formatters/base_formatter.rb +32 -130
  30. data/lib/rspec/core/formatters/base_text_formatter.rb +62 -190
  31. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  32. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  33. data/lib/rspec/core/formatters/console_codes.rb +76 -0
  34. data/lib/rspec/core/formatters/deprecation_formatter.rb +223 -0
  35. data/lib/rspec/core/formatters/documentation_formatter.rb +62 -27
  36. data/lib/rspec/core/formatters/exception_presenter.rb +521 -0
  37. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  38. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  39. data/lib/rspec/core/formatters/helpers.rb +93 -14
  40. data/lib/rspec/core/formatters/html_formatter.rb +104 -415
  41. data/lib/rspec/core/formatters/html_printer.rb +414 -0
  42. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  43. data/lib/rspec/core/formatters/json_formatter.rb +102 -0
  44. data/lib/rspec/core/formatters/profile_formatter.rb +68 -0
  45. data/lib/rspec/core/formatters/progress_formatter.rb +12 -15
  46. data/lib/rspec/core/formatters/protocol.rb +182 -0
  47. data/lib/rspec/core/formatters/snippet_extractor.rb +115 -39
  48. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  49. data/lib/rspec/core/formatters.rb +279 -0
  50. data/lib/rspec/core/hooks.rb +451 -300
  51. data/lib/rspec/core/invocations.rb +87 -0
  52. data/lib/rspec/core/memoized_helpers.rb +580 -0
  53. data/lib/rspec/core/metadata.rb +395 -173
  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 +208 -64
  63. data/lib/rspec/core/ordering.rb +169 -0
  64. data/lib/rspec/core/output_wrapper.rb +29 -0
  65. data/lib/rspec/core/pending.rb +115 -59
  66. data/lib/rspec/core/profiler.rb +34 -0
  67. data/lib/rspec/core/project_initializer/.rspec +1 -0
  68. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +98 -0
  69. data/lib/rspec/core/project_initializer.rb +26 -65
  70. data/lib/rspec/core/rake_task.rb +140 -131
  71. data/lib/rspec/core/reporter.rb +207 -44
  72. data/lib/rspec/core/ruby_project.rb +15 -6
  73. data/lib/rspec/core/runner.rb +180 -44
  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 +25 -19
  77. data/lib/rspec/core/shared_example_group.rb +229 -54
  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 +3 -1
  81. data/lib/rspec/core/warnings.rb +40 -0
  82. data/lib/rspec/core/world.rb +208 -49
  83. data/lib/rspec/core.rb +166 -80
  84. data.tar.gz.sig +0 -0
  85. metadata +230 -445
  86. metadata.gz.sig +0 -0
  87. data/exe/autospec +0 -13
  88. data/features/Autotest.md +0 -38
  89. data/features/README.md +0 -17
  90. data/features/Upgrade.md +0 -364
  91. data/features/command_line/README.md +0 -28
  92. data/features/command_line/example_name_option.feature +0 -101
  93. data/features/command_line/exit_status.feature +0 -83
  94. data/features/command_line/format_option.feature +0 -81
  95. data/features/command_line/init.feature +0 -18
  96. data/features/command_line/line_number_appended_to_path.feature +0 -140
  97. data/features/command_line/line_number_option.feature +0 -58
  98. data/features/command_line/order.feature +0 -29
  99. data/features/command_line/pattern_option.feature +0 -31
  100. data/features/command_line/rake_task.feature +0 -68
  101. data/features/command_line/ruby.feature +0 -22
  102. data/features/command_line/tag.feature +0 -91
  103. data/features/configuration/alias_example_to.feature +0 -48
  104. data/features/configuration/custom_settings.feature +0 -84
  105. data/features/configuration/default_path.feature +0 -38
  106. data/features/configuration/fail_fast.feature +0 -77
  107. data/features/configuration/read_options_from_file.feature +0 -87
  108. data/features/example_groups/basic_structure.feature +0 -55
  109. data/features/example_groups/shared_context.feature +0 -74
  110. data/features/example_groups/shared_examples.feature +0 -204
  111. data/features/expectation_framework_integration/configure_expectation_framework.feature +0 -102
  112. data/features/filtering/exclusion_filters.feature +0 -139
  113. data/features/filtering/if_and_unless.feature +0 -168
  114. data/features/filtering/inclusion_filters.feature +0 -105
  115. data/features/filtering/run_all_when_everything_filtered.feature +0 -46
  116. data/features/formatters/custom_formatter.feature +0 -36
  117. data/features/formatters/text_formatter.feature +0 -46
  118. data/features/helper_methods/arbitrary_methods.feature +0 -40
  119. data/features/helper_methods/let.feature +0 -50
  120. data/features/helper_methods/modules.feature +0 -149
  121. data/features/hooks/around_hooks.feature +0 -343
  122. data/features/hooks/before_and_after_hooks.feature +0 -423
  123. data/features/hooks/filtering.feature +0 -234
  124. data/features/metadata/current_example.feature +0 -17
  125. data/features/metadata/described_class.feature +0 -17
  126. data/features/metadata/user_defined.feature +0 -113
  127. data/features/mock_framework_integration/use_any_framework.feature +0 -106
  128. data/features/mock_framework_integration/use_flexmock.feature +0 -96
  129. data/features/mock_framework_integration/use_mocha.feature +0 -97
  130. data/features/mock_framework_integration/use_rr.feature +0 -98
  131. data/features/mock_framework_integration/use_rspec.feature +0 -97
  132. data/features/pending/pending_examples.feature +0 -229
  133. data/features/spec_files/arbitrary_file_suffix.feature +0 -13
  134. data/features/step_definitions/additional_cli_steps.rb +0 -30
  135. data/features/subject/attribute_of_subject.feature +0 -124
  136. data/features/subject/explicit_subject.feature +0 -82
  137. data/features/subject/implicit_receiver.feature +0 -29
  138. data/features/subject/implicit_subject.feature +0 -63
  139. data/features/support/env.rb +0 -12
  140. data/lib/autotest/discover.rb +0 -1
  141. data/lib/autotest/rspec2.rb +0 -73
  142. data/lib/rspec/core/backward_compatibility.rb +0 -65
  143. data/lib/rspec/core/command_line.rb +0 -36
  144. data/lib/rspec/core/deprecation.rb +0 -36
  145. data/lib/rspec/core/drb_command_line.rb +0 -26
  146. data/lib/rspec/core/drb_options.rb +0 -87
  147. data/lib/rspec/core/extensions/instance_eval_with_args.rb +0 -44
  148. data/lib/rspec/core/extensions/kernel.rb +0 -9
  149. data/lib/rspec/core/extensions/module_eval_with_args.rb +0 -38
  150. data/lib/rspec/core/extensions/ordered.rb +0 -21
  151. data/lib/rspec/core/extensions.rb +0 -4
  152. data/lib/rspec/core/formatters/text_mate_formatter.rb +0 -34
  153. data/lib/rspec/core/let.rb +0 -110
  154. data/lib/rspec/core/load_path.rb +0 -3
  155. data/lib/rspec/core/metadata_hash_builder.rb +0 -97
  156. data/lib/rspec/core/mocking/with_absolutely_nothing.rb +0 -11
  157. data/lib/rspec/core/mocking/with_flexmock.rb +0 -27
  158. data/lib/rspec/core/mocking/with_mocha.rb +0 -29
  159. data/lib/rspec/core/mocking/with_rr.rb +0 -27
  160. data/lib/rspec/core/mocking/with_rspec.rb +0 -23
  161. data/lib/rspec/core/subject.rb +0 -219
  162. data/spec/autotest/discover_spec.rb +0 -19
  163. data/spec/autotest/failed_results_re_spec.rb +0 -45
  164. data/spec/autotest/rspec_spec.rb +0 -123
  165. data/spec/command_line/order_spec.rb +0 -137
  166. data/spec/rspec/core/command_line_spec.rb +0 -108
  167. data/spec/rspec/core/command_line_spec_output.txt +0 -0
  168. data/spec/rspec/core/configuration_options_spec.rb +0 -377
  169. data/spec/rspec/core/configuration_spec.rb +0 -1196
  170. data/spec/rspec/core/deprecations_spec.rb +0 -66
  171. data/spec/rspec/core/drb_command_line_spec.rb +0 -108
  172. data/spec/rspec/core/drb_options_spec.rb +0 -180
  173. data/spec/rspec/core/dsl_spec.rb +0 -17
  174. data/spec/rspec/core/example_group_spec.rb +0 -1098
  175. data/spec/rspec/core/example_spec.rb +0 -370
  176. data/spec/rspec/core/filter_manager_spec.rb +0 -256
  177. data/spec/rspec/core/formatters/base_formatter_spec.rb +0 -80
  178. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +0 -363
  179. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +0 -88
  180. data/spec/rspec/core/formatters/helpers_spec.rb +0 -66
  181. data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +0 -410
  182. data/spec/rspec/core/formatters/html_formatted-1.8.7.html +0 -409
  183. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +0 -416
  184. data/spec/rspec/core/formatters/html_formatted-1.9.3.html +0 -416
  185. data/spec/rspec/core/formatters/html_formatter_spec.rb +0 -82
  186. data/spec/rspec/core/formatters/progress_formatter_spec.rb +0 -30
  187. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +0 -18
  188. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +0 -410
  189. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +0 -409
  190. data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +0 -416
  191. data/spec/rspec/core/formatters/text_mate_formatted-1.9.3.html +0 -416
  192. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +0 -83
  193. data/spec/rspec/core/hooks_filtering_spec.rb +0 -227
  194. data/spec/rspec/core/hooks_spec.rb +0 -250
  195. data/spec/rspec/core/kernel_extensions_spec.rb +0 -9
  196. data/spec/rspec/core/let_spec.rb +0 -55
  197. data/spec/rspec/core/metadata_spec.rb +0 -447
  198. data/spec/rspec/core/option_parser_spec.rb +0 -166
  199. data/spec/rspec/core/pending_example_spec.rb +0 -220
  200. data/spec/rspec/core/project_initializer_spec.rb +0 -130
  201. data/spec/rspec/core/rake_task_spec.rb +0 -138
  202. data/spec/rspec/core/reporter_spec.rb +0 -103
  203. data/spec/rspec/core/resources/a_bar.rb +0 -0
  204. data/spec/rspec/core/resources/a_foo.rb +0 -0
  205. data/spec/rspec/core/resources/a_spec.rb +0 -1
  206. data/spec/rspec/core/resources/custom_example_group_runner.rb +0 -14
  207. data/spec/rspec/core/resources/formatter_specs.rb +0 -60
  208. data/spec/rspec/core/resources/utf8_encoded.rb +0 -8
  209. data/spec/rspec/core/rspec_matchers_spec.rb +0 -45
  210. data/spec/rspec/core/ruby_project_spec.rb +0 -24
  211. data/spec/rspec/core/runner_spec.rb +0 -81
  212. data/spec/rspec/core/shared_context_spec.rb +0 -67
  213. data/spec/rspec/core/shared_example_group_spec.rb +0 -84
  214. data/spec/rspec/core/subject_spec.rb +0 -244
  215. data/spec/rspec/core/world_spec.rb +0 -144
  216. data/spec/rspec/core_spec.rb +0 -35
  217. data/spec/spec_helper.rb +0 -98
  218. data/spec/support/config_options_helper.rb +0 -24
  219. data/spec/support/helper_methods.rb +0 -5
  220. data/spec/support/matchers.rb +0 -65
  221. data/spec/support/shared_example_groups.rb +0 -41
  222. data/spec/support/spec_files.rb +0 -44
@@ -0,0 +1,521 @@
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, [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(exception_message_string(last_cause)).split("\n").each do |line|
55
+ cause << " #{line}"
56
+ end
57
+
58
+ unless last_cause.backtrace.nil? || last_cause.backtrace.empty?
59
+ cause << (" #{backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata).first}")
60
+ end
61
+ end
62
+
63
+ cause
64
+ end
65
+ else
66
+ # :nocov:
67
+ def formatted_cause(_)
68
+ []
69
+ end
70
+ # :nocov:
71
+ end
72
+
73
+ def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
74
+ formatted_backtrace.map do |backtrace_info|
75
+ colorizer.wrap "# #{backtrace_info}", RSpec.configuration.detail_color
76
+ end
77
+ end
78
+
79
+ def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes)
80
+ lines = fully_formatted_lines(failure_number, colorizer)
81
+ lines.join("\n") << "\n"
82
+ end
83
+
84
+ def fully_formatted_lines(failure_number, colorizer)
85
+ lines = [
86
+ encoded_description(description),
87
+ detail_formatter.call(example, colorizer),
88
+ formatted_message_and_backtrace(colorizer),
89
+ extra_detail_formatter.call(failure_number, colorizer),
90
+ ].compact.flatten
91
+
92
+ lines = indent_lines(lines, failure_number)
93
+ lines.unshift("")
94
+ lines
95
+ end
96
+
97
+ private
98
+
99
+ def final_exception(exception, previous=[])
100
+ cause = exception.cause
101
+
102
+ if cause && Exception === cause && !previous.include?(cause)
103
+ previous << cause
104
+ final_exception(cause, previous)
105
+ else
106
+ exception
107
+ end
108
+ end
109
+
110
+ if String.method_defined?(:encoding)
111
+ def encoding_of(string)
112
+ string.encoding
113
+ end
114
+
115
+ def encoded_string(string)
116
+ RSpec::Support::EncodedString.new(string, Encoding.default_external)
117
+ end
118
+ else # for 1.8.7
119
+ # :nocov:
120
+ def encoding_of(_string)
121
+ end
122
+
123
+ def encoded_string(string)
124
+ RSpec::Support::EncodedString.new(string)
125
+ end
126
+ # :nocov:
127
+ end
128
+
129
+ def indent_lines(lines, failure_number)
130
+ alignment_basis = ' ' * @indentation
131
+ alignment_basis << "#{failure_number}) " if failure_number
132
+ indentation = ' ' * alignment_basis.length
133
+
134
+ lines.each_with_index.map do |line, index|
135
+ if index == 0
136
+ "#{alignment_basis}#{line}"
137
+ elsif line.empty?
138
+ line
139
+ else
140
+ "#{indentation}#{line}"
141
+ end
142
+ end
143
+ end
144
+
145
+ def exception_class_name(exception=@exception)
146
+ name = exception.class.name.to_s
147
+ name = "(anonymous error class)" if name == ''
148
+ name
149
+ end
150
+
151
+ def failure_lines
152
+ @failure_lines ||= [].tap do |lines|
153
+ lines.concat(failure_slash_error_lines)
154
+
155
+ sections = [failure_slash_error_lines, exception_lines]
156
+ if sections.any? { |section| section.size > 1 } && !exception_lines.first.empty?
157
+ lines << ''
158
+ end
159
+
160
+ lines.concat(exception_lines)
161
+ lines.concat(extra_failure_lines)
162
+ end
163
+ end
164
+
165
+ def failure_slash_error_lines
166
+ lines = read_failed_lines
167
+ if lines.count == 1
168
+ lines[0] = "Failure/Error: #{lines[0].strip}"
169
+ else
170
+ least_indentation = SnippetExtractor.least_indentation_from(lines)
171
+ lines = lines.map { |line| line.sub(/^#{least_indentation}/, ' ') }
172
+ lines.unshift('Failure/Error:')
173
+ end
174
+ lines
175
+ end
176
+
177
+ # rubocop:disable Lint/RescueException
178
+ def exception_message_string(exception)
179
+ exception.message.to_s
180
+ rescue Exception => other
181
+ "A #{exception.class} for which `exception.message.to_s` raises #{other.class}."
182
+ end
183
+ # rubocop:enable Lint/RescueException
184
+
185
+ def exception_lines
186
+ @exception_lines ||= begin
187
+ lines = []
188
+ lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/
189
+ encoded_string(exception_message_string(exception)).split("\n").each do |line|
190
+ lines << (line.empty? ? line : " #{line}")
191
+ end
192
+ lines
193
+ end
194
+ end
195
+
196
+ def extra_failure_lines
197
+ @extra_failure_lines ||= begin
198
+ lines = Array(example.metadata[:extra_failure_lines])
199
+ unless lines.empty?
200
+ lines.unshift('')
201
+ lines.push('')
202
+ end
203
+ lines
204
+ end
205
+ end
206
+
207
+ def add_shared_group_lines(lines, colorizer)
208
+ return lines if @skip_shared_group_trace
209
+
210
+ example.metadata[:shared_group_inclusion_backtrace].each do |frame|
211
+ lines << colorizer.wrap(frame.description, RSpec.configuration.default_color)
212
+ end
213
+
214
+ lines
215
+ end
216
+
217
+ def read_failed_lines
218
+ matching_line = find_failed_line
219
+ unless matching_line
220
+ return ["Unable to find matching line from backtrace"]
221
+ end
222
+
223
+ file_and_line_number = matching_line.match(/(.+?):(\d+)(|:\d+)/)
224
+
225
+ unless file_and_line_number
226
+ return ["Unable to infer file and line number from backtrace"]
227
+ end
228
+
229
+ file_path, line_number = file_and_line_number[1..2]
230
+ max_line_count = RSpec.configuration.max_displayed_failure_line_count
231
+ lines = SnippetExtractor.extract_expression_lines_at(file_path, line_number.to_i, max_line_count)
232
+ RSpec.world.syntax_highlighter.highlight(lines)
233
+ rescue SnippetExtractor::NoSuchFileError
234
+ ["Unable to find #{file_path} to read failed line"]
235
+ rescue SnippetExtractor::NoSuchLineError
236
+ ["Unable to find matching line in #{file_path}"]
237
+ rescue SecurityError
238
+ ["Unable to read failed line"]
239
+ end
240
+
241
+ def find_failed_line
242
+ line_regex = RSpec.configuration.in_project_source_dir_regex
243
+ loaded_spec_files = RSpec.configuration.loaded_spec_files
244
+
245
+ exception_backtrace.find do |line|
246
+ next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
247
+ path = File.expand_path(line_path)
248
+ loaded_spec_files.include?(path) || path =~ line_regex
249
+ end || exception_backtrace.first
250
+ end
251
+
252
+ def formatted_message_and_backtrace(colorizer)
253
+ lines = colorized_message_lines(colorizer) + colorized_formatted_backtrace(colorizer)
254
+ encoding = encoding_of("")
255
+ lines.map do |line|
256
+ RSpec::Support::EncodedString.new(line, encoding)
257
+ end
258
+ end
259
+
260
+ if String.method_defined?(:encoding)
261
+ def encoded_description(description)
262
+ return if description.nil?
263
+ encoded_string(description)
264
+ end
265
+ else # for 1.8.7
266
+ def encoded_description(description)
267
+ description
268
+ end
269
+ end
270
+
271
+ def exception_backtrace
272
+ exception.backtrace || []
273
+ end
274
+
275
+ # @private
276
+ # Configuring the `ExceptionPresenter` with the right set of options to handle
277
+ # pending vs failed vs skipped and aggregated (or not) failures is not simple.
278
+ # This class takes care of building an appropriate `ExceptionPresenter` for the
279
+ # provided example.
280
+ class Factory
281
+ def build
282
+ ExceptionPresenter.new(@exception, @example, options)
283
+ end
284
+
285
+ private
286
+
287
+ def initialize(example)
288
+ @example = example
289
+ @execution_result = example.execution_result
290
+ @exception = if @execution_result.status == :pending
291
+ @execution_result.pending_exception
292
+ else
293
+ @execution_result.exception
294
+ end
295
+ end
296
+
297
+ def options
298
+ with_multiple_error_options_as_needed(@exception, pending_options || {})
299
+ end
300
+
301
+ def pending_options
302
+ if @execution_result.pending_fixed?
303
+ {
304
+ :description => "#{@example.full_description} FIXED",
305
+ :message_color => RSpec.configuration.fixed_color,
306
+ :failure_lines => [
307
+ "Expected pending '#{@execution_result.pending_message}' to fail. No error was raised."
308
+ ]
309
+ }
310
+ elsif @execution_result.status == :pending
311
+ {
312
+ :message_color => RSpec.configuration.pending_color,
313
+ :detail_formatter => PENDING_DETAIL_FORMATTER
314
+ }
315
+ end
316
+ end
317
+
318
+ def with_multiple_error_options_as_needed(exception, options)
319
+ return options unless multiple_exceptions_error?(exception)
320
+
321
+ options = options.merge(
322
+ :failure_lines => [],
323
+ :extra_detail_formatter => sub_failure_list_formatter(exception, options[:message_color]),
324
+ :detail_formatter => multiple_exception_summarizer(exception,
325
+ options[:detail_formatter],
326
+ options[:message_color])
327
+ )
328
+
329
+ return options unless exception.aggregation_metadata[:hide_backtrace]
330
+ options[:backtrace_formatter] = EmptyBacktraceFormatter
331
+ options
332
+ end
333
+
334
+ def multiple_exceptions_error?(exception)
335
+ MultipleExceptionError::InterfaceTag === exception
336
+ end
337
+
338
+ def multiple_exception_summarizer(exception, prior_detail_formatter, color)
339
+ lambda do |example, colorizer|
340
+ summary = if exception.aggregation_metadata[:hide_backtrace]
341
+ # Since the backtrace is hidden, the subfailures will come
342
+ # immediately after this, and using `:` will read well.
343
+ "Got #{exception.exception_count_description}:"
344
+ else
345
+ # The backtrace comes after this, so using a `:` doesn't make sense
346
+ # since the failures may be many lines below.
347
+ "#{exception.summary}."
348
+ end
349
+
350
+ summary = colorizer.wrap(summary, color || RSpec.configuration.failure_color)
351
+ return summary unless prior_detail_formatter
352
+ [
353
+ prior_detail_formatter.call(example, colorizer),
354
+ summary
355
+ ]
356
+ end
357
+ end
358
+
359
+ def sub_failure_list_formatter(exception, message_color)
360
+ common_backtrace_truncater = CommonBacktraceTruncater.new(exception)
361
+
362
+ lambda do |failure_number, colorizer|
363
+ FlatMap.flat_map(exception.all_exceptions.each_with_index) do |failure, index|
364
+ options = with_multiple_error_options_as_needed(
365
+ failure,
366
+ :description => nil,
367
+ :indentation => 0,
368
+ :message_color => message_color || RSpec.configuration.failure_color,
369
+ :skip_shared_group_trace => true
370
+ )
371
+
372
+ failure = common_backtrace_truncater.with_truncated_backtrace(failure)
373
+ presenter = ExceptionPresenter.new(failure, @example, options)
374
+ presenter.fully_formatted_lines(
375
+ "#{failure_number ? "#{failure_number}." : ''}#{index + 1}",
376
+ colorizer
377
+ )
378
+ end
379
+ end
380
+ end
381
+
382
+ # @private
383
+ # Used to prevent a confusing backtrace from showing up from the `aggregate_failures`
384
+ # block declared for `:aggregate_failures` metadata.
385
+ module EmptyBacktraceFormatter
386
+ def self.format_backtrace(*)
387
+ []
388
+ end
389
+ end
390
+
391
+ # @private
392
+ class CommonBacktraceTruncater
393
+ def initialize(parent)
394
+ @parent = parent
395
+ end
396
+
397
+ def with_truncated_backtrace(child)
398
+ child_bt = child.backtrace
399
+ parent_bt = @parent.backtrace
400
+ return child if child_bt.nil? || child_bt.empty? || parent_bt.nil?
401
+
402
+ index_before_first_common_frame = -1.downto(-child_bt.size).find do |index|
403
+ parent_bt[index] != child_bt[index]
404
+ end
405
+
406
+ return child if index_before_first_common_frame.nil?
407
+ return child if index_before_first_common_frame == -1
408
+
409
+ child = child.dup
410
+ child.set_backtrace(child_bt[0..index_before_first_common_frame])
411
+ child
412
+ end
413
+ end
414
+ end
415
+
416
+ # @private
417
+ PENDING_DETAIL_FORMATTER = Proc.new do |example, colorizer|
418
+ colorizer.wrap("# #{example.execution_result.pending_message}", :detail)
419
+ end
420
+ end
421
+ end
422
+
423
+ # Provides a single exception instance that provides access to
424
+ # multiple sub-exceptions. This is used in situations where a single
425
+ # individual spec has multiple exceptions, such as one in the `it` block
426
+ # and one in an `after` block.
427
+ class MultipleExceptionError < StandardError
428
+ # @private
429
+ # Used so there is a common module in the ancestor chain of this class
430
+ # and `RSpec::Expectations::MultipleExpectationsNotMetError`, which allows
431
+ # code to detect exceptions that are instances of either, without first
432
+ # checking to see if rspec-expectations is loaded.
433
+ module InterfaceTag
434
+ # Appends the provided exception to the list.
435
+ # @param exception [Exception] Exception to append to the list.
436
+ # @private
437
+ def add(exception)
438
+ # `PendingExampleFixedError` can be assigned to an example that initially has no
439
+ # failures, but when the `aggregate_failures` around hook completes, it notifies of
440
+ # a failure. If we do not ignore `PendingExampleFixedError` it would be surfaced to
441
+ # the user as part of a multiple exception error, which is undesirable. While it's
442
+ # pretty weird we handle this here, it's the best solution I've been able to come
443
+ # up with, and `PendingExampleFixedError` always represents the _lack_ of any exception
444
+ # so clearly when we are transitioning to a `MultipleExceptionError`, it makes sense to
445
+ # ignore it.
446
+ return if Pending::PendingExampleFixedError === exception
447
+
448
+ return if exception == self
449
+
450
+ all_exceptions << exception
451
+
452
+ if exception.class.name =~ /RSpec/
453
+ failures << exception
454
+ else
455
+ other_errors << exception
456
+ end
457
+ end
458
+
459
+ # Provides a way to force `ex` to be something that satisfies the multiple
460
+ # exception error interface. If it already satisfies it, it will be returned;
461
+ # otherwise it will wrap it in a `MultipleExceptionError`.
462
+ # @private
463
+ def self.for(ex)
464
+ return ex if self === ex
465
+ MultipleExceptionError.new(ex)
466
+ end
467
+ end
468
+
469
+ include InterfaceTag
470
+
471
+ # @return [Array<Exception>] The list of failures.
472
+ attr_reader :failures
473
+
474
+ # @return [Array<Exception>] The list of other errors.
475
+ attr_reader :other_errors
476
+
477
+ # @return [Array<Exception>] The list of failures and other exceptions, combined.
478
+ attr_reader :all_exceptions
479
+
480
+ # @return [Hash] Metadata used by RSpec for formatting purposes.
481
+ attr_reader :aggregation_metadata
482
+
483
+ # @return [nil] Provided only for interface compatibility with
484
+ # `RSpec::Expectations::MultipleExpectationsNotMetError`.
485
+ attr_reader :aggregation_block_label
486
+
487
+ # @param exceptions [Array<Exception>] The initial list of exceptions.
488
+ def initialize(*exceptions)
489
+ super()
490
+
491
+ @failures = []
492
+ @other_errors = []
493
+ @all_exceptions = []
494
+ @aggregation_metadata = { :hide_backtrace => true }
495
+ @aggregation_block_label = nil
496
+
497
+ exceptions.each { |e| add e }
498
+ end
499
+
500
+ # @return [String] Combines all the exception messages into a single string.
501
+ # @note RSpec does not actually use this -- instead it formats each exception
502
+ # individually.
503
+ def message
504
+ all_exceptions.map(&:message).join("\n\n")
505
+ end
506
+
507
+ # @return [String] A summary of the failure, including the block label and a count of failures.
508
+ def summary
509
+ "Got #{exception_count_description}"
510
+ end
511
+
512
+ # return [String] A description of the failure/error counts.
513
+ def exception_count_description
514
+ failure_count = Formatters::Helpers.pluralize(failures.size, "failure")
515
+ return failure_count if other_errors.empty?
516
+ error_count = Formatters::Helpers.pluralize(other_errors.size, "other error")
517
+ "#{failure_count} and #{error_count}"
518
+ end
519
+ end
520
+ end
521
+ end
@@ -0,0 +1,23 @@
1
+ RSpec::Support.require_rspec_core "formatters/base_formatter"
2
+
3
+ module RSpec
4
+ module Core
5
+ module Formatters
6
+ # @private
7
+ class FailureListFormatter < BaseFormatter
8
+ Formatters.register self, :example_failed, :dump_profile, :message
9
+
10
+ def example_failed(failure)
11
+ output.puts "#{failure.example.location}:#{failure.example.description}"
12
+ end
13
+
14
+ # Discard profile and messages
15
+ #
16
+ # These outputs are not really relevant in the context of this failure
17
+ # list formatter.
18
+ def dump_profile(_profile); end
19
+ def message(_message); end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ module RSpec
2
+ module Core
3
+ module Formatters
4
+ # @api private
5
+ # Formatter for providing message output as a fallback when no other
6
+ # profiler implements #message
7
+ class FallbackMessageFormatter
8
+ Formatters.register self, :message
9
+
10
+ def initialize(output)
11
+ @output = output
12
+ end
13
+
14
+ # @private
15
+ attr_reader :output
16
+
17
+ # @api public
18
+ #
19
+ # Used by the reporter to send messages to the output stream.
20
+ #
21
+ # @param notification [MessageNotification] containing message
22
+ def message(notification)
23
+ output.puts notification.message
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end