Skip to content

Commit 3e420ec

Browse files
authored
xtask: Allow builds/runs of multiple tests at once (esp-rs#4678)
* Allow multiple test runs * address reviews + protection against `--test ""`/ `--test " "` * Update comment for value parser * reimplement empty strings rejection, allow spaces between tests
1 parent 5f7098e commit 3e420ec

File tree

2 files changed

+62
-40
lines changed

2 files changed

+62
-40
lines changed

xtask/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ option, while the other will select `multiple-integrated`.
153153
You can specifiy a test or example by file, or by configuration. If the
154154
parameters match multiple files, they will be built or executed in sucession.
155155

156-
For example, running `cargo xtask run-tests esp32 embassy_test` will run both
156+
For example, running `cargo xtask run tests esp32 embassy_test` will run both
157157
`embassy_test_single_integrated` and `embassy_test_multiple_integrated`, but you can also
158-
run `cargo xtask run-tests esp32 embassy_test_multiple_integrated` to select only one.
158+
run `cargo xtask run tests esp32 embassy_test_multiple_integrated` to select only one.
159159

160-
In this example, running the `cargo xtask build-tests esp32 embassy_test` command creates an
160+
In this example, running the `cargo xtask build tests esp32 embassy_test` command creates an
161161
`embassy_test_single_integrated` and an `embassy_test_multiple_integrated` binary.

xtask/src/commands/mod.rs

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ pub struct TestsArgs {
7070
pub repeat: usize,
7171
/// Optional test to act on (all tests used if omitted).
7272
///
73-
/// The `test_suite::test_name` syntax allows running a single specific test.
74-
#[arg(long, short = 't')]
75-
pub test: Option<String>,
73+
/// Multiple tests may be selected via a comma-separated list, e.g. `--test rmt,i2c,uart`.
74+
/// The `test_suite::test_name` syntax allows selecting a specific test (and may be combined
75+
/// with commas).
76+
#[arg(long, short = 't', alias = "tests", value_delimiter = ',', num_args = 1..)]
77+
pub test: Option<Vec<String>>,
7678

7779
/// The toolchain used to build the tests
7880
#[arg(long)]
@@ -212,15 +214,6 @@ pub fn examples(workspace: &Path, mut args: ExamplesArgs, action: CargoAction) -
212214

213215
/// Execute the given action on the specified HIL tests.
214216
pub fn tests(workspace: &Path, args: TestsArgs, action: CargoAction) -> Result<()> {
215-
let (test_arg, filter) = if let Some(test_arg) = args.test.as_deref() {
216-
match test_arg.split_once("::") {
217-
Some((test, filter)) => (Some(test), Some(filter)),
218-
None => (Some(test_arg), None),
219-
}
220-
} else {
221-
(None, None)
222-
};
223-
224217
// Absolute path of the 'hil-test' package's root:
225218
let package_path = crate::windows_safe_path(&workspace.join("hil-test"));
226219

@@ -236,11 +229,6 @@ pub fn tests(workspace: &Path, args: TestsArgs, action: CargoAction) -> Result<(
236229
// Sort all tests by name:
237230
tests.sort_by_key(|a| a.binary_name());
238231

239-
let run_test_extra_args = (action == CargoAction::Run)
240-
.then(|| filter.as_ref().map(|f| std::slice::from_ref(f)))
241-
.flatten()
242-
.unwrap_or(&[]);
243-
244232
if let CargoAction::Build(Some(out_dir)) = &action {
245233
// Make sure the tmp directory has no garbage for us.
246234
let tmp_dir = out_dir.join("tmp");
@@ -250,26 +238,59 @@ pub fn tests(workspace: &Path, args: TestsArgs, action: CargoAction) -> Result<(
250238

251239
let mut commands = CargoCommandBatcher::new();
252240
// Execute the specified action:
253-
if tests.iter().any(|test| test.matches(test_arg.as_deref())) {
254-
for test in tests
255-
.iter()
256-
.filter(|test| test.matches(test_arg.as_deref()))
257-
{
258-
let command = crate::generate_build_command(
259-
&package_path,
260-
args.chip,
261-
&target,
262-
&test,
263-
action.clone(),
264-
false,
265-
args.toolchain.as_deref(),
266-
args.timings,
267-
run_test_extra_args,
268-
)?;
269-
commands.push(command);
241+
// If user sets some specific test(s)
242+
if let Some(test_arg) = args.test.as_deref() {
243+
let trimmed: Vec<&str> = test_arg.iter().map(|s| s.trim()).collect();
244+
245+
// Reject `--test ""` / `--test " "`
246+
if trimmed.iter().all(|s| s.is_empty()) {
247+
bail!("Empty test selector is not allowed.");
248+
}
249+
250+
let mut selected_failed: Vec<String> = Vec::new();
251+
252+
for selected in trimmed.into_iter().filter(|s| !s.is_empty()) {
253+
let (test_arg, filter) = match selected.split_once("::") {
254+
Some((test, filter)) => (Some(test), Some(filter)),
255+
None => (Some(selected), None),
256+
};
257+
258+
let run_test_extra_args = (action == CargoAction::Run)
259+
.then(|| filter.as_ref().map(|f| std::slice::from_ref(f)))
260+
.flatten()
261+
.unwrap_or(&[]);
262+
263+
let matched: Vec<_> = tests
264+
.iter()
265+
.filter(|t| t.matches(test_arg.as_deref()))
266+
.collect();
267+
268+
if matched.is_empty() {
269+
selected_failed.push(selected.to_string());
270+
} else {
271+
for test in matched {
272+
let command = crate::generate_build_command(
273+
&package_path,
274+
args.chip,
275+
&target,
276+
test,
277+
action.clone(),
278+
false,
279+
args.toolchain.as_deref(),
280+
args.timings,
281+
run_test_extra_args,
282+
)?;
283+
commands.push(command);
284+
}
285+
}
286+
}
287+
288+
if !selected_failed.is_empty() {
289+
bail!(
290+
"Test not found or unsupported for the given chip: {}",
291+
selected_failed.join(", ")
292+
)
270293
}
271-
} else if test_arg.is_some() {
272-
bail!("Test not found or unsupported for the given chip")
273294
} else {
274295
for test in tests {
275296
let command = crate::generate_build_command(
@@ -281,11 +302,12 @@ pub fn tests(workspace: &Path, args: TestsArgs, action: CargoAction) -> Result<(
281302
false,
282303
args.toolchain.as_deref(),
283304
args.timings,
284-
run_test_extra_args,
305+
&[],
285306
)?;
286307
commands.push(command);
287308
}
288309
}
310+
289311
let mut failed = Vec::new();
290312

291313
for c in commands.build(false) {

0 commit comments

Comments
 (0)