Skip to content

Commit a3eedfb

Browse files
committed
[nextest-runner] hide/show progress bar around run paused/continued
This has to be done with some care. There can be multiple reasons for showing or hiding the bar (e.g. run paused in the middle of running a setup script with no-capture) -- ensure that we track all of them and don't just use whatever we saw last.
1 parent 0b61823 commit a3eedfb

File tree

1 file changed

+109
-53
lines changed

1 file changed

+109
-53
lines changed

nextest-runner/src/reporter/displayer.rs

Lines changed: 109 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ impl TestReporterBuilder {
276276
}
277277

278278
ReporterStderr::Terminal => {
279-
let progress_bar = ProgressBar::new(test_list.test_count() as u64);
279+
let bar = ProgressBar::new(test_list.test_count() as u64);
280280
// Emulate Cargo's style.
281281
let test_count_width = format!("{}", test_list.test_count()).len();
282282
// Create the template using the width as input. This is a little confusing -- {{foo}}
@@ -289,7 +289,7 @@ impl TestReporterBuilder {
289289
"{{prefix:>12}} [{{elapsed_precise:>9}}] [{{wide_bar}}] \
290290
{{pos:>{test_count_width}}}/{{len:{test_count_width}}}: {{msg}} "
291291
);
292-
progress_bar.set_style(
292+
bar.set_style(
293293
ProgressStyle::default_bar()
294294
.progress_chars("=> ")
295295
.template(&template)
@@ -300,10 +300,13 @@ impl TestReporterBuilder {
300300
//
301301
// This used to be unbuffered, but that option went away from indicatif 0.17.0. The
302302
// refresh rate is now 20hz so that it's double the steady tick rate.
303-
progress_bar.set_draw_target(ProgressDrawTarget::stderr_with_hz(20));
303+
bar.set_draw_target(ProgressDrawTarget::stderr_with_hz(20));
304304
// Enable a steady tick 10 times a second.
305-
progress_bar.enable_steady_tick(Duration::from_millis(100));
306-
ReporterStderrImpl::TerminalWithBar(progress_bar)
305+
bar.enable_steady_tick(Duration::from_millis(100));
306+
ReporterStderrImpl::TerminalWithBar {
307+
bar,
308+
state: ProgressBarState::new(),
309+
}
307310
}
308311
ReporterStderr::Buffer(buf) => ReporterStderrImpl::Buffer(buf),
309312
};
@@ -337,22 +340,115 @@ impl TestReporterBuilder {
337340
}
338341

339342
enum ReporterStderrImpl<'a> {
340-
TerminalWithBar(ProgressBar),
343+
TerminalWithBar {
344+
bar: ProgressBar,
345+
// Reporter-specific progress bar state.
346+
state: ProgressBarState,
347+
},
341348
TerminalWithoutBar,
342349
Buffer(&'a mut Vec<u8>),
343350
}
344351

345352
impl ReporterStderrImpl<'_> {
346353
fn finish_and_clear_bar(&self) {
347354
match self {
348-
ReporterStderrImpl::TerminalWithBar(bar) => {
355+
ReporterStderrImpl::TerminalWithBar { bar, .. } => {
349356
bar.finish_and_clear();
350357
}
351358
ReporterStderrImpl::TerminalWithoutBar | ReporterStderrImpl::Buffer(_) => {}
352359
}
353360
}
354361
}
355362

363+
#[derive(Debug)]
364+
struct ProgressBarState {
365+
// Reasons for hiding the progress bar. We show the progress bar if none of
366+
// these are set and hide it if any of them are set.
367+
//
368+
// indicatif cannot handle this kind of "stacked" state management so it
369+
// falls on us to do so.
370+
hidden_no_capture: bool,
371+
hidden_run_paused: bool,
372+
}
373+
374+
impl ProgressBarState {
375+
fn new() -> Self {
376+
Self {
377+
hidden_no_capture: false,
378+
hidden_run_paused: false,
379+
}
380+
}
381+
382+
fn should_hide(&self) -> bool {
383+
self.hidden_no_capture || self.hidden_run_paused
384+
}
385+
386+
fn update_progress_bar(
387+
&mut self,
388+
event: &TestEvent<'_>,
389+
styles: &Styles,
390+
progress_bar: &ProgressBar,
391+
) {
392+
let before_should_hide = self.should_hide();
393+
394+
match &event.kind {
395+
TestEventKind::SetupScriptStarted { no_capture, .. } => {
396+
// Hide the progress bar if either stderr or stdout are being passed through.
397+
if *no_capture {
398+
self.hidden_no_capture = true;
399+
}
400+
}
401+
TestEventKind::SetupScriptFinished { no_capture, .. } => {
402+
// Restore the progress bar if it was hidden.
403+
if *no_capture {
404+
self.hidden_no_capture = false;
405+
}
406+
}
407+
TestEventKind::TestStarted {
408+
current_stats,
409+
running,
410+
cancel_state,
411+
..
412+
}
413+
| TestEventKind::TestFinished {
414+
current_stats,
415+
running,
416+
cancel_state,
417+
..
418+
} => {
419+
progress_bar.set_prefix(progress_bar_prefix(current_stats, *cancel_state, styles));
420+
progress_bar.set_message(progress_bar_msg(current_stats, *running, styles));
421+
// If there are skipped tests, the initial run count will be lower than when constructed
422+
// in ProgressBar::new.
423+
progress_bar.set_length(current_stats.initial_run_count as u64);
424+
progress_bar.set_position(current_stats.finished_count as u64);
425+
}
426+
TestEventKind::RunPaused { .. } => {
427+
// Pausing the run should hide the progress bar since we'll exit
428+
// to the terminal immediately after.
429+
self.hidden_run_paused = true;
430+
}
431+
TestEventKind::RunContinued { .. } => {
432+
// Continuing the run should show the progress bar since we'll
433+
// continue to output to it.
434+
self.hidden_run_paused = false;
435+
}
436+
TestEventKind::RunBeginCancel { .. } => {
437+
progress_bar.set_prefix(progress_bar_cancel_prefix(styles));
438+
}
439+
_ => {}
440+
}
441+
442+
let after_should_hide = self.should_hide();
443+
444+
match (before_should_hide, after_should_hide) {
445+
(false, true) => progress_bar.set_draw_target(ProgressDrawTarget::hidden()),
446+
(true, false) => progress_bar.set_draw_target(ProgressDrawTarget::stderr()),
447+
_ => {}
448+
}
449+
}
450+
}
451+
356452
/// Functionality to report test results to stderr, JUnit, and/or structured,
357453
/// machine-readable results to stdout
358454
pub struct TestReporter<'a> {
@@ -387,19 +483,19 @@ impl<'a> TestReporter<'a> {
387483
/// Report this test event to the given writer.
388484
fn write_event(&mut self, event: TestEvent<'a>) -> Result<(), WriteEventError> {
389485
match &mut self.stderr {
390-
ReporterStderrImpl::TerminalWithBar(progress_bar) => {
486+
ReporterStderrImpl::TerminalWithBar { bar, state } => {
391487
// Write to a string that will be printed as a log line.
392488
let mut buf: Vec<u8> = Vec::new();
393489
self.inner
394490
.write_event_impl(&event, &mut buf)
395491
.map_err(WriteEventError::Io)?;
396-
// ProgressBar::println doesn't print status lines if the bar is hidden. The suspend
397-
// method prints it in both cases.
398-
progress_bar.suspend(|| {
492+
493+
state.update_progress_bar(&event, &self.inner.styles, bar);
494+
// ProgressBar::println doesn't print status lines if the bar is
495+
// hidden. The suspend method prints it in all cases.
496+
bar.suspend(|| {
399497
_ = std::io::stderr().write_all(&buf);
400498
});
401-
402-
update_progress_bar(&event, &self.inner.styles, progress_bar);
403499
}
404500
ReporterStderrImpl::TerminalWithoutBar => {
405501
// Write to a buffered stderr.
@@ -422,46 +518,6 @@ impl<'a> TestReporter<'a> {
422518
}
423519
}
424520

425-
fn update_progress_bar(event: &TestEvent<'_>, styles: &Styles, progress_bar: &ProgressBar) {
426-
match &event.kind {
427-
TestEventKind::SetupScriptStarted { no_capture, .. } => {
428-
// Hide the progress bar if either stderr or stdout are being passed through.
429-
if *no_capture {
430-
progress_bar.set_draw_target(ProgressDrawTarget::hidden());
431-
}
432-
}
433-
TestEventKind::SetupScriptFinished { no_capture, .. } => {
434-
// Restore the progress bar if it was hidden.
435-
if *no_capture {
436-
progress_bar.set_draw_target(ProgressDrawTarget::stderr());
437-
}
438-
}
439-
TestEventKind::TestStarted {
440-
current_stats,
441-
running,
442-
cancel_state,
443-
..
444-
}
445-
| TestEventKind::TestFinished {
446-
current_stats,
447-
running,
448-
cancel_state,
449-
..
450-
} => {
451-
progress_bar.set_prefix(progress_bar_prefix(current_stats, *cancel_state, styles));
452-
progress_bar.set_message(progress_bar_msg(current_stats, *running, styles));
453-
// If there are skipped tests, the initial run count will be lower than when constructed
454-
// in ProgressBar::new.
455-
progress_bar.set_length(current_stats.initial_run_count as u64);
456-
progress_bar.set_position(current_stats.finished_count as u64);
457-
}
458-
TestEventKind::RunBeginCancel { .. } => {
459-
progress_bar.set_prefix(progress_bar_cancel_prefix(styles));
460-
}
461-
_ => {}
462-
}
463-
}
464-
465521
fn progress_bar_cancel_prefix(styles: &Styles) -> String {
466522
format!("{:>12}", "Cancelling".style(styles.fail))
467523
}

0 commit comments

Comments
 (0)