зеркало из https://github.com/mozilla/sccache.git
408 строки
12 KiB
Rust
408 строки
12 KiB
Rust
//! System tests for compiling Rust code with cargo.
|
|
//!
|
|
//! Any copyright is dedicated to the Public Domain.
|
|
//! http://creativecommons.org/publicdomain/zero/1.0/
|
|
|
|
pub mod helpers;
|
|
|
|
use anyhow::{Context, Result};
|
|
use helpers::{cargo_clean, stop_sccache, CARGO, CRATE_DIR};
|
|
|
|
use assert_cmd::prelude::*;
|
|
use fs_err as fs;
|
|
use helpers::{SccacheTest, SCCACHE_BIN};
|
|
use predicates::prelude::*;
|
|
use serial_test::serial;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_check() -> Result<()> {
|
|
test_rust_cargo_cmd("check", SccacheTest::new(None)?)
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_check_readonly() -> Result<()> {
|
|
test_rust_cargo_cmd_readonly("check", SccacheTest::new(None)?)
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_build() -> Result<()> {
|
|
test_rust_cargo_cmd("build", SccacheTest::new(None)?)
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_build_readonly() -> Result<()> {
|
|
test_rust_cargo_cmd_readonly("build", SccacheTest::new(None)?)
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
#[cfg(unix)]
|
|
fn test_run_log_no_perm() -> Result<()> {
|
|
trace!("sccache with log");
|
|
stop_sccache()?;
|
|
let mut cmd = Command::new(SCCACHE_BIN.as_os_str());
|
|
cmd.arg("gcc")
|
|
.env("SCCACHE_ERROR_LOG", "/no-perm.log") // Should not work
|
|
.env("SCCACHE_LOG", "debug");
|
|
|
|
cmd.assert().failure().stderr(predicate::str::contains(
|
|
"Cannot open/write log file '/no-perm.log'",
|
|
));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn test_run_log() -> Result<()> {
|
|
trace!("sccache with log");
|
|
stop_sccache()?;
|
|
|
|
let tempdir = tempfile::Builder::new()
|
|
.prefix("sccache_test_rust_cargo")
|
|
.tempdir()
|
|
.context("Failed to create tempdir")?;
|
|
let tmppath = tempdir.path().join("perm.log");
|
|
let mut cmd = Command::new(SCCACHE_BIN.as_os_str());
|
|
cmd.arg("--start-server")
|
|
.env("SCCACHE_ERROR_LOG", &tmppath) // Should not work
|
|
.env("SCCACHE_LOG", "debug");
|
|
|
|
cmd.assert().success();
|
|
stop_sccache()?;
|
|
assert!(Path::new(&tmppath).is_file());
|
|
Ok(())
|
|
}
|
|
|
|
/// This test checks that changing an environment variable reference by env! is detected by
|
|
/// sccache, causes a rebuild and is correctly printed to stdout.
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_run_with_env_dep_parsing() -> Result<()> {
|
|
test_rust_cargo_env_dep(SccacheTest::new(None)?)
|
|
}
|
|
|
|
#[cfg(feature = "unstable")]
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_check_nightly() -> Result<()> {
|
|
use std::ffi::OsString;
|
|
|
|
test_rust_cargo_cmd(
|
|
"check",
|
|
SccacheTest::new(Some(&[(
|
|
"RUSTFLAGS",
|
|
OsString::from("-Cprofile-generate=."),
|
|
)]))?,
|
|
)
|
|
}
|
|
|
|
#[cfg(feature = "unstable")]
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_check_nightly_readonly() -> Result<()> {
|
|
use std::ffi::OsString;
|
|
|
|
test_rust_cargo_cmd_readonly(
|
|
"check",
|
|
SccacheTest::new(Some(&[(
|
|
"RUSTFLAGS",
|
|
OsString::from("-Cprofile-generate=."),
|
|
)]))?,
|
|
)
|
|
}
|
|
|
|
#[cfg(feature = "unstable")]
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_build_nightly() -> Result<()> {
|
|
use std::ffi::OsString;
|
|
|
|
test_rust_cargo_cmd(
|
|
"build",
|
|
SccacheTest::new(Some(&[(
|
|
"RUSTFLAGS",
|
|
OsString::from("-Cprofile-generate=."),
|
|
)]))?,
|
|
)
|
|
}
|
|
|
|
#[cfg(feature = "unstable")]
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_build_nightly_readonly() -> Result<()> {
|
|
use std::ffi::OsString;
|
|
|
|
test_rust_cargo_cmd_readonly(
|
|
"build",
|
|
SccacheTest::new(Some(&[(
|
|
"RUSTFLAGS",
|
|
OsString::from("-Cprofile-generate=."),
|
|
)]))?,
|
|
)
|
|
}
|
|
|
|
/// Test that building a simple Rust crate with cargo using sccache results in a cache hit
|
|
/// when built a second time and a cache miss, when the environment variable referenced via
|
|
/// env! is changed.
|
|
fn test_rust_cargo_cmd(cmd: &str, test_info: SccacheTest) -> Result<()> {
|
|
// `cargo clean` first, just to be sure there's no leftover build objects.
|
|
cargo_clean(&test_info)?;
|
|
|
|
// Now build the crate with cargo.
|
|
Command::new(CARGO.as_os_str())
|
|
.args([cmd, "--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()?;
|
|
// Clean it so we can build it again.
|
|
cargo_clean(&test_info)?;
|
|
Command::new(CARGO.as_os_str())
|
|
.args([cmd, "--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_stats()?
|
|
.try_stdout(
|
|
predicates::str::contains(
|
|
r#""cache_hits":{"counts":{"Rust":2},"adv_counts":{"rust":2}}"#,
|
|
)
|
|
.from_utf8(),
|
|
)?
|
|
.try_success()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn restart_sccache(
|
|
test_info: &SccacheTest,
|
|
additional_envs: Option<Vec<(String, String)>>,
|
|
) -> Result<()> {
|
|
let cache_dir = test_info.tempdir.path().join("cache");
|
|
|
|
stop_sccache()?;
|
|
|
|
trace!("sccache --start-server");
|
|
|
|
let mut cmd = Command::new(SCCACHE_BIN.as_os_str());
|
|
cmd.arg("--start-server");
|
|
cmd.env("SCCACHE_DIR", &cache_dir);
|
|
|
|
if let Some(additional_envs) = additional_envs {
|
|
cmd.envs(additional_envs);
|
|
}
|
|
|
|
cmd.assert()
|
|
.try_success()
|
|
.context("Failed to start sccache server")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Test that building a simple Rust crate with cargo using sccache results in the following behaviors (for three different runs):
|
|
/// - In read-only mode, a cache miss.
|
|
/// - In read-write mode, a cache miss.
|
|
/// - In read-only mode, a cache hit.
|
|
///
|
|
/// The environment variable for read/write mode is added by this function.
|
|
fn test_rust_cargo_cmd_readonly(cmd: &str, test_info: SccacheTest) -> Result<()> {
|
|
// `cargo clean` first, just to be sure there's no leftover build objects.
|
|
cargo_clean(&test_info)?;
|
|
|
|
// The cache must be put into read-only mode, and that can only be configured
|
|
// when the server starts up, so we need to restart it.
|
|
restart_sccache(
|
|
&test_info,
|
|
Some(vec![("SCCACHE_LOCAL_RW_MODE".into(), "READ_ONLY".into())]),
|
|
)?;
|
|
|
|
// Now build the crate with cargo.
|
|
Command::new(CARGO.as_os_str())
|
|
.args([cmd, "--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()?;
|
|
|
|
// Stats reset on server restart, so this needs to be run for each build.
|
|
test_info
|
|
.show_stats()?
|
|
.try_stdout(
|
|
predicates::str::contains(r#""cache_hits":{"counts":{},"adv_counts":{}}"#).from_utf8(),
|
|
)?
|
|
.try_stdout(
|
|
predicates::str::contains(
|
|
r#""cache_misses":{"counts":{"Rust":2},"adv_counts":{"rust":2}}"#,
|
|
)
|
|
.from_utf8(),
|
|
)?
|
|
.try_success()?;
|
|
|
|
cargo_clean(&test_info)?;
|
|
restart_sccache(
|
|
&test_info,
|
|
Some(vec![("SCCACHE_LOCAL_RW_MODE".into(), "READ_WRITE".into())]),
|
|
)?;
|
|
Command::new(CARGO.as_os_str())
|
|
.args([cmd, "--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_stats()?
|
|
.try_stdout(
|
|
predicates::str::contains(r#""cache_hits":{"counts":{},"adv_counts":{}}"#).from_utf8(),
|
|
)?
|
|
.try_stdout(
|
|
predicates::str::contains(
|
|
r#""cache_misses":{"counts":{"Rust":2},"adv_counts":{"rust":2}}"#,
|
|
)
|
|
.from_utf8(),
|
|
)?
|
|
.try_success()?;
|
|
|
|
cargo_clean(&test_info)?;
|
|
restart_sccache(
|
|
&test_info,
|
|
Some(vec![("SCCACHE_LOCAL_RW_MODE".into(), "READ_ONLY".into())]),
|
|
)?;
|
|
Command::new(CARGO.as_os_str())
|
|
.args([cmd, "--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_stats()?
|
|
.try_stdout(
|
|
predicates::str::contains(
|
|
r#""cache_hits":{"counts":{"Rust":2},"adv_counts":{"rust":2}}"#,
|
|
)
|
|
.from_utf8(),
|
|
)?
|
|
.try_stdout(
|
|
predicates::str::contains(r#""cache_misses":{"counts":{},"adv_counts":{}}"#)
|
|
.from_utf8(),
|
|
)?
|
|
.try_success()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn test_rust_cargo_env_dep(test_info: SccacheTest) -> Result<()> {
|
|
cargo_clean(&test_info)?;
|
|
// Now build the crate with cargo.
|
|
Command::new(CARGO.as_os_str())
|
|
.args(["run", "--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_stdout(predicates::str::contains("Env var: 1"))?
|
|
.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())
|
|
.env("TEST_ENV_VAR", "OTHER_VALUE")
|
|
.current_dir(CRATE_DIR.as_os_str())
|
|
.assert()
|
|
.try_stderr(predicates::str::contains("\x1b[").from_utf8())?
|
|
.try_stdout(predicates::str::contains("Env var: OTHER_VALUE"))?
|
|
.try_success()?;
|
|
|
|
// Now get the stats and ensure that we had one cache hit for the second build.
|
|
// The test crate has one dependency (itoa) so there are two separate compilations, but only
|
|
// itoa should be cached (due to the changed environment variable).
|
|
test_info
|
|
.show_stats()?
|
|
.try_stdout(predicates::str::contains(r#""cache_hits":{"counts":{"Rust":1}"#).from_utf8())?
|
|
.try_success()?;
|
|
|
|
drop(test_info);
|
|
Ok(())
|
|
}
|
|
|
|
/// Test that building a simple Rust crate with cargo using sccache in read-only mode with an empty cache results in
|
|
/// a cache miss that is produced by the readonly storage wrapper (and does not attempt to write to the underlying cache).
|
|
#[test]
|
|
#[serial]
|
|
fn test_rust_cargo_cmd_readonly_preemtive_block() -> Result<()> {
|
|
let test_info = SccacheTest::new(None)?;
|
|
// `cargo clean` first, just to be sure there's no leftover build objects.
|
|
cargo_clean(&test_info)?;
|
|
|
|
let sccache_log = test_info.tempdir.path().join("sccache.log");
|
|
|
|
stop_sccache()?;
|
|
|
|
restart_sccache(
|
|
&test_info,
|
|
Some(vec![
|
|
("SCCACHE_LOCAL_RW_MODE".into(), "READ_ONLY".into()),
|
|
("SCCACHE_LOG".into(), "trace".into()),
|
|
(
|
|
"SCCACHE_ERROR_LOG".into(),
|
|
sccache_log.to_str().unwrap().into(),
|
|
),
|
|
]),
|
|
)?;
|
|
|
|
// Now build the crate with cargo.
|
|
// Assert that our cache miss is due to the readonly storage wrapper, not due to the underlying disk cache.
|
|
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()?;
|
|
|
|
let log_contents = fs::read_to_string(sccache_log)?;
|
|
assert!(predicates::str::contains("server has setup with ReadOnly").eval(log_contents.as_str()));
|
|
assert!(predicates::str::contains(
|
|
"Error executing cache write: Cannot write to read-only storage"
|
|
)
|
|
.eval(log_contents.as_str()));
|
|
assert!(predicates::str::contains("DiskCache::finish_put")
|
|
.not()
|
|
.eval(log_contents.as_str()));
|
|
|
|
// Stats reset on server restart, so this needs to be run for each build.
|
|
test_info
|
|
.show_stats()?
|
|
.try_stdout(
|
|
predicates::str::contains(r#""cache_hits":{"counts":{},"adv_counts":{}}"#).from_utf8(),
|
|
)?
|
|
.try_stdout(
|
|
predicates::str::contains(
|
|
r#""cache_misses":{"counts":{"Rust":2},"adv_counts":{"rust":2}}"#,
|
|
)
|
|
.from_utf8(),
|
|
)?
|
|
.try_success()?;
|
|
Ok(())
|
|
}
|