Skip to content

Commit

Permalink
Tee stdout or stderr
Browse files Browse the repository at this point in the history
  • Loading branch information
smoelius committed Nov 12, 2023
1 parent 7fc9198 commit be085e5
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 35 deletions.
2 changes: 2 additions & 0 deletions tests/ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ fn clippy() {
"--deny=warnings",
"--warn=clippy::pedantic",
"--allow=clippy::format-collect",
"--allow=clippy::missing-errors-doc",
"--allow=clippy::missing-panics-doc",
])
.current_dir(dir)
.assert()
Expand Down
25 changes: 11 additions & 14 deletions tests/rustsec_comparison.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use snapbox::{assert_matches_path, cmd::Command};
use std::env::{remove_var, var};
use snapbox::assert_matches_path;
use std::{env::remove_var, process::Command};

mod util;
use util::{tee, token_modifier, Tee};

#[ctor::ctor]
fn initialize() {
Expand All @@ -8,24 +11,18 @@ fn initialize() {

#[test]
fn rustsec_comparison() {
let assert = Command::new("cargo")
let mut command = Command::new("cargo");
command
.arg("run")
.current_dir("rustsec_comparison")
.env("RUST_BACKTRACE", "0")
.assert();
.env("RUST_BACKTRACE", "0");

let output = tee(command, Tee::Stdout).unwrap();

let stdout_actual = std::str::from_utf8(&assert.get_output().stdout).unwrap();
let stdout_actual = std::str::from_utf8(&output.captured).unwrap();

assert_matches_path(
format!("tests/rustsec_comparison.{}.stdout", token_modifier()),
stdout_actual,
);
}

fn token_modifier() -> &'static str {
if var("GITHUB_TOKEN_PATH").is_ok() {
"with_token"
} else {
"without_token"
}
}
55 changes: 34 additions & 21 deletions tests/snapbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ use regex::Regex;
use serde::Deserialize;
use snapbox::{
assert_matches_path,
cmd::{cargo_bin, Command},
cmd::{cargo_bin, Command as SnapboxCommand},
};
use std::{
env::var,
ffi::OsStr,
fs::{read_dir, read_to_string},
process::Command,
};
use tempfile::tempdir;

mod util;
use util::{enabled, tee, token_modifier, Tee};

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Test {
Expand Down Expand Up @@ -54,45 +57,55 @@ fn snapbox() -> Result<()> {

let tempdir = tempdir()?;

let mut command =
Command::new("git").args(["clone", &test.url, &tempdir.path().to_string_lossy()]);
let mut command = SnapboxCommand::new("git").args([
"clone",
&test.url,
&tempdir.path().to_string_lossy(),
]);
if test.rev.is_none() {
command = command.arg("--depth=1");
}
command.assert().success();

if let Some(rev) = &test.rev {
Command::new("git")
SnapboxCommand::new("git")
.args(["checkout", rev])
.current_dir(&tempdir)
.assert()
.success();
}

let output = Command::new(cargo_bin("cargo-unmaintained"))
let mut command = Command::new(cargo_bin("cargo-unmaintained"));
command
.args(["unmaintained", "--color=never", "--imprecise"])
.current_dir(&tempdir)
.output()?;
.current_dir(&tempdir);

if enabled("VERBOSE") {
// smoelius If `VERBOSE` is enabled, don't bother comparing stderr, because it won't
// match.
command.arg("--verbose");

let output = tee(command, Tee::Stdout)?;

let stderr_actual = String::from_utf8(output.stderr)?;
let stdout_actual = String::from_utf8(output.stdout)?;
let stdout_actual = String::from_utf8(output.captured)?;

// smoelius: Compare stderr before stdout so that you can see any errors that occurred.
assert_matches_path(stderr_path, stderr_actual);
assert_matches_path(stdout_path, stdout_actual);
assert_matches_path(stdout_path, stdout_actual);
} else {
let output = command.output()?;

let stderr_actual = String::from_utf8(output.stderr)?;
let stdout_actual = String::from_utf8(output.stdout)?;

// smoelius: Compare stderr before stdout so that you can see any errors that
// occurred.
assert_matches_path(stderr_path, stderr_actual);
assert_matches_path(stdout_path, stdout_actual);
}

Ok(())
})
}

fn token_modifier() -> &'static str {
if var("GITHUB_TOKEN_PATH").is_ok() {
"with_token"
} else {
"without_token"
}
}

static RES: Lazy<[Regex; 2]> = Lazy::new(|| {
[
Regex::new(r"([^ ]*) days").unwrap(),
Expand Down
73 changes: 73 additions & 0 deletions tests/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#![cfg_attr(dylint_lib = "general", allow(crate_wide_allow))]
#![allow(dead_code)]

use anyhow::{Context, Result};
use std::{
env::var,
io::Read,
process::{Command, ExitStatus, Stdio},
};

#[derive(Clone, Copy)]
pub enum Tee {
Stdout,
Stderr,
}

pub struct Output {
pub status: ExitStatus,
pub captured: Vec<u8>,
}

const BUF_SIZE: usize = 1024;

pub fn tee(mut command: Command, which: Tee) -> Result<Output> {
match which {
Tee::Stdout => {
command.stdout(Stdio::piped());
}
Tee::Stderr => {
command.stderr(Stdio::piped());
}
}

let mut child = command
.spawn()
.with_context(|| format!("command failed: {command:?}"))?;

let stream: &mut dyn Read = match which {
Tee::Stdout => child.stdout.as_mut().unwrap(),
Tee::Stderr => child.stderr.as_mut().unwrap(),
};

let mut captured = Vec::new();

loop {
let mut buf = [0u8; BUF_SIZE];
let size = stream.read(&mut buf).with_context(|| "`read` failed")?;
if size == 0 {
break;
}
let s = std::str::from_utf8(&buf)?;
print!("{s}");
captured.extend_from_slice(&buf[..size]);
}

let status = child.wait().with_context(|| "`wait` failed")?;

Ok(Output { status, captured })
}

#[must_use]
pub fn token_modifier() -> &'static str {
if var("GITHUB_TOKEN_PATH").is_ok() {
"with_token"
} else {
"without_token"
}
}

#[must_use]
pub fn enabled(key: &str) -> bool {
var(key).map_or(false, |value| value != "0")
}

0 comments on commit be085e5

Please sign in to comment.