Skip to content

Commit

Permalink
Add integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Saruniks committed Jul 17, 2024
1 parent f5b64f1 commit dd35cfa
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 140 deletions.
118 changes: 118 additions & 0 deletions tests/cache_hit_rate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
pub mod helpers;

use std::process::Command;

use anyhow::Result;
use assert_cmd::assert::OutputAssertExt;
use helpers::{cargo_clean, SccacheTest, CARGO, CRATE_DIR};
use predicates::{boolean::PredicateBooleanExt, str::PredicateStrExt};
use serial_test::serial;

#[test]
#[serial]
fn test_cache_hit_rate() -> Result<()> {
let test_info = SccacheTest::new(None)?;

Command::new(CARGO.as_os_str())
.args(["build", "--color=never"])
.envs(test_info.env.iter().cloned())
.current_dir(CRATE_DIR.as_os_str())
.assert()
.try_stderr(predicates::str::contains("\x1b[").from_utf8().not())?
.try_success()?;

test_info
.show_text_stats(false)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate\s+0\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate \(Rust\)\s+0\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_success()?;

// Clean it so we can build it again.
cargo_clean(&test_info)?;

Command::new(CARGO.as_os_str())
.args(["run", "--color=always"])
.envs(test_info.env.iter().cloned())
.current_dir(CRATE_DIR.as_os_str())
.assert()
.try_stderr(predicates::str::contains("\x1b[").from_utf8())?
.try_success()?;

test_info
.show_text_stats(false)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate\s+50\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate \(Rust\)\s+50\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_success()?;

Ok(())
}

#[test]
#[serial]
fn test_adv_cache_hit_rate() -> Result<()> {
let test_info = SccacheTest::new(None)?;

Command::new(CARGO.as_os_str())
.args(["build", "--color=never"])
.envs(test_info.env.iter().cloned())
.current_dir(CRATE_DIR.as_os_str())
.assert()
.try_stderr(predicates::str::contains("\x1b[").from_utf8().not())?
.try_success()?;

test_info
.show_text_stats(true)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate\s+0\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate \(rust\)\s+0\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_success()?;

cargo_clean(&test_info)?;

Command::new(CARGO.as_os_str())
.args(["run", "--color=always"])
.envs(test_info.env.iter().cloned())
.current_dir(CRATE_DIR.as_os_str())
.assert()
.try_stderr(predicates::str::contains("\x1b[").from_utf8())?
.try_success()?;

test_info
.show_text_stats(true)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate\s+50\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_stdout(
predicates::str::is_match(r"Cache hits rate \(rust\)\s+50\.00\s%")
.unwrap()
.from_utf8(),
)?
.try_success()?;

Ok(())
}
142 changes: 142 additions & 0 deletions tests/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use anyhow::{Context, Result};
use assert_cmd::assert::OutputAssertExt;
use chrono::Local;
use fs_err as fs;
use log::trace;
use once_cell::sync::Lazy;
use std::convert::Infallible;
use std::ffi::OsString;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

pub static CRATE_DIR: Lazy<PathBuf> =
Lazy::new(|| Path::new(file!()).parent().unwrap().join("../test-crate"));
pub static CARGO: Lazy<OsString> = Lazy::new(|| std::env::var_os("CARGO").unwrap());
pub static SCCACHE_BIN: Lazy<PathBuf> = Lazy::new(|| assert_cmd::cargo::cargo_bin("sccache"));
/// Ensures the logger is only initialized once. Panics if initialization fails.
static LOGGER: Lazy<Result<(), Infallible>> = Lazy::new(|| {
env_logger::Builder::new()
.format(|f, record| {
writeln!(
f,
"{} [{}] - {}",
Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
record.level(),
record.args()
)
})
.parse_env("RUST_LOG")
.init();
Ok(())
});

/// Used as a test setup fixture. The drop implementation cleans up after a _successful_ test.
/// We catch the panic to ensure that the drop runs and the TempDir is cleaned up.
pub struct SccacheTest<'a> {
/// Tempdir used for Sccache cache and cargo output. It is kept in the struct only to have the
/// destructor run when SccacheTest goes out of scope, but is never used otherwise.
#[allow(dead_code)]
pub tempdir: tempfile::TempDir,
pub env: Vec<(&'a str, std::ffi::OsString)>,
}

