Skip to content

Commit a50803c

Browse files
committed
[nextest-runner] initial support for recording runs
Gated behind an environment variable.
1 parent 92522ce commit a50803c

File tree

17 files changed

+1719
-45
lines changed

17 files changed

+1719
-45
lines changed

Cargo.lock

Lines changed: 47 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cargo-nextest/src/dispatch.rs

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ use nextest_runner::{
3030
platform::BuildPlatforms,
3131
reporter::{structured, FinalStatusLevel, StatusLevel, TestOutputDisplay, TestReporterBuilder},
3232
reuse_build::{archive_to_file, ArchiveReporter, MetadataOrPath, PathMapper, ReuseBuildInfo},
33+
run_store::RunStore,
3334
runner::{configure_handle_inheritance, RunStatsFailureKind, TestRunnerBuilder},
3435
show_config::{ShowNextestVersion, ShowTestGroupSettings, ShowTestGroups, ShowTestGroupsMode},
3536
signal::SignalHandlerKind,
3637
target_runner::{PlatformRunner, TargetRunner},
3738
test_filter::{RunIgnored, TestFilterBuilder},
39+
test_output::CaptureStrategy,
3840
};
3941
use once_cell::sync::OnceCell;
4042
use owo_colors::{OwoColorize, Stream, Style};
@@ -743,10 +745,7 @@ pub struct TestRunnerOpts {
743745
}
744746

