cli-test: Build radicle binaries automatically

To avoid having to call `cargo build` before running the tests, we build
the necessary binaries automatically using the `escargot` crate.
This commit is contained in:
Alexis Sellier 2023-05-31 11:39:35 +02:00
parent 21533f37eb
commit 97f122c451
No known key found for this signature in database
4 changed files with 90 additions and 16 deletions

13
Cargo.lock generated
View File

@ -833,6 +833,18 @@ dependencies = [
"libc",
]
[[package]]
name = "escargot"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6"
dependencies = [
"log",
"once_cell",
"serde",
"serde_json",
]
[[package]]
name = "fastrand"
version = "1.9.0"
@ -1947,6 +1959,7 @@ dependencies = [
name = "radicle-cli-test"
version = "0.1.1"
dependencies = [
"escargot",
"log",
"pretty_assertions",
"shlex",

View File

@ -15,6 +15,7 @@ keywords = ["cli", "test", "command"]
readme = "README.md"
[dependencies]
escargot = { version = "0.5.7" }
log = { version = "0.4", features = ["std"] }
pretty_assertions = { version = "1.3.0" }
shlex = { version = "1.1.0" }

View File

@ -2,12 +2,16 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::{env, fs, io, mem};
use std::sync;
use std::{env, ffi, fs, io, mem};
use snapbox::cmd::{Command, OutputAssert};
use snapbox::{Assert, Substitutions};
use thiserror::Error;
/// Used to ensure the build task is only run once.
static BUILD: sync::Once = sync::Once::new();
#[derive(Error, Debug)]
pub enum Error {
#[error("parsing failed")]
@ -66,6 +70,8 @@ pub struct TestFormula {
tests: Vec<Test>,
/// Output substitutions.
subs: Substitutions,
/// Binaries path.
bins: Vec<PathBuf>,
}
impl TestFormula {
@ -75,9 +81,62 @@ impl TestFormula {
env: HashMap::new(),
tests: Vec::new(),
subs: Substitutions::new(),
bins: env::var("PATH")
.map(|p| p.split(':').map(PathBuf::from).collect())
.unwrap_or_default(),
}
}
pub fn build(&mut self, binaries: &[(&str, &str)]) -> &mut Self {
let manifest = env::var("CARGO_MANIFEST_DIR").expect(
"TestFormula::build: cannot build binaries: variable `CARGO_MANIFEST_DIR` is not set",
);
let profile = if cfg!(debug_assertions) {
"debug"
} else {
"release"
};
let target_dir = env::var("CARGO_TARGET_DIR").unwrap_or("target".to_string());
let manifest = Path::new(manifest.as_str());
let bins = manifest.join(&target_dir).join(profile);
// Add the target dir to the beginning of the list we will use as `PATH`.
self.bins.insert(0, bins);
// We don't need to re-build everytime the `build` function is called. Once is enough.
BUILD.call_once(|| {
use escargot::format::Message;
for (package, binary) in binaries {
log::debug!(target: "test", "Building binaries for package `{package}`..");
let results = escargot::CargoBuild::new()
.package(package)
.bin(binary)
.manifest_path(&manifest.join("Cargo.toml"))
.target_dir(&target_dir)
.exec()
.unwrap();
for result in results {
match result {
Ok(msg) => {
if let Ok(Message::CompilerArtifact(a)) = msg.decode() {
if let Some(e) = a.executable {
log::debug!(target: "test", "Built {}", e.display());
}
}
}
Err(e) => {
log::error!(target: "test", "Error building package `{package}`: {e}");
}
}
}
}
});
self
}
pub fn cwd(&mut self, path: impl AsRef<Path>) -> &mut Self {
self.cwd = path.as_ref().into();
self
@ -208,21 +267,7 @@ impl TestFormula {
let assert = Assert::new().substitutions(self.subs.clone());
fs::create_dir_all(&self.cwd)?;
let mut bins = env::var("PATH").unwrap_or_default();
if let Ok(manifest) = env::var("CARGO_MANIFEST_DIR") {
let profile = if cfg!(debug_assertions) {
"debug"
} else {
"release"
};
let cargo_target_dir = env::var("CARGO_TARGET_DIR").unwrap_or("target".to_string());
let path = Path::new(manifest.as_str());
let path = path.parent().unwrap().join(cargo_target_dir).join(profile);
bins = format!("{}:{bins}", path.display());
}
log::debug!(target: "test", "Using PATH={bins}");
log::debug!(target: "test", "Using PATH {:?}", self.bins);
for test in &self.tests {
for assertion in &test.assertions {
@ -258,6 +303,12 @@ impl TestFormula {
log::error!(target: "test", "{path}: Directory {} does not exist..", self.cwd.display());
}
let bins = self
.bins
.iter()
.map(|p| p.as_os_str())
.collect::<Vec<_>>()
.join(ffi::OsStr::new(":"));
let result = Command::new(cmd.clone())
.env_clear()
.env("PATH", &bins)
@ -342,6 +393,11 @@ $ rad sync
cwd: PathBuf::new(),
env: HashMap::new(),
subs: Substitutions::new(),
bins: env::var("PATH")
.unwrap()
.split(':')
.map(PathBuf::from)
.collect(),
tests: vec![
Test {
context: vec![String::from("Let's try to track @dave and @sean:")],

View File

@ -53,6 +53,10 @@ fn test<'a>(
.envs(git::env::GIT_DEFAULT_CONFIG)
.envs(envs)
.cwd(cwd)
.build(&[
("radicle-remote-helper", "git-remote-rad"),
("radicle-cli", "rad"),
])
.file(base.join(test))?
.run()?;