1
1
# encoding: utf-8
2
+ RSpec ::Support . require_rspec_core "formatters/snippet_extractor"
3
+ RSpec ::Support . require_rspec_support "encoded_string"
4
+
2
5
module RSpec
3
6
module Core
4
7
module Formatters
@@ -12,14 +15,13 @@ def initialize(exception, example, options={})
12
15
@exception = exception
13
16
@example = example
14
17
@message_color = options . fetch ( :message_color ) { RSpec . configuration . failure_color }
15
- @description = options . fetch ( :description_formatter ) { Proc . new { example . full_description } } . call ( self )
18
+ @description = options . fetch ( :description ) { example . full_description }
16
19
@detail_formatter = options . fetch ( :detail_formatter ) { Proc . new { } }
17
20
@extra_detail_formatter = options . fetch ( :extra_detail_formatter ) { Proc . new { } }
18
21
@backtrace_formatter = options . fetch ( :backtrace_formatter ) { RSpec . configuration . backtrace_formatter }
19
22
@indentation = options . fetch ( :indentation , 2 )
20
23
@skip_shared_group_trace = options . fetch ( :skip_shared_group_trace , false )
21
24
@failure_lines = options [ :failure_lines ]
22
- @extra_failure_lines = Array ( example . metadata [ :extra_failure_lines ] )
23
25
end
24
26
25
27
def message_lines
@@ -71,16 +73,21 @@ def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCo
71
73
end
72
74
73
75
def fully_formatted ( failure_number , colorizer = ::RSpec ::Core ::Formatters ::ConsoleCodes )
74
- alignment_basis = "#{ ' ' * @indentation } #{ failure_number } ) "
75
- indentation = ' ' * alignment_basis . length
76
-
77
- "\n #{ alignment_basis } #{ description_and_detail ( colorizer , indentation ) } " \
78
- "\n #{ formatted_message_and_backtrace ( colorizer , indentation ) } " \
79
- "#{ extra_detail_formatter . call ( failure_number , colorizer , indentation ) } "
76
+ lines = fully_formatted_lines ( failure_number , colorizer )
77
+ lines . join ( "\n " ) << "\n "
80
78
end
81
79
82
- def failure_slash_error_line
83
- @failure_slash_error_line ||= "Failure/Error: #{ read_failed_line . strip } "
80
+ def fully_formatted_lines ( failure_number , colorizer )
81
+ lines = [
82
+ description ,
83
+ detail_formatter . call ( example , colorizer ) ,
84
+ formatted_message_and_backtrace ( colorizer ) ,
85
+ extra_detail_formatter . call ( failure_number , colorizer ) ,
86
+ ] . compact . flatten
87
+
88
+ lines = indent_lines ( lines , failure_number )
89
+ lines . unshift ( "" )
90
+ lines
84
91
end
85
92
86
93
private
@@ -93,12 +100,6 @@ def final_exception(exception)
93
100
end
94
101
end
95
102
96
- def description_and_detail ( colorizer , indentation )
97
- detail = detail_formatter . call ( example , colorizer , indentation )
98
- return ( description || detail ) unless description && detail
99
- "#{ description } \n #{ indentation } #{ detail } "
100
- end
101
-
102
103
if String . method_defined? ( :encoding )
103
104
def encoding_of ( string )
104
105
string . encoding
@@ -118,28 +119,71 @@ def encoded_string(string)
118
119
# :nocov:
119
120
end
120
121
122
+ def indent_lines ( lines , failure_number )
123
+ alignment_basis = "#{ ' ' * @indentation } #{ failure_number } ) "
124
+ indentation = ' ' * alignment_basis . length
125
+
126
+ lines . each_with_index . map do |line , index |
127
+ if index == 0
128
+ "#{ alignment_basis } #{ line } "
129
+ elsif line . empty?
130
+ line
131
+ else
132
+ "#{ indentation } #{ line } "
133
+ end
134
+ end
135
+ end
136
+
121
137
def exception_class_name ( exception = @exception )
122
138
name = exception . class . name . to_s
123
139
name = "(anonymous error class)" if name == ''
124
140
name
125
141
end
126
142
127
143
def failure_lines
128
- @failure_lines ||=
129
- begin
130
- lines = [ ]
131
- lines << failure_slash_error_line unless ( description == failure_slash_error_line )
132
- lines << "#{ exception_class_name } :" unless exception_class_name =~ /RSpec/
133
- encoded_string ( exception . message . to_s ) . split ( "\n " ) . each do |line |
134
- lines << " #{ line } "
135
- end
136
- unless @extra_failure_lines . empty?
137
- lines << ''
138
- lines . concat ( @extra_failure_lines )
139
- lines << ''
140
- end
141
- lines
144
+ @failure_lines ||= [ ] . tap do |lines |
145
+ lines . concat ( failure_slash_error_lines )
146
+
147
+ sections = [ failure_slash_error_lines , exception_lines ]
148
+ if sections . any? { |section | section . size > 1 } && !exception_lines . first . empty?
149
+ lines << ''
142
150
end
151
+
152
+ lines . concat ( exception_lines )
153
+ lines . concat ( extra_failure_lines )
154
+ end
155
+ end
156
+
157
+ def failure_slash_error_lines
158
+ lines = read_failed_lines
159
+ if lines . count == 1
160
+ lines [ 0 ] = "Failure/Error: #{ lines [ 0 ] . strip } "
161
+ else
162
+ least_indentation = lines . map { |line | line [ /^[ \t ]*/ ] } . min
163
+ lines = lines . map { |line | line . sub ( /^#{ least_indentation } / , ' ' ) }
164
+ lines . unshift ( 'Failure/Error:' )
165
+ end
166
+ lines
167
+ end
168
+
169
+ def exception_lines
170
+ lines = [ ]
171
+ lines << "#{ exception_class_name } :" unless exception_class_name =~ /RSpec/
172
+ encoded_string ( exception . message . to_s ) . split ( "\n " ) . each do |line |
173
+ lines << ( line . empty? ? line : " #{ line } " )
174
+ end
175
+ lines
176
+ end
177
+
178
+ def extra_failure_lines
179
+ @extra_failure_lines ||= begin
180
+ lines = Array ( example . metadata [ :extra_failure_lines ] )
181
+ unless lines . empty?
182
+ lines . unshift ( '' )
183
+ lines . push ( '' )
184
+ end
185
+ lines
186
+ end
143
187
end
144
188
145
189
def add_shared_group_lines ( lines , colorizer )
@@ -152,22 +196,21 @@ def add_shared_group_lines(lines, colorizer)
152
196
lines
153
197
end
154
198
155
- def read_failed_line
199
+ def read_failed_lines
156
200
matching_line = find_failed_line
157
201
unless matching_line
158
- return "Unable to find matching line from backtrace"
202
+ return [ "Unable to find matching line from backtrace" ]
159
203
end
160
204
161
205
file_path , line_number = matching_line . match ( /(.+?):(\d +)(|:\d +)/ ) [ 1 ..2 ]
162
-
163
- if File . exist? ( file_path )
164
- File . readlines ( file_path ) [ line_number . to_i - 1 ] ||
165
- "Unable to find matching line in #{ file_path } "
166
- else
167
- "Unable to find #{ file_path } to read failed line"
168
- end
206
+ max_line_count = RSpec . configuration . max_displayed_failure_line_count
207
+ SnippetExtractor . extract_expression_lines_at ( file_path , line_number . to_i , max_line_count )
208
+ rescue SnippetExtractor ::NoSuchFileError
209
+ [ "Unable to find #{ file_path } to read failed line" ]
210
+ rescue SnippetExtractor ::NoSuchLineError
211
+ [ "Unable to find matching line in #{ file_path } " ]
169
212
rescue SecurityError
170
- "Unable to read failed line"
213
+ [ "Unable to read failed line" ]
171
214
end
172
215
173
216
def find_failed_line
@@ -181,16 +224,12 @@ def find_failed_line
181
224
end || exception_backtrace . first
182
225
end
183
226
184
- def formatted_message_and_backtrace ( colorizer , indentation )
227
+ def formatted_message_and_backtrace ( colorizer )
185
228
lines = colorized_message_lines ( colorizer ) + colorized_formatted_backtrace ( colorizer )
186
-
187
- formatted = ""
188
-
189
- lines . each do |line |
190
- formatted << RSpec ::Support ::EncodedString . new ( "#{ indentation } #{ line } \n " , encoding_of ( formatted ) )
229
+ encoding = encoding_of ( "" )
230
+ lines . map do |line |
231
+ RSpec ::Support ::EncodedString . new ( line , encoding )
191
232
end
192
-
193
- formatted
194
233
end
195
234
196
235
def exception_backtrace
@@ -226,9 +265,9 @@ def options
226
265
def pending_options
227
266
if @execution_result . pending_fixed?
228
267
{
229
- :description_formatter => Proc . new { "#{ @example . full_description } FIXED" } ,
230
- :message_color => RSpec . configuration . fixed_color ,
231
- :failure_lines => [
268
+ :description => "#{ @example . full_description } FIXED" ,
269
+ :message_color => RSpec . configuration . fixed_color ,
270
+ :failure_lines => [
232
271
"Expected pending '#{ @execution_result . pending_message } ' to fail. No Error was raised."
233
272
]
234
273
}
@@ -251,8 +290,6 @@ def with_multiple_error_options_as_needed(exception, options)
251
290
options [ :message_color ] )
252
291
)
253
292
254
- options [ :description_formatter ] &&= Proc . new { }
255
-
256
293
return options unless exception . aggregation_metadata [ :hide_backtrace ]
257
294
options [ :backtrace_formatter ] = EmptyBacktraceFormatter
258
295
options
@@ -263,7 +300,7 @@ def multiple_exceptions_error?(exception)
263
300
end
264
301
265
302
def multiple_exception_summarizer ( exception , prior_detail_formatter , color )
266
- lambda do |example , colorizer , indentation |
303
+ lambda do |example , colorizer |
267
304
summary = if exception . aggregation_metadata [ :hide_backtrace ]
268
305
# Since the backtrace is hidden, the subfailures will come
269
306
# immediately after this, and using `:` will read well.
@@ -276,27 +313,30 @@ def multiple_exception_summarizer(exception, prior_detail_formatter, color)
276
313
277
314
summary = colorizer . wrap ( summary , color || RSpec . configuration . failure_color )
278
315
return summary unless prior_detail_formatter
279
- "#{ prior_detail_formatter . call ( example , colorizer , indentation ) } \n #{ indentation } #{ summary } "
316
+ [
317
+ prior_detail_formatter . call ( example , colorizer ) ,
318
+ summary
319
+ ]
280
320
end
281
321
end
282
322
283
323
def sub_failure_list_formatter ( exception , message_color )
284
324
common_backtrace_truncater = CommonBacktraceTruncater . new ( exception )
285
325
286
- lambda do |failure_number , colorizer , indentation |
287
- exception . all_exceptions . each_with_index . map do |failure , index |
326
+ lambda do |failure_number , colorizer |
327
+ FlatMap . flat_map ( exception . all_exceptions . each_with_index ) do |failure , index |
288
328
options = with_multiple_error_options_as_needed (
289
329
failure ,
290
- :description_formatter => :failure_slash_error_line . to_proc ,
291
- :indentation => indentation . length ,
330
+ :description => nil ,
331
+ :indentation => 0 ,
292
332
:message_color => message_color || RSpec . configuration . failure_color ,
293
333
:skip_shared_group_trace => true
294
334
)
295
335
296
336
failure = common_backtrace_truncater . with_truncated_backtrace ( failure )
297
337
presenter = ExceptionPresenter . new ( failure , @example , options )
298
- presenter . fully_formatted ( "#{ failure_number } .#{ index + 1 } " , colorizer )
299
- end . join
338
+ presenter . fully_formatted_lines ( "#{ failure_number } .#{ index + 1 } " , colorizer )
339
+ end
300
340
end
301
341
end
302
342
0 commit comments