745747
impl TestRunnerOpts {
746-
fn to_builder(
747-
&self,
748-
cap_strat: nextest_runner::test_output::CaptureStrategy,
749-
) -> Option<TestRunnerBuilder> {
748+
fn to_builder(&self, cap_strat: CaptureStrategy) -> Option<TestRunnerBuilder> {
750749
if self.no_run {
751750
return None;
752751
}
@@ -1557,7 +1556,6 @@ impl App {
15571556
structured_reporter.set_libtest(libtest);
15581557
}
15591558
};
1560-
use nextest_runner::test_output::CaptureStrategy;
15611559

15621560
let cap_strat = if no_capture {
15631561
CaptureStrategy::None
@@ -1584,19 +1582,6 @@ impl App {
15841582
let output = output_writer.reporter_output();
15851583
let profile = profile.apply_build_platforms(&build_platforms);
15861584

1587-
let mut reporter = reporter_opts
1588-
.to_builder(no_capture)
1589-
.set_verbose(self.base.output.verbose)
1590-
.build(&test_list, &profile, output, structured_reporter);
1591-
if self
1592-
.base
1593-
.output
1594-
.color
1595-
.should_colorize(supports_color::Stream::Stderr)
1596-
{
1597-
reporter.colorize();
1598-
}
1599-
16001585
let handler = SignalHandlerKind::Standard;
16011586
let runner_builder = match runner_opts.to_builder(cap_strat) {
16021587
Some(runner_builder) => runner_builder,
@@ -1615,11 +1600,51 @@ impl App {
16151600
target_runner.clone(),
16161601
)?;
16171602

1603+
// Start recording runs if the environment variable is set.
1604+
{
1605+
const EXPERIMENTAL_ENV: &str = "NEXTEST_EXPERIMENTAL_RECORD_RUNS";
1606+
if std::env::var(EXPERIMENTAL_ENV).as_deref() == Ok("1") {
1607+
// For the record reporter, use the global store dir to share runs across profiles.
1608+
let store = RunStore::new(profile.global_store_dir())
1609+
.map_err(|err| ExpectedError::RunRecordError { err })?;
1610+
let locked_store = store
1611+
.lock_exclusive()
1612+
.map_err(|err| ExpectedError::RunRecordError { err })?;
1613+
let recorder = locked_store
1614+
.create_run_recorder(
1615+
runner.run_id(),
1616+
self.base.current_version.clone(),
1617+
runner.started_at().fixed_offset(),
1618+
)
1619+
.map_err(|err| ExpectedError::RunRecordError { err })?;
1620+
1621+
let record = structured::RecordReporter::new(recorder);
1622+
structured_reporter.set_record(record);
1623+
}
1624+
}
1625+
1626+
let mut reporter = reporter_opts
1627+
.to_builder(no_capture)
1628+
.set_verbose(self.base.output.verbose)
1629+
.build(&test_list, &profile, output, structured_reporter);
1630+
if self
1631+
.base
1632+
.output
1633+
.color
1634+
.should_colorize(supports_color::Stream::Stderr)
1635+
{
1636+
reporter.colorize();
1637+
}
1638+
16181639
configure_handle_inheritance(no_capture)?;
1640+
reporter.report_meta(&self.base.cargo_metadata_json, &test_list);
1641+
16191642
let run_stats = runner.try_execute(|event| {
16201643
// Write and flush the event.
16211644
reporter.report_event(event)
16221645
})?;
1646+
reporter.finish()?;
1647+
16231648
self.base
16241649
.check_version_config_final(version_only_config.nextest_version())?;
16251650
if !run_stats.is_success() {

cargo-nextest/src/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ pub enum ExpectedError {
141141
#[from]
142142
err: FromMessagesError,
143143
},
144+
#[error("error recording test run")]
145+
RunRecordError {
146+
#[source]
147+
err: RunStoreError,
148+
},
144149
#[error("create test list error")]
145150
CreateTestListError {
146151
#[source]
@@ -386,6 +391,7 @@ impl ExpectedError {
386391
| Self::TestFilterBuilderError { .. }
387392
| Self::UnknownHostPlatform { .. }
388393
| Self::ArgumentFileReadError { .. }
394+
| Self::RunRecordError { .. }
389395
| Self::UnknownArchiveFormat { .. }
390396
| Self::ArchiveExtractError { .. }
391397
| Self::RustBuildMetaParseError { .. }
@@ -700,6 +706,10 @@ impl ExpectedError {
700706
log::error!("failed to parse messages generated by Cargo");
701707
Some(err as &dyn Error)
702708
}
709+
Self::RunRecordError { err } => {
710+
log::error!("error recording run");
711+
Some(err as &dyn Error)
712+
}
703713
Self::CreateTestListError { err } => {
704714
log::error!("creating test list failed");
705715
Some(err as &dyn Error)

nextest-metadata/src/test_list.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ impl TestListSummary {
146146
rust_suites: BTreeMap::new(),
147147
}
148148
}
149+
149150
/// Parse JSON output from `cargo nextest list --message-format json`.
150151
pub fn parse_json(json: impl AsRef<str>) -> Result<Self, serde_json::Error> {
151152
serde_json::from_str(json.as_ref())

nextest-runner/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ config = { version = "0.13.4", default-features = false, features = [
2828
] }
2929
cargo_metadata = "0.18.1"
3030
cfg-if = "1.0.0"
31-
chrono = "0.4.33"
31+
chrono = { version = "0.4.33", features = ["serde"] }
3232
debug-ignore = "1.0.5"
3333
display-error-chain = "0.2.0"
3434
either = "1.9.0"
3535
futures = "0.3.30"
36+
fs4 = "0.7.0"
3637
guppy = "0.17.4"
3738
# Used to find the cargo root directory, which is needed in case the user has
3839
# added a config.toml there
@@ -77,6 +78,7 @@ tokio = { version = "1.35.1", features = [
7778
toml = "0.8.8"
7879
toml_edit = { version = "0.21.0", features = ["serde"] }
7980
xxhash-rust = { version = "0.8.8", features = ["xxh64"] }
81+
zip = { version = "0.6.6", default-features = false, features = ["zstd"] }
8082
zstd = { version = "0.13.0", features = ["zstdmt"] }
8183

8284
###
@@ -93,7 +95,7 @@ self_update = { version = "0.39.0", optional = true, default-features = false, f
9395
nextest-filtering = { version = "0.7.1", path = "../nextest-filtering" }
9496
nextest-metadata = { version = "0.10.0", path = "../nextest-metadata" }
9597
quick-junit = { version = "0.3.5", path = "../quick-junit" }
96-
uuid = { version = "1.7.0", features = ["v4"] }
98+
uuid = { version = "1.7.0", features = ["v4", "serde"] }
9799
console-subscriber = { version = "0.2.0", optional = true }
98100
unicode-ident = "1.0.12"
99101
unicode-normalization = "0.1.22"

0 commit comments

Comments
 (0)