impl SccacheTest<'_> {
pub fn new(additional_envs: Option<&[(&'static str, std::ffi::OsString)]>) -> Result<Self> {
assert!(LOGGER.is_ok());

// Create a temp directory to use for the disk cache.
let tempdir = tempfile::Builder::new()
.prefix("sccache_test_rust_cargo")
.tempdir()
.context("Failed to create tempdir")?;
let cache_dir = tempdir.path().join("cache");
fs::create_dir(&cache_dir)?;
let cargo_dir = tempdir.path().join("cargo");
fs::create_dir(&cargo_dir)?;

// Ensure there's no existing sccache server running.
stop_sccache()?;

trace!("sccache --start-server");

Command::new(SCCACHE_BIN.as_os_str())
.arg("--start-server")
.env("SCCACHE_DIR", &cache_dir)
.assert()
.try_success()
.context("Failed to start sccache server")?;

let mut env = vec![
("CARGO_TARGET_DIR", cargo_dir.as_os_str().to_owned()),
("RUSTC_WRAPPER", SCCACHE_BIN.as_os_str().to_owned()),
// Explicitly disable incremental compilation because sccache is unable to cache it at
// the time of writing.
("CARGO_INCREMENTAL", OsString::from("0")),
("TEST_ENV_VAR", OsString::from("1")),
];

if let Some(vec) = additional_envs {
env.extend_from_slice(vec);
}

Ok(SccacheTest {
tempdir,
env: env.to_owned(),
})
}

/// Show the statistics for sccache. This will be called at the end of a test and making this
/// an associated function will ensure that the struct lives until the end of the test.
pub fn show_stats(&self) -> assert_cmd::assert::AssertResult {
trace!("sccache --show-stats");

Command::new(SCCACHE_BIN.as_os_str())
.args(["--show-stats", "--stats-format=json"])
.assert()
.try_success()
}

pub fn show_text_stats(&self, advanced: bool) -> assert_cmd::assert::AssertResult {
let cmd = if advanced {
"--show-adv-stats"
} else {
"--show-stats"
};

trace!("sccache {cmd}");

Command::new(SCCACHE_BIN.as_os_str())
.args([cmd, "--stats-format=text"])
.assert()
.try_success()
}
}

impl Drop for SccacheTest<'_> {
fn drop(&mut self) {
stop_sccache().expect("Stopping Sccache server failed");
}
}

pub fn stop_sccache() -> Result<()> {
trace!("sccache --stop-server");

Command::new(SCCACHE_BIN.as_os_str())
.arg("--stop-server")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.context("Failed to stop sccache server")?;
Ok(())
}

pub fn cargo_clean(test_info: &SccacheTest) -> Result<()> {
Command::new(CARGO.as_os_str())
.args(["clean"])
.envs(test_info.env.iter().cloned())
.current_dir(CRATE_DIR.as_os_str())
.assert()
.try_success()?;
Ok(())
}
22 changes: 4 additions & 18 deletions tests/sccache_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,18 @@
//!
//! Any copyright is dedicated to the Public Domain.
//! http://creativecommons.org/publicdomain/zero/1.0/
pub mod helpers;

use anyhow::{Context, Result};
use anyhow::Result;
use assert_cmd::prelude::*;
use once_cell::sync::Lazy;
use helpers::{stop_sccache, SCCACHE_BIN};
use predicates::prelude::*;
use serial_test::serial;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::process::Command;

#[macro_use]
extern crate log;

static SCCACHE_BIN: Lazy<PathBuf> = Lazy::new(|| assert_cmd::cargo::cargo_bin("sccache"));

fn stop_sccache() -> Result<()> {
trace!("sccache --stop-server");

Command::new(SCCACHE_BIN.as_os_str())
.arg("--stop-server")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.context("Failed to stop sccache server")?;
Ok(())
}

#[test]
#[serial]
#[cfg(feature = "gcs")]
Expand Down
Loading

0 comments on commit dd35cfa

Please sign in to comment.