@@ -5,29 +5,47 @@ pub use self::imp::read2;
5
5
use std:: io;
6
6
use std:: process:: { Child , Output } ;
7
7
8
- pub fn read2_abbreviated ( mut child : Child ) -> io:: Result < Output > {
8
+ pub fn read2_abbreviated ( mut child : Child , exclude_from_len : & [ String ] ) -> io:: Result < Output > {
9
9
use io:: Write ;
10
10
use std:: mem:: replace;
11
11
12
12
const HEAD_LEN : usize = 160 * 1024 ;
13
13
const TAIL_LEN : usize = 256 * 1024 ;
14
14
15
15
enum ProcOutput {
16
- Full ( Vec < u8 > ) ,
16
+ Full { bytes : Vec < u8 > , excluded_len : usize } ,
17
17
Abbreviated { head : Vec < u8 > , skipped : usize , tail : Box < [ u8 ] > } ,
18
18
}
19
19
20
20
impl ProcOutput {
21
- fn extend ( & mut self , data : & [ u8 ] ) {
21
+ fn extend ( & mut self , data : & [ u8 ] , exclude_from_len : & [ String ] ) {
22
22
let new_self = match * self {
23
- ProcOutput :: Full ( ref mut bytes) => {
23
+ ProcOutput :: Full { ref mut bytes, ref mut excluded_len } => {
24
24
bytes. extend_from_slice ( data) ;
25
+
26
+ // We had problems in the past with tests failing only in some environments,
27
+ // due to the length of the base path pushing the output size over the limit.
28
+ //
29
+ // To make those failures deterministic across all environments we ignore known
30
+ // paths when calculating the string length, while still including the full
31
+ // path in the output. This could result in some output being larger than the
32
+ // threshold, but it's better than having nondeterministic failures.
33
+ for pattern in exclude_from_len {
34
+ let pattern_bytes = pattern. as_bytes ( ) ;
35
+ let matches = data
36
+ . windows ( pattern_bytes. len ( ) )
37
+ . filter ( |window| window == & pattern_bytes)
38
+ . count ( ) ;
39
+ * excluded_len += matches * pattern_bytes. len ( ) ;
40
+ }
41
+
25
42
let new_len = bytes. len ( ) ;
26
- if new_len <= HEAD_LEN + TAIL_LEN {
43
+ if new_len. saturating_sub ( * excluded_len ) <= HEAD_LEN + TAIL_LEN {
27
44
return ;
28
45
}
29
- let tail = bytes. split_off ( new_len - TAIL_LEN ) . into_boxed_slice ( ) ;
30
- let head = replace ( bytes, Vec :: new ( ) ) ;
46
+
47
+ let mut head = replace ( bytes, Vec :: new ( ) ) ;
48
+ let tail = head. split_off ( new_len - TAIL_LEN ) . into_boxed_slice ( ) ;
31
49
let skipped = new_len - HEAD_LEN - TAIL_LEN ;
32
50
ProcOutput :: Abbreviated { head, skipped, tail }
33
51
}
@@ -47,7 +65,7 @@ pub fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
47
65
48
66
fn into_bytes ( self ) -> Vec < u8 > {
49
67
match self {
50
- ProcOutput :: Full ( bytes) => bytes,
68
+ ProcOutput :: Full { bytes, .. } => bytes,
51
69
ProcOutput :: Abbreviated { mut head, skipped, tail } => {
52
70
write ! ( & mut head, "\n \n <<<<<< SKIPPED {} BYTES >>>>>>\n \n " , skipped) . unwrap ( ) ;
53
71
head. extend_from_slice ( & tail) ;
@@ -57,15 +75,15 @@ pub fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
57
75
}
58
76
}
59
77
60
- let mut stdout = ProcOutput :: Full ( Vec :: new ( ) ) ;
61
- let mut stderr = ProcOutput :: Full ( Vec :: new ( ) ) ;
78
+ let mut stdout = ProcOutput :: Full { bytes : Vec :: new ( ) , excluded_len : 0 } ;
79
+ let mut stderr = ProcOutput :: Full { bytes : Vec :: new ( ) , excluded_len : 0 } ;
62
80
63
81
drop ( child. stdin . take ( ) ) ;
64
82
read2 (
65
83
child. stdout . take ( ) . unwrap ( ) ,
66
84
child. stderr . take ( ) . unwrap ( ) ,
67
85
& mut |is_stdout, data, _| {
68
- if is_stdout { & mut stdout } else { & mut stderr } . extend ( data) ;
86
+ if is_stdout { & mut stdout } else { & mut stderr } . extend ( data, exclude_from_len ) ;
69
87
data. clear ( ) ;
70
88
} ,
71
89
) ?;
0 commit comments