Skip to content

Commit

Permalink
tests: add helper to snapshot command output and exit status all at once
Browse files Browse the repository at this point in the history
It's easier to review insta snapshot diffs than scanning panic log containing
lengthy output. I think this will also help printf debugging.

test_global_opts.rs is migrated to new API as an example.
  • Loading branch information
yuja committed Feb 19, 2025
1 parent bc21f68 commit 6bf4b85
Show file tree
Hide file tree
Showing 2 changed files with 459 additions and 236 deletions.
94 changes: 93 additions & 1 deletion cli/tests/common/test_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fmt;
use std::fmt::Display;
use std::path::Path;
use std::path::PathBuf;
use std::process::ExitStatus;

use indoc::formatdoc;
use regex::Captures;
Expand Down Expand Up @@ -94,6 +96,31 @@ impl Default for TestEnvironment {
}

impl TestEnvironment {
/// Runs `jj args..` at the `current_dir`, returns the output.
#[must_use = "either snapshot the output or assert the exit status with .success()"]
pub fn jj_at<I>(&self, current_dir: &Path, args: I) -> CommandOutput
where
I: IntoIterator,
I::Item: AsRef<OsStr>,
{
self.jj_with(|cmd| cmd.current_dir(current_dir).args(args))
}

/// Runs `jj` command with additional configuration, returns the output.
#[must_use = "either snapshot the output or assert the exit status with .success()"]
pub fn jj_with(
&self,
configure: impl FnOnce(&mut assert_cmd::Command) -> &mut assert_cmd::Command,
) -> CommandOutput {
let mut cmd = self.jj_cmd(&self.env_root, &[]);
let output = configure(&mut cmd).output().unwrap();
CommandOutput {
stdout: self.normalize_output(String::from_utf8(output.stdout).unwrap()),
stderr: self.normalize_output(String::from_utf8(output.stderr).unwrap()),
status: output.status,
}
}

#[must_use]
pub fn jj_cmd(&self, current_dir: &Path, args: &[&str]) -> assert_cmd::Command {
let mut cmd = assert_cmd::Command::cargo_bin("jj").unwrap();
Expand Down Expand Up @@ -338,6 +365,71 @@ impl TestEnvironment {
}
}

/// Command output and exit status to be displayed in normalized form.
#[derive(Clone)]
pub struct CommandOutput {
pub stdout: CommandOutputString,
pub stderr: CommandOutputString,
pub status: ExitStatus,
}

impl CommandOutput {
/// Normalizes Windows directory separator to slash.
#[must_use]
pub fn normalize_backslash(self) -> Self {
CommandOutput {
stdout: self.stdout.normalize_backslash(),
stderr: self.stderr.normalize_backslash(),
status: self.status,
}
}

/// Removes the last line (such as platform-specific error message) from the
/// normalized stderr text.
#[must_use]
pub fn strip_stderr_last_line(self) -> Self {
CommandOutput {
stdout: self.stdout,
stderr: self.stderr.strip_last_line(),
status: self.status,
}
}

/// Ensures that the command exits with success status.
#[track_caller]
pub fn success(self) -> Self {
assert!(self.status.success(), "{self}");
self
}
}

impl Display for CommandOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let CommandOutput {
stdout,
stderr,
status,
} = self;
if !stdout.is_empty() {
writeln!(f, "------- stdout -------")?;
write!(f, "{stdout}")?;
}
if !stderr.is_empty() {
writeln!(f, "------- stderr -------")?;
write!(f, "{stderr}")?;
}
if !status.success() {
if let Some(code) = status.code() {
writeln!(f, "[exit status: {code}]")?;
} else {
// On Windows, this would print "exit code: N".
writeln!(f, "[{status}]")?;
}
}
Ok(())
}
}

/// Command output data to be displayed in normalized form.
// TODO: Maybe we can add wrapper that stores both stdout/stderr and print them.
#[derive(Clone)]
Expand All @@ -354,7 +446,7 @@ impl CommandOutputString {
self.normalize_with(|s| s.replace('\\', "/"))
}

/// Normalizes [`std::process::ExitStatus`] message.
/// Normalizes [`ExitStatus`] message.
///
/// On Windows, it prints "exit code" instead of "exit status".
#[must_use]
Expand Down
Loading

0 comments on commit 6bf4b85

Please sign in to comment.