@@ -39,55 +39,63 @@ pub enum DescriptionKind<'a> {
3939 ///
4040 /// The output is borrowed from standard error.
4141 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 > ,
4444 } ,
4545
4646 /// An error string was found in the output.
4747 ///
4848 /// The output is borrowed from standard error.
4949 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 > ,
5252 } ,
5353
5454 /// A should-panic test did not panic.
5555 ///
5656 /// The output is borrowed from standard output.
5757 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 > ,
6060 } ,
6161}
6262
6363impl < ' a > DescriptionKind < ' a > {
6464 /// 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 > > {
6666 match self {
6767 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) ,
7072 DescriptionKind :: ShouldPanic { .. } => None ,
7173 }
7274 }
7375
7476 /// 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 > > {
7678 match self {
7779 DescriptionKind :: Abort { .. } => None ,
7880 DescriptionKind :: StackTrace { .. } => None ,
7981 DescriptionKind :: ErrorStr { .. } => None ,
80- DescriptionKind :: ShouldPanic { stdout_output } => Some ( * stdout_output) ,
82+ DescriptionKind :: ShouldPanic {
83+ stdout_subslice, ..
84+ } => Some ( * stdout_subslice) ,
8185 }
8286 }
8387
8488 /// 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 > > {
8690 match self {
8791 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) ,
9199 }
92100 }
93101
@@ -97,6 +105,18 @@ impl<'a> DescriptionKind<'a> {
97105 }
98106}
99107
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+
100120/// A display wrapper for [`DescriptionKind`].
101121#[ derive( Clone , Copy , Debug ) ]
102122pub struct DescriptionKindDisplay < ' a > ( DescriptionKind < ' a > ) ;
@@ -137,14 +157,15 @@ impl fmt::Display for DescriptionKindDisplay<'_> {
137157 }
138158 Ok ( ( ) )
139159 }
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) )
142163 }
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 ) )
145166 }
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 ) )
148169 }
149170 }
150171 }
@@ -166,26 +187,39 @@ pub fn heuristic_extract_description<'a>(
166187 }
167188
168189 // 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 } ) ;
171192 }
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 } ) ;
174195 }
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 } ) ;
177198 }
178199
179200 None
180201}
181202
182- fn heuristic_should_panic ( stdout : & [ u8 ] ) -> Option < & [ u8 ] > {
183- stdout
203+ fn heuristic_should_panic ( stdout : & [ u8 ] ) -> Option < ByteSubslice < ' _ > > {
204+ let line = stdout
184205 . 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 } )
186220}
187221
188- fn heuristic_stack_trace ( stderr : & [ u8 ] ) -> Option < & [ u8 ] > {
222+ fn heuristic_stack_trace ( stderr : & [ u8 ] ) -> Option < ByteSubslice < ' _ > > {
189223 let panicked_at_match = PANICKED_AT_REGEX . find ( stderr) ?;
190224 // If the previous line starts with "Error: ", grab it as well -- it contains the error with
191225 // result-based test failures.
@@ -200,17 +234,24 @@ fn heuristic_stack_trace(stderr: &[u8]) -> Option<&[u8]> {
200234 // TODO: this grabs too much -- it is possible that destructors print out further messages so we
201235 // should be more careful. But it's hard to tell what's printed by the panic and what's printed
202236 // 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+ } )
204241}
205242
206- fn heuristic_error_str ( stderr : & [ u8 ] ) -> Option < & [ u8 ] > {
243+ fn heuristic_error_str ( stderr : & [ u8 ] ) -> Option < ByteSubslice < ' _ > > {
207244 // Starting Rust 1.66, Result-based errors simply print out "Error: ".
208245 let error_match = ERROR_REGEX . find ( stderr) ?;
209246 let start = error_match. start ( ) ;
247+
210248 // TODO: this grabs too much -- it is possible that destructors print out further messages so we
211249 // should be more careful. But it's hard to tell what's printed by the error and what's printed
212250 // 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+ } )
214255}
215256
216257/// 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;
257298 for ( input, output) in tests {
258299 let extracted = heuristic_should_panic ( input. as_bytes ( ) )
259300 . 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+ ) ;
261309 }
262310 }
263311
@@ -379,9 +427,16 @@ some more text at the end, followed by some newlines"#,
379427 ] ;
380428
381429 for ( input, output) in tests {
382- let trace = heuristic_stack_trace ( input. as_bytes ( ) )
430+ let extracted = heuristic_stack_trace ( input. as_bytes ( ) )
383431 . 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+ ) ;
385440 }
386441 }
387442
@@ -393,9 +448,16 @@ some more text at the end, followed by some newlines"#,
393448 ) ] ;
394449
395450 for ( input, output) in tests {
396- let error_str =
451+ let extracted =
397452 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+ ) ;
399461 }
400462 }
401463
0 commit comments