@@ -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