Skip to content

Commit 54cb257

Browse files
authored
[integration-tests] redact hh:mm:ss durations (#3197)
Fixes an issue identified in #3190 -- this shows up as 00:00:01 elapsed, breaking the snapshot.
1 parent c0ff7bb commit 54cb257

File tree

9 files changed

+128
-72
lines changed

9 files changed

+128
-72
lines changed

cargo-nextest/src/dispatch/core/replay.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use nextest_runner::{
2424
STORE_FORMAT_VERSION, StoreReader, TestEventKindSummary, TestEventSummary,
2525
records_state_dir,
2626
},
27+
redact::Redactor,
2728
reporter::ReporterOutput,
2829
user_config::{UserConfig, UserConfigExperimental},
2930
};
@@ -265,6 +266,9 @@ fn run_replay_common(
265266

266267
let mut reporter_builder = ReplayReporterBuilder::new();
267268
reporter_builder.set_colorize(should_colorize);
269+
if crate::output::should_redact() {
270+
reporter_builder.set_redactor(Redactor::for_snapshot_testing());
271+
}
268272
replay_opts.reporter_opts.apply_to_replay_builder(
269273
&mut reporter_builder,
270274
&user_config.ui,

cargo-nextest/src/dispatch/core/run.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use nextest_runner::{
3434
RecordSession, RecordSessionConfig, RerunRootInfo, RunIdOrRecordingSelector, RunIdSelector,
3535
RunStore, STORE_FORMAT_VERSION, Styles as RecordStyles, records_state_dir,
3636
},
37+
redact::Redactor,
3738
reporter::{
3839
MaxProgressRunning, ReporterBuilder, ShowProgress, ShowTerminalProgress, TestOutputDisplay,
3940
events::{FinalRunStats, RunStats},
@@ -746,6 +747,9 @@ impl ReporterOpts {
746747

747748
builder.set_show_progress(show_progress);
748749
builder.set_max_progress_running(max_progress_running);
750+
if crate::output::should_redact() {
751+
builder.set_redactor(Redactor::for_snapshot_testing());
752+
}
749753
builder
750754
}
751755
}
@@ -787,6 +791,9 @@ impl BenchReporterOpts {
787791
.map(ShowProgress::from)
788792
.unwrap_or(resolved_ui.show_progress.into());
789793
builder.set_show_progress(show_progress);
794+
if crate::output::should_redact() {
795+
builder.set_redactor(Redactor::for_snapshot_testing());
796+
}
790797
builder
791798
}
792799
}

integration-tests/tests/integration/snapshots/integration__record_replay__replay_stress_run.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ expression: "redact_dynamic_fields(&output, temp_root)"
99
Nextest run ID 60000001-0000-0000-0000-000000000001 with nextest profile: default
1010
Starting 1 test across 7 binaries (31 tests skipped)
1111
────────────
12-
Stress test iteration 1/5 (00:00:00 elapsed so far, 5 iterations remaining)
12+
Stress test iteration 1/5 (HH:MM:SS elapsed so far, 5 iterations remaining)
1313
SKIP [ ] [1/5] (───) nextest-tests tests::call_dylib_add_two
1414
SKIP [ ] [1/5] (───) nextest-tests tests::unit_test_success
1515
SKIP [ ] [1/5] (───) nextest-tests::basic test_cargo_env_vars
@@ -44,7 +44,7 @@ expression: "redact_dynamic_fields(&output, temp_root)"
4444
PASS [ duration] [1/5] (1/1) nextest-tests::basic test_success
4545
Stress test [ duration] iteration 1/5: 1 test run: 1 passed, 31 skipped
4646
────────────
47-
Stress test iteration 2/5 (00:00:00 elapsed so far, 4 iterations remaining)
47+
Stress test iteration 2/5 (HH:MM:SS elapsed so far, 4 iterations remaining)
4848
SKIP [ ] [2/5] (───) nextest-tests tests::call_dylib_add_two
4949
SKIP [ ] [2/5] (───) nextest-tests tests::unit_test_success
5050
SKIP [ ] [2/5] (───) nextest-tests::basic test_cargo_env_vars
@@ -79,7 +79,7 @@ expression: "redact_dynamic_fields(&output, temp_root)"
7979
PASS [ duration] [2/5] (1/1) nextest-tests::basic test_success
8080
Stress test [ duration] iteration 2/5: 1 test run: 1 passed, 31 skipped
8181
────────────
82-
Stress test iteration 3/5 (00:00:00 elapsed so far, 3 iterations remaining)
82+
Stress test iteration 3/5 (HH:MM:SS elapsed so far, 3 iterations remaining)
8383
SKIP [ ] [3/5] (───) nextest-tests tests::call_dylib_add_two
8484
SKIP [ ] [3/5] (───) nextest-tests tests::unit_test_success
8585
SKIP [ ] [3/5] (───) nextest-tests::basic test_cargo_env_vars
@@ -114,7 +114,7 @@ expression: "redact_dynamic_fields(&output, temp_root)"
114114
PASS [ duration] [3/5] (1/1) nextest-tests::basic test_success
115115
Stress test [ duration] iteration 3/5: 1 test run: 1 passed, 31 skipped
116116
────────────
117-
Stress test iteration 4/5 (00:00:00 elapsed so far, 2 iterations remaining)
117+
Stress test iteration 4/5 (HH:MM:SS elapsed so far, 2 iterations remaining)
118118
SKIP [ ] [4/5] (───) nextest-tests tests::call_dylib_add_two
119119
SKIP [ ] [4/5] (───) nextest-tests tests::unit_test_success
120120
SKIP [ ] [4/5] (───) nextest-tests::basic test_cargo_env_vars
@@ -149,7 +149,7 @@ expression: "redact_dynamic_fields(&output, temp_root)"
149149
PASS [ duration] [4/5] (1/1) nextest-tests::basic test_success
150150
Stress test [ duration] iteration 4/5: 1 test run: 1 passed, 31 skipped
151151
────────────
152-
Stress test iteration 5/5 (00:00:00 elapsed so far, 1 iteration remaining)
152+
Stress test iteration 5/5 (HH:MM:SS elapsed so far, 1 iteration remaining)
153153
SKIP [ ] [5/5] (───) nextest-tests tests::call_dylib_add_two
154154
SKIP [ ] [5/5] (───) nextest-tests tests::unit_test_success
155155
SKIP [ ] [5/5] (───) nextest-tests::basic test_cargo_env_vars

nextest-runner/src/helpers.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,41 @@ pub(crate) fn rel_path_join(rel_path: &Utf8Path, path: &Utf8Path) -> Utf8PathBuf
633633
#[derive(Debug)]
634634
pub(crate) struct FormattedDuration(pub(crate) Duration);
635635

636+
/// Controls how sub-second precision is handled when formatting durations.
637+
#[derive(Copy, Clone, Debug)]
638+
pub(crate) enum DurationRounding {
639+
/// Truncate sub-second precision (floor). Use for elapsed time.
640+
Floor,
641+
642+
/// Round up to the next second when sub-second milliseconds are present
643+
/// (ceiling). Use for remaining time so that elapsed + remaining doesn't
644+
/// appear to exceed the total.
645+
Ceiling,
646+
}
647+
648+
/// Formats a duration as `HH:MM:SS`.
649+
#[derive(Debug)]
650+
pub(crate) struct FormattedHhMmSs {
651+
pub(crate) duration: Duration,
652+
pub(crate) rounding: DurationRounding,
653+
}
654+
655+
impl fmt::Display for FormattedHhMmSs {
656+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
657+
let total_secs = self.duration.as_secs();
658+
let total_secs = match self.rounding {
659+
DurationRounding::Ceiling if self.duration.subsec_millis() > 0 => total_secs + 1,
660+
_ => total_secs,
661+
};
662+
let secs = total_secs % 60;
663+
let total_mins = total_secs / 60;
664+
let mins = total_mins % 60;
665+
let hours = total_mins / 60;
666+
667+
write!(f, "{hours:02}:{mins:02}:{secs:02}")
668+
}
669+
}
670+
636671
impl fmt::Display for FormattedDuration {
637672
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638673
let duration = self.0.as_secs_f64();

nextest-runner/src/record/replay.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ use crate::{
602602
run_id_index::{RunIdIndex, ShortestRunIdPrefix},
603603
store::{RecordedRunInfo, RecordedRunStatus},
604604
},
605+
redact::Redactor,
605606
reporter::{
606607
DisplayConfig, DisplayReporter, DisplayReporterBuilder, DisplayerKind, FinalStatusLevel,
607608
MaxProgressRunning, OutputLoadDecider, ReporterOutput, ShowProgress, ShowTerminalProgress,
@@ -663,6 +664,7 @@ pub struct ReplayReporterBuilder {
663664
show_progress: ShowProgress,
664665
max_progress_running: MaxProgressRunning,
665666
no_output_indent: bool,
667+
redactor: Redactor,
666668
}
667669

668670
impl Default for ReplayReporterBuilder {
@@ -677,6 +679,7 @@ impl Default for ReplayReporterBuilder {
677679
show_progress: ShowProgress::default(),
678680
max_progress_running: MaxProgressRunning::default(),
679681
no_output_indent: false,
682+
redactor: Redactor::noop(),
680683
}
681684
}
682685
}
@@ -744,6 +747,12 @@ impl ReplayReporterBuilder {
744747
self
745748
}
746749

750+
/// Sets the redactor for snapshot testing.
751+
pub fn set_redactor(&mut self, redactor: Redactor) -> &mut Self {
752+
self.redactor = redactor;
753+
self
754+
}
755+
747756
/// Builds the replay reporter with the given output destination.
748757
pub fn build<'a>(
749758
self,
@@ -771,6 +780,7 @@ impl ReplayReporterBuilder {
771780
// we're replaying events, not running live tests.
772781
show_term_progress: ShowTerminalProgress::No,
773782
displayer_kind: DisplayerKind::Replay,
783+
redactor: self.redactor,
774784
}
775785
.build(output);
776786

nextest-runner/src/redact.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
88
use crate::{
99
helpers::{
10-
FormattedDuration, FormattedRelativeDuration, convert_rel_path_to_forward_slash,
11-
decimal_char_width,
10+
DurationRounding, FormattedDuration, FormattedHhMmSs, FormattedRelativeDuration,
11+
convert_rel_path_to_forward_slash, decimal_char_width,
1212
},
1313
list::RustBuildMeta,
1414
};
@@ -40,6 +40,8 @@ static SIZE_REDACTION: &str = "<size>";
4040
static VERSION_REDACTION: &str = "<version>";
4141
/// Placeholder for redacted relative durations (e.g. "30s ago").
4242
static RELATIVE_DURATION_REDACTION: &str = "<ago>";
43+
/// 8 chars, matches `HH:MM:SS` format.
44+
static HHMMSS_REDACTION: &str = "HH:MM:SS";
4345

4446
/// A helper for redacting data that varies by environment.
4547
///
@@ -50,6 +52,12 @@ pub struct Redactor {
5052
kind: Arc<RedactorKind>,
5153
}
5254

55+
impl Default for Redactor {
56+
fn default() -> Self {
57+
Self::noop()
58+
}
59+
}
60+
5361
impl Redactor {
5462
/// Creates a new no-op redactor.
5563
pub fn noop() -> Self {
@@ -149,6 +157,23 @@ impl Redactor {
149157
}
150158
}
151159

160+
/// Redacts an `HH:MM:SS` duration (used for stress test elapsed/remaining
161+
/// time).
162+
///
163+
/// The placeholder `HH:MM:SS` is 8 characters, matching the width of the
164+
/// zero-padded `%02H:%02M:%02S` format.
165+
pub(crate) fn redact_hhmmss_duration(
166+
&self,
167+
duration: Duration,
168+
rounding: DurationRounding,
169+
) -> RedactorOutput<FormattedHhMmSs> {
170+
if self.kind.is_active() {
171+
RedactorOutput::Redacted(HHMMSS_REDACTION.to_string())
172+
} else {
173+
RedactorOutput::Unredacted(FormattedHhMmSs { duration, rounding })
174+
}
175+
}
176+
152177
/// Returns true if this redactor is active (will redact values).
153178
pub fn is_active(&self) -> bool {
154179
self.kind.is_active()

nextest-runner/src/reporter/displayer/formatters.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,32 +34,6 @@ impl fmt::Display for DisplayBracketedHhMmSs {
3434
}
3535
}
3636

37-
pub(super) struct DisplayHhMmSs {
38-
pub(super) duration: Duration,
39-
// True if floor, false if ceiling.
40-
pub(super) floor: bool,
41-
}
42-
43-
impl fmt::Display for DisplayHhMmSs {
44-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45-
// This matches indicatif's elapsed_precise display.
46-
let total_secs = self.duration.as_secs();
47-
let total_secs = if !self.floor && self.duration.subsec_millis() > 0 {
48-
total_secs + 1
49-
} else {
50-
total_secs
51-
};
52-
let secs = total_secs % 60;
53-
let total_mins = total_secs / 60;
54-
let mins = total_mins % 60;
55-
let hours = total_mins / 60;
56-
57-
// Buffer the output internally to provide padding.
58-
let out = format!("{hours:02}:{mins:02}:{secs:02}");
59-
write!(f, "{out}")
60-
}
61-
}
62-
6337
pub(super) struct DisplayBracketedDuration(pub(super) Duration);
6438

6539
impl fmt::Display for DisplayBracketedDuration {

0 commit comments

Comments
 (0)