Skip to content

Commit cf95a0b

Browse files
committed
feat(harness): Add argfile support
1 parent ca86776 commit cf95a0b

4 files changed

Lines changed: 103 additions & 84 deletions

File tree

DESIGN.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,15 @@ Unsure which of those would be slower and how the different characteristics matc
156156

157157
Rather than build into every harness shuffle, sharding, and any other specific logic like that,
158158
we can instead give the user direct control over the test order by the order they are specified on the command line.
159+
160+
### Decision: argfile support
161+
162+
Similar to filters changing the order of tests,
163+
argfile support allows for passing a large list of arguments to a test binary.
164+
165+
The syntax and semantics match rustc:
166+
- Expanded before parsing, independent of any other syntax
167+
- Arguments are delimited by newlines; no shell escaping
168+
- rustc has unstable support for `@shell:<path>`
169+
- Lines are read literal, empty lines are empty arguments and no comments
170+
- Non-recursive

crates/libtest2-harness/src/harness.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ use libtest_lexarg::OutputFormat;
33
use crate::{cli, notify, Case, RunError, RunMode, State};
44

55
pub struct Harness {
6-
raw: Vec<std::ffi::OsString>,
6+
raw: std::io::Result<Vec<std::ffi::OsString>>,
77
cases: Vec<Box<dyn Case>>,
88
}
99

