@@ -39,55 +39,63 @@ pub enum DescriptionKind<'a> {
39
39
///
40
40
/// The output is borrowed from standard error.
41
41
StackTrace {
42
- /// The stack trace as a subslice of the standard error .
43
- stderr_output : & ' a [ u8 ] ,
42
+ /// The subslice of standard error that contains the stack trace .
43
+ stderr_subslice : ByteSubslice < ' a > ,
44
44
} ,
45
45
46
46
/// An error string was found in the output.
47
47
///
48
48
/// The output is borrowed from standard error.
49
49
ErrorStr {
50
- /// The error string as a subslice of the standard error .
51
- stderr_output : & ' a [ u8 ] ,
50
+ /// The subslice of standard error that contains the stack trace .
51
+ stderr_subslice : ByteSubslice < ' a > ,
52
52
} ,
53
53
54
54
/// A should-panic test did not panic.
55
55
///
56
56
/// The output is borrowed from standard output.
57
57
ShouldPanic {
58
- /// The should-panic of the test as a subslice of the standard output .
59
- stdout_output : & ' a [ u8 ] ,
58
+ /// The subslice of standard output that contains the should-panic message .
59
+ stdout_subslice : ByteSubslice < ' a > ,
60
60
} ,
61
61
}
62
62
63
63
impl < ' a > DescriptionKind < ' a > {
64
64
/// Returns the subslice of standard error that contains the description.
65
- pub fn stderr_output ( & self ) -> Option < & ' a [ u8 ] > {
65
+ pub fn stderr_subslice ( & self ) -> Option < ByteSubslice < ' a > > {
66
66
match self {
67
67
DescriptionKind :: Abort { .. } => None ,
68
- DescriptionKind :: StackTrace { stderr_output } => Some ( * stderr_output) ,
69
- DescriptionKind :: ErrorStr { stderr_output } => Some ( * stderr_output) ,
68
+ DescriptionKind :: StackTrace { stderr_subslice }
69
+ | DescriptionKind :: ErrorStr {
70
+ stderr_subslice, ..
71
+ } => Some ( * stderr_subslice) ,
70
72
DescriptionKind :: ShouldPanic { .. } => None ,
71
73
}
72
74
}
73
75
74
76
/// Returns the subslice of standard output that contains the description.
75
- pub fn stdout_output ( & self ) -> Option < & ' a [ u8 ] > {
77
+ pub fn stdout_subslice ( & self ) -> Option < ByteSubslice < ' a > > {
76
78
match self {
77
79
DescriptionKind :: Abort { .. } => None ,
78
80
DescriptionKind :: StackTrace { .. } => None ,
79
81
DescriptionKind :: ErrorStr { .. } => None ,
80
- DescriptionKind :: ShouldPanic { stdout_output } => Some ( * stdout_output) ,
82
+ DescriptionKind :: ShouldPanic {
83
+ stdout_subslice, ..
84
+ } => Some ( * stdout_subslice) ,
81
85
}
82
86
}
83
87
84
88
/// Returns the subslice of combined output (either stdout or stderr) that contains the description.
85
- pub fn combined_output ( & self ) -> Option < & ' a [ u8 ] > {
89
+ pub fn combined_subslice ( & self ) -> Option < ByteSubslice < ' a > > {
86
90
match self {
87
91
DescriptionKind :: Abort { .. } => None ,
88
- DescriptionKind :: StackTrace { stderr_output } => Some ( * stderr_output) ,
89
- DescriptionKind :: ErrorStr { stderr_output } => Some ( * stderr_output) ,
90
- DescriptionKind :: ShouldPanic { stdout_output } => Some ( * stdout_output) ,
92
+ DescriptionKind :: StackTrace { stderr_subslice }
93
+ | DescriptionKind :: ErrorStr {
94
+ stderr_subslice, ..
95
+ } => Some ( * stderr_subslice) ,
96
+ DescriptionKind :: ShouldPanic {
97
+ stdout_subslice, ..
98
+ } => Some ( * stdout_subslice) ,
91
99
}
92
100
}
93
101
@@ -97,6 +105,18 @@ impl<'a> DescriptionKind<'a> {
97
105
}
98
106
}
99
107
108
+ /// A subslice of a byte slice.
109
+ ///
110
+ /// This type tracks the start index of the subslice from the parent slice.
111
+ #[ derive( Clone , Copy , Debug ) ]
112
+ pub struct ByteSubslice < ' a > {
113
+ /// The slice.
114
+ pub slice : & ' a [ u8 ] ,
115
+
116
+ /// The start index of the subslice from the parent slice.
117
+ pub start : usize ,
118
+ }
119
+
100
120
/// A display wrapper for [`DescriptionKind`].
101
121
#[ derive( Clone , Copy , Debug ) ]
102
122
pub struct DescriptionKindDisplay < ' a > ( DescriptionKind < ' a > ) ;
@@ -137,14 +157,15 @@ impl fmt::Display for DescriptionKindDisplay<'_> {
137
157
}
138
158
Ok ( ( ) )
139
159
}
140
- DescriptionKind :: StackTrace { stderr_output } => {
141
- write ! ( f, "{}" , String :: from_utf8_lossy( stderr_output) )
160
+ DescriptionKind :: StackTrace { stderr_subslice } => {
161
+ // Strip invalid XML characters.
162
+ write ! ( f, "{}" , String :: from_utf8_lossy( stderr_subslice. slice) )
142
163
}
143
- DescriptionKind :: ErrorStr { stderr_output } => {
144
- write ! ( f, "{}" , String :: from_utf8_lossy( stderr_output ) )
164
+ DescriptionKind :: ErrorStr { stderr_subslice } => {
165
+ write ! ( f, "{}" , String :: from_utf8_lossy( stderr_subslice . slice ) )
145
166
}
146
- DescriptionKind :: ShouldPanic { stdout_output } => {
147
- write ! ( f, "{}" , String :: from_utf8_lossy( stdout_output ) )
167
+ DescriptionKind :: ShouldPanic { stdout_subslice } => {
168
+ write ! ( f, "{}" , String :: from_utf8_lossy( stdout_subslice . slice ) )
148
169
}
149
170
}
150
171
}
@@ -166,26 +187,39 @@ pub fn heuristic_extract_description<'a>(
166
187
}
167
188
168
189
// Try the heuristic stack trace extraction first to try and grab more information first.
169
- if let Some ( stderr_output ) = heuristic_stack_trace ( stderr) {
170
- return Some ( DescriptionKind :: StackTrace { stderr_output } ) ;
190
+ if let Some ( stderr_subslice ) = heuristic_stack_trace ( stderr) {
191
+ return Some ( DescriptionKind :: StackTrace { stderr_subslice } ) ;
171
192
}
172
- if let Some ( stderr_output ) = heuristic_error_str ( stderr) {
173
- return Some ( DescriptionKind :: ErrorStr { stderr_output } ) ;
193
+ if let Some ( stderr_subslice ) = heuristic_error_str ( stderr) {
194
+ return Some ( DescriptionKind :: ErrorStr { stderr_subslice } ) ;
174
195
}
175
- if let Some ( stdout_output ) = heuristic_should_panic ( stdout) {
176
- return Some ( DescriptionKind :: ShouldPanic { stdout_output } ) ;
196
+ if let Some ( stdout_subslice ) = heuristic_should_panic ( stdout) {
197
+ return Some ( DescriptionKind :: ShouldPanic { stdout_subslice } ) ;
177
198
}
178
199
179
200
None
180
201
}
181
202
182
- fn heuristic_should_panic ( stdout : & [ u8 ] ) -> Option < & [ u8 ] > {
183
- stdout
203
+ fn heuristic_should_panic ( stdout : & [ u8 ] ) -> Option < ByteSubslice < ' _ > > {
204
+ let line = stdout
184
205
. lines ( )
185
- . find ( |line| line. contains_str ( "note: test did not panic as expected" ) )
206
+ . find ( |line| line. contains_str ( "note: test did not panic as expected" ) ) ?;
207
+
208
+ // SAFETY: line is a subslice of stdout.
209
+ let start = unsafe { line. as_ptr ( ) . offset_from ( stdout. as_ptr ( ) ) } ;
210
+
211
+ let start = usize:: try_from ( start) . unwrap_or_else ( |error| {
212
+ panic ! (
213
+ "negative offset from stdout.as_ptr() ({:x}) to line.as_ptr() ({:x}): {}" ,
214
+ stdout. as_ptr( ) as usize ,
215
+ line. as_ptr( ) as usize ,
216
+ error
217
+ )
218
+ } ) ;
219
+ Some ( ByteSubslice { slice : line, start } )
186
220
}
187
221
188
- fn heuristic_stack_trace ( stderr : & [ u8 ] ) -> Option < & [ u8 ] > {
222
+ fn heuristic_stack_trace ( stderr : & [ u8 ] ) -> Option < ByteSubslice < ' _ > > {
189
223
let panicked_at_match = PANICKED_AT_REGEX . find ( stderr) ?;
190
224
// If the previous line starts with "Error: ", grab it as well -- it contains the error with
191
225
// result-based test failures.
@@ -200,17 +234,24 @@ fn heuristic_stack_trace(stderr: &[u8]) -> Option<&[u8]> {
200
234
// TODO: this grabs too much -- it is possible that destructors print out further messages so we
201
235
// should be more careful. But it's hard to tell what's printed by the panic and what's printed
202
236
// by destructors, so we lean on the side of caution.
203
- Some ( stderr[ start..] . trim_end_with ( |c| c. is_whitespace ( ) ) )
237
+ Some ( ByteSubslice {
238
+ slice : stderr[ start..] . trim_end_with ( |c| c. is_whitespace ( ) ) ,
239
+ start,
240
+ } )
204
241
}
205
242
206
- fn heuristic_error_str ( stderr : & [ u8 ] ) -> Option < & [ u8 ] > {
243
+ fn heuristic_error_str ( stderr : & [ u8 ] ) -> Option < ByteSubslice < ' _ > > {
207
244
// Starting Rust 1.66, Result-based errors simply print out "Error: ".
208
245
let error_match = ERROR_REGEX . find ( stderr) ?;
209
246
let start = error_match. start ( ) ;
247
+
210
248
// TODO: this grabs too much -- it is possible that destructors print out further messages so we
211
249
// should be more careful. But it's hard to tell what's printed by the error and what's printed
212
250
// by destructors, so we lean on the side of caution.
213
- Some ( stderr[ start..] . trim_end_with ( |c| c. is_whitespace ( ) ) )
251
+ Some ( ByteSubslice {
252
+ slice : stderr[ start..] . trim_end_with ( |c| c. is_whitespace ( ) ) ,
253
+ start,
254
+ } )
214
255
}
215
256
216
257
/// Given a slice, find the index of the point at which highlighting should end.
@@ -257,7 +298,14 @@ test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 13 filtered out;
257
298
for ( input, output) in tests {
258
299
let extracted = heuristic_should_panic ( input. as_bytes ( ) )
259
300
. expect ( "should-panic message should have been found" ) ;
260
- assert_eq ! ( DisplayWrapper ( extracted) , DisplayWrapper ( output. as_bytes( ) ) ) ;
301
+ assert_eq ! (
302
+ DisplayWrapper ( extracted. slice) ,
303
+ DisplayWrapper ( output. as_bytes( ) )
304
+ ) ;
305
+ assert_eq ! (
306
+ extracted. start,
307
+ extracted. slice. as_ptr( ) as usize - input. as_bytes( ) . as_ptr( ) as usize
308
+ ) ;
261
309
}
262
310
}
263
311
@@ -379,9 +427,16 @@ some more text at the end, followed by some newlines"#,
379
427
] ;
380
428
381
429
for ( input, output) in tests {
382
- let trace = heuristic_stack_trace ( input. as_bytes ( ) )
430
+ let extracted = heuristic_stack_trace ( input. as_bytes ( ) )
383
431
. expect ( "stack trace should have been found" ) ;
384
- assert_eq ! ( DisplayWrapper ( trace) , DisplayWrapper ( output. as_bytes( ) ) ) ;
432
+ assert_eq ! (
433
+ DisplayWrapper ( extracted. slice) ,
434
+ DisplayWrapper ( output. as_bytes( ) )
435
+ ) ;
436
+ assert_eq ! (
437
+ extracted. start,
438
+ extracted. slice. as_ptr( ) as usize - input. as_bytes( ) . as_ptr( ) as usize
439
+ ) ;
385
440
}
386
441
}
387
442
@@ -393,9 +448,16 @@ some more text at the end, followed by some newlines"#,
393
448
) ] ;
394
449
395
450
for ( input, output) in tests {
396
- let error_str =
451
+ let extracted =
397
452
heuristic_error_str ( input. as_bytes ( ) ) . expect ( "error string should have been found" ) ;
398
- assert_eq ! ( DisplayWrapper ( error_str) , DisplayWrapper ( output. as_bytes( ) ) ) ;
453
+ assert_eq ! (
454
+ DisplayWrapper ( extracted. slice) ,
455
+ DisplayWrapper ( output. as_bytes( ) )
456
+ ) ;
457
+ assert_eq ! (
458
+ extracted. start,
459
+ extracted. slice. as_ptr( ) as usize - input. as_bytes( ) . as_ptr( ) as usize
460
+ ) ;
399
461
}
400
462
}
401
463
0 commit comments