Skip to content

Commit

Permalink
Added support for negative jobs values (#523)
Browse files Browse the repository at this point in the history
Closes #495.
  • Loading branch information
OLUWAMUYIWA committed Sep 21, 2022
1 parent db282ed commit 09cd62c
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 10 deletions.
8 changes: 7 additions & 1 deletion cargo-nextest/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ pub struct TestRunnerOpts {
visible_alias = "jobs",
value_name = "THREADS",
conflicts_with_all = &["no-capture", "no-run"],
env = "NEXTEST_TEST_THREADS"
env = "NEXTEST_TEST_THREADS",
allow_hyphen_values = true
)]
test_threads: Option<TestThreads>,

Expand Down Expand Up @@ -1353,6 +1354,9 @@ mod tests {
// Test binary arguments
// ---
"cargo nextest run -- --a an arbitary arg",
// Test negative test threads
"cargo nextest run --jobs -3",
"cargo nextest run --jobs 3",
];

let invalid: &[(&'static str, ErrorKind)] = &[
Expand Down Expand Up @@ -1467,6 +1471,8 @@ mod tests {
"cargo nextest run --archive-file foo --target-dir-remap bar",
ArgumentConflict,
),
// Invalid test threads: 0
("cargo nextest run --jobs 0", ValueValidation),
];

for valid_args in valid {
Expand Down
95 changes: 88 additions & 7 deletions nextest-runner/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use config::{builder::DefaultState, Config, ConfigBuilder, File, FileFormat, Fil
use guppy::graph::PackageGraph;
use nextest_filtering::{FilteringExpr, TestQuery};
use serde::{de::IntoDeserializer, Deserialize};
use std::{collections::HashMap, fmt, num::NonZeroUsize, str::FromStr, time::Duration};
use std::{
cmp::Ordering, collections::HashMap, fmt, num::NonZeroUsize, str::FromStr, time::Duration,
};

/// Overall configuration for nextest.
///
Expand Down Expand Up @@ -548,11 +550,18 @@ impl FromStr for TestThreads {

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "num-cpus" {
Ok(Self::NumCpus)
} else if let Ok(threads) = s.parse::<usize>() {
Ok(Self::Count(threads))
} else {
Err(TestThreadsParseError::new(s))
return Ok(Self::NumCpus);
}

match s.parse::<isize>() {
Err(e) => Err(TestThreadsParseError::new(format!(
"Error: {e} parsing {s}"
))),
Ok(0) => Err(TestThreadsParseError::new("jobs may not be 0")),
Ok(j) if j < 0 => Ok(TestThreads::Count(
(num_cpus::get() as isize + j).max(1) as usize
)),
Ok(j) => Ok(TestThreads::Count(j as usize)),
}
}
}
Expand Down Expand Up @@ -590,7 +599,16 @@ impl<'de> Deserialize<'de> for TestThreads {
where
E: serde::de::Error,
{
Ok(TestThreads::Count(v as usize))
match v.cmp(&0) {
Ordering::Greater => Ok(TestThreads::Count(v as usize)),
Ordering::Less => Ok(TestThreads::Count(
(num_cpus::get() as i64 + v).max(1) as usize
)),
Ordering::Equal => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Signed(v),
&self,
)),
}
}
}

Expand Down Expand Up @@ -1254,6 +1272,69 @@ mod tests {
);
}

#[test_case(
indoc! {r#"
[profile.custom]
test-threads = -1
"#},
None
; "negative_j"
)]
#[test_case(
indoc! {r#"
[profile.custom]
test-threads = 2
"#},
Some(2)
; "positive_j"
)]
#[test_case(
indoc! {r#"
[profile.custom]
test-threads = 0
"#},
Some(0)
; "zero_j"
)]
fn parse_test_threads_negative_jobs(config_contents: &str, n_threads: Option<usize>) {
let workspace_dir = tempdir().unwrap();
let workspace_path: &Utf8Path = workspace_dir.path().try_into().unwrap();

let graph = temp_workspace(workspace_path, config_contents);

let config = NextestConfig::from_sources(graph.workspace().root(), &graph, None, []);
match n_threads {
Some(0) => assert!(config.is_err()),
Some(j) => assert_eq!(
config
.unwrap()
.profile("custom")
.unwrap()
.custom_profile
.unwrap()
.test_threads
.unwrap()
.compute(),
j
),
None => assert_eq!(
config
.unwrap()
.profile("custom")
.unwrap()
.custom_profile
.unwrap()
.test_threads
.unwrap()
.compute(),
num_cpus::get() - 1
),
}
}

fn temp_workspace(temp_dir: &Utf8Path, config_contents: &str) -> PackageGraph {
Command::new(cargo_path())
.args(["init", "--lib", "--name=test-package"])
Expand Down
6 changes: 4 additions & 2 deletions site/help-text/run-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ OPTIONS:

RUNNER OPTIONS:
--no-run Compile, but don't run tests
-j, --test-threads <THREADS> Number of tests to run simultaneously [possible values: integer
or "num-cpus"] [env: NEXTEST_TEST_THREADS=] [aliases: jobs]
-j, --test-threads <THREADS> Number of tests to run simultaneously [possible values: integer or "num-cpus".
If value is negative, it is set to the number of logical CPUs plus provided value.
Zero givers an error]
[env: NEXTEST_TEST_THREADS=] [aliases: jobs]
--retries <RETRIES> Number of retries for failing tests [default: from profile]
[env: NEXTEST_RETRIES=]
--fail-fast Cancel test run on the first failure
Expand Down

0 comments on commit 09cd62c

Please sign in to comment.