1010
impl Harness {
1111
pub fn with_args(args: impl IntoIterator<Item = impl Into<std::ffi::OsString>>) -> Self {
12-
let raw = args.into_iter().map(|s| s.into()).collect::<Vec<_>>();
12+
let raw = expand_args(args);
1313
Self { raw, cases: vec![] }
1414
}
1515

1616
pub fn with_env() -> Self {
17-
let raw = std::env::args_os().collect::<Vec<_>>();
17+
let raw = std::env::args_os();
18+
let raw = expand_args(raw);
1819
Self { raw, cases: vec![] }
1920
}
2021

@@ -31,7 +32,14 @@ impl Harness {
3132
}
3233

3334
pub fn main(mut self) -> ! {
34-
let mut parser = cli::Parser::new(&self.raw);
35+
let raw = match self.raw {
36+
Ok(raw) => raw,
37+
Err(err) => {
38+
eprintln!("{err}");
39+
std::process::exit(1)
40+
}
41+
};
42+
let mut parser = cli::Parser::new(&raw);
3543
let opts = parse(&mut parser).unwrap_or_else(|err| {
3644
eprintln!("{err}");
3745
std::process::exit(1)
@@ -131,6 +139,27 @@ fn parse<'p>(
131139
Ok(opts)
132140
}
133141

142+
fn expand_args(
143+
args: impl IntoIterator<Item = impl Into<std::ffi::OsString>>,
144+
) -> std::io::Result<Vec<std::ffi::OsString>> {
145+
let mut expanded = Vec::new();
146+
for arg in args {
147+
let arg = arg.into();
148+
if let Some(argfile) = arg.to_str().and_then(|s| s.strip_prefix("@")) {
149+
expanded.extend(parse_argfile(std::path::Path::new(argfile))?);
150+
} else {
151+
expanded.push(arg);
152+
}
153+
}
154+
Ok(expanded)
155+
}
156+
157+
fn parse_argfile(path: &std::path::Path) -> std::io::Result<Vec<std::ffi::OsString>> {
158+
// Logic taken from rust-lang/rust's `compiler/rustc_driver_impl/src/args.rs`
159+
let content = std::fs::read_to_string(path)?;
160+
Ok(content.lines().map(|s| s.into()).collect())
161+
}
162+
134163
fn notifier(opts: &libtest_lexarg::TestOpts) -> std::io::Result<Box<dyn notify::Notifier>> {
135164
#[cfg(feature = "color")]
136165
let stdout = anstream::stdout();

crates/libtest2-mimic/tests/testsuite/argfile.rs

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,22 @@ fn empty() {
5959
0,
6060
str![[r#"
6161
62-
running 0 tests
62+
running 4 tests
63+
test one ... ok
64+
test one_two ... ok
65+
test three ... ok
66+
test two ... ok
6367
64-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
68+
test result: ok. 4 passed; 0 failed; 0 ignored; 0 filtered out; finished in [..]s
6569
6670
6771
"#]],
6872
str![[r#"
6973
70-
running 0 tests
74+
running 4 tests
7175
...
7276
73-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
77+
test result: ok. 4 passed; 0 failed; 0 ignored; 0 filtered out; finished in [..]s
7478
7579
7680
"#]],
@@ -85,19 +89,21 @@ fn list() {
8589
&argfile,
8690
0,
8791
str![[r#"
92+
one: test
93+
one_two: test
94+
three: test
95+
two: test
8896
89-
running 0 tests
90-
91-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
97+
4 tests
9298
9399
94100
"#]],
95101
str![[r#"
96-
97-
running 0 tests
102+
one: test
103+
one_two: test
98104
...
99105
100-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
106+
4 tests
101107
102108
103109
"#]],
@@ -119,18 +125,20 @@ two
119125
0,
120126
str![[r#"
121127
122-
running 0 tests
128+
running 2 tests
129+
test one ... ok
130+
test two ... ok
123131
124-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
132+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
125133
126134
127135
"#]],
128136
str![[r#"
129137
130-
running 0 tests
138+
running 2 tests
131139
...
132140
133-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
141+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
134142
135143
136144
"#]],
@@ -149,18 +157,20 @@ two",
149157
0,
150158
str![[r#"
151159
152-
running 0 tests
160+
running 2 tests
161+
test one ... ok
162+
test two ... ok
153163
154-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
164+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
155165
156166
157167
"#]],
158168
str![[r#"
159169
160-
running 0 tests
170+
running 2 tests
161171
...
162172
163-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
173+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
164174
165175
166176
"#]],
@@ -170,26 +180,5 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]
170180
#[test]
171181
fn invalid() {
172182
let argfile = std::path::Path::new("highly-improbably-non-existent-file.txt");
173-
check(
174-
&[],
175-
argfile,
176-
0,
177-
str![[r#"
178-
179-
running 0 tests
180-
181-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
182-
183-
184-
"#]],
185-
str![[r#"
186-
187-
running 0 tests
188-
...
189-
190-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
191-
192-
193-
"#]],
194-
);
183+
check(&[], argfile, 1, str![""], str![""]);
195184
}

crates/libtest2/tests/testsuite/argfile.rs

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,22 @@ fn empty() {
6464
0,
6565
str![[r#"
6666
67-
running 0 tests
67+
running 4 tests
68+
test one ... ok
69+
test one_two ... ok
70+
test three ... ok
71+
test two ... ok
6872
69-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
73+
test result: ok. 4 passed; 0 failed; 0 ignored; 0 filtered out; finished in [..]s
7074
7175
7276
"#]],
7377
str![[r#"
7478
75-
running 0 tests
79+
running 4 tests
7680
...
7781
78-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
82+
test result: ok. 4 passed; 0 failed; 0 ignored; 0 filtered out; finished in [..]s
7983
8084
8185
"#]],
@@ -90,19 +94,21 @@ fn list() {
9094
&argfile,
9195
0,
9296
str![[r#"
97+
one: test
98+
one_two: test
99+
three: test
100+
two: test
93101
94-
running 0 tests
95-
96-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
102+
4 tests
97103
98104
99105
"#]],
100106
str![[r#"
101-
102-
running 0 tests
107+
one: test
108+
one_two: test
103109
...
104110
105-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
111+
4 tests
106112
107113
108114
"#]],
@@ -124,18 +130,20 @@ two
124130
0,
125131
str![[r#"
126132
127-
running 0 tests
133+
running 2 tests
134+
test one ... ok
135+
test two ... ok
128136
129-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
137+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
130138
131139
132140
"#]],
133141
str![[r#"
134142
135-
running 0 tests
143+
running 2 tests
136144
...
137145
138-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
146+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
139147
140148
141149
"#]],
@@ -154,18 +162,20 @@ two",
154162
0,
155163
str![[r#"
156164
157-
running 0 tests
165+
running 2 tests
166+
test one ... ok
167+
test two ... ok
158168
159-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
169+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
160170
161171
162172
"#]],
163173
str![[r#"
164174
165-
running 0 tests
175+
running 2 tests
166176
...
167177
168-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
178+
test result: ok. 2 passed; 0 failed; 0 ignored; 2 filtered out; finished in [..]s
169179
170180
171181
"#]],
@@ -175,26 +185,5 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]
175185
#[test]
176186
fn invalid() {
177187
let argfile = std::path::Path::new("highly-improbably-non-existent-file.txt");
178-
check(
179-
&[],
180-
argfile,
181-
0,
182-
str![[r#"
183-
184-
running 0 tests
185-
186-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
187-
188-
189-
"#]],
190-
str![[r#"
191-
192-
running 0 tests
193-
...
194-
195-
test result: ok. 0 passed; 0 failed; 0 ignored; 4 filtered out; finished in [..]s
196-
197-
198-
"#]],
199-
);
188+
check(&[], argfile, 1, str![""], str![""]);
200189
}

0 commit comments

Comments
 (0)