mirror of
https://github.com/Noratrieb/cargo-minimize.git
synced 2026-01-14 16:35:01 +01:00
move to separate crate
This commit is contained in:
parent
c9bdac41d8
commit
6b24aa05ec
8 changed files with 416 additions and 287 deletions
15
testsuite/Cargo.toml
Normal file
15
testsuite/Cargo.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "testsuite"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.70"
|
||||
fs_extra = "1.3.0"
|
||||
once_cell = "1.17.1"
|
||||
rayon = "1.7.0"
|
||||
regex = "1.7.3"
|
||||
tempfile = "3.5.0"
|
||||
walkdir = "2.3.3"
|
||||
1
testsuite/src/bin/regression_checker.rs
Normal file
1
testsuite/src/bin/regression_checker.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
fn main() {}
|
||||
6
testsuite/src/bin/runtest.rs
Normal file
6
testsuite/src/bin/runtest.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#[cfg(not(unix))]
|
||||
compile_error!("FIXME: This does not support windows yet. I am so sorry.");
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
testsuite::full_tests()
|
||||
}
|
||||
250
testsuite/src/lib.rs
Normal file
250
testsuite/src/lib.rs
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
use anyhow::{ensure, Context, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
use regex::Regex;
|
||||
use std::collections::hash_map::RandomState;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::Permissions;
|
||||
use std::io::BufWriter;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
};
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn full_tests() -> Result<()> {
|
||||
todo!("FIXME: Make this not cursed.")
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn full_tests() -> Result<()> {
|
||||
let exit = Command::new("cargo")
|
||||
.arg("build")
|
||||
.spawn()
|
||||
.context("spawn: cargo build")?
|
||||
.wait()
|
||||
.context("wait: cargo build")?;
|
||||
|
||||
ensure!(exit.success(), "cargo build failed");
|
||||
|
||||
let path = Path::new(file!())
|
||||
.canonicalize()?
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("full-tests");
|
||||
|
||||
let children = fs::read_dir(&path).with_context(|| format!("reading {}", path.display()))?;
|
||||
|
||||
let children = children
|
||||
.map(|e| e.map_err(Into::into))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
if std::env::var("PARALLEL").as_deref() != Ok("0") {
|
||||
children
|
||||
.into_par_iter()
|
||||
.map(|child| {
|
||||
let path = child.path();
|
||||
|
||||
build(&path).with_context(|| format!("building {:?}", path.file_name().unwrap()))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
} else {
|
||||
for child in children {
|
||||
let path = child.path();
|
||||
|
||||
build(&path).with_context(|| format!("building {:?}", path.file_name().unwrap()))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_dir(path: &Path) -> Result<(TempDir, PathBuf)> {
|
||||
let tempdir = tempfile::tempdir()?;
|
||||
|
||||
let proj_name = path.file_name().unwrap().to_str().unwrap();
|
||||
let proj_name = if let Some(proj_name) = proj_name.strip_suffix(".rs") {
|
||||
let out = Command::new("cargo")
|
||||
.arg("new")
|
||||
.arg(proj_name)
|
||||
.current_dir(tempdir.path())
|
||||
.output()
|
||||
.context("spawning cargo new")?;
|
||||
|
||||
ensure!(out.status.success(), "Failed to run cargo new");
|
||||
|
||||
fs::copy(
|
||||
path,
|
||||
tempdir.path().join(proj_name).join("src").join("main.rs"),
|
||||
)
|
||||
.context("copying to main.rs")?;
|
||||
proj_name
|
||||
} else {
|
||||
proj_name
|
||||
};
|
||||
|
||||
writeln!(io::stdout(), ".... Testing {}", proj_name)?;
|
||||
|
||||
fs_extra::copy_items(&[path], &tempdir, &fs_extra::dir::CopyOptions::new())?;
|
||||
|
||||
let proj_dir = tempdir.path().join(proj_name).canonicalize()?;
|
||||
|
||||
Ok((tempdir, proj_dir))
|
||||
}
|
||||
|
||||
fn setup_scripts(start_roots: &[String], proj_dir: &Path) -> Result<()> {
|
||||
// FIXME: Do this in a good way.
|
||||
// What the fuck is this.
|
||||
{
|
||||
let file = fs::File::create(proj_dir.join("check.sh"))?;
|
||||
|
||||
let expected_roots = start_roots
|
||||
.iter()
|
||||
.map(|root| format!("'{}'", root))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
dbg!(&expected_roots);
|
||||
|
||||
write!(
|
||||
BufWriter::new(&file),
|
||||
r#"#!/usr/bin/env bash
|
||||
if ! cargo check ; then
|
||||
>&2 echo "Cargo check failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUT=$(grep -ro "~MINIMIZE-ROOT [a-zA-Z_\-]*" --no-filename src)
|
||||
|
||||
python3 -c "
|
||||
# Get the data from bash by just substituting it in. It works!
|
||||
out = '''$OUT'''
|
||||
|
||||
lines = out.split('\n')
|
||||
|
||||
found = set()
|
||||
|
||||
for line in lines:
|
||||
name = line.removeprefix('~MINIMIZE-ROOT').strip()
|
||||
found.add(name)
|
||||
|
||||
# Pass in the data _from Rust directly_. Beautiful.
|
||||
expected_roots = {{{expected_roots}}}
|
||||
|
||||
for root in expected_roots:
|
||||
if root in found:
|
||||
print(f'Found {{root}} in output')
|
||||
else:
|
||||
print(f'Did not find {{root}} in output!')
|
||||
exit(1)
|
||||
"
|
||||
"#
|
||||
)?;
|
||||
|
||||
file.set_permissions(Permissions::from_mode(0o777))?;
|
||||
}
|
||||
{
|
||||
let file = fs::File::create(proj_dir.join("lint.sh"))?;
|
||||
|
||||
write!(
|
||||
BufWriter::new(&file),
|
||||
r#"#!/usr/bin/env bash
|
||||
cargo check
|
||||
"#
|
||||
)?;
|
||||
|
||||
#[cfg(unix)]
|
||||
file.set_permissions(Permissions::from_mode(0o777))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build(path: &Path) -> Result<()> {
|
||||
let (_tempdir, proj_dir) = setup_dir(path).context("setting up tempdir")?;
|
||||
let cargo_minimize = Path::new("target/debug/cargo-minimize")
|
||||
.canonicalize()
|
||||
.context("canonicalizing target/debug/cargo-minimize")?;
|
||||
|
||||
let start_roots = get_roots(&proj_dir).context("getting initial MINIMIZE-ROOTs")?;
|
||||
eprintln!("Roots: {:?}", start_roots);
|
||||
|
||||
setup_scripts(&start_roots, &proj_dir).context("setting up scripts")?;
|
||||
|
||||
let mut cmd = Command::new(cargo_minimize);
|
||||
cmd.current_dir(&proj_dir);
|
||||
|
||||
cmd.arg("minimize");
|
||||
cmd.arg("--script-path=./check.sh");
|
||||
cmd.arg("--script-path-lints=./lint.sh");
|
||||
|
||||
let out = cmd.output().context("spawning cargo-minimize")?;
|
||||
let stderr = String::from_utf8(out.stderr).unwrap();
|
||||
let stdout = String::from_utf8(out.stdout).unwrap();
|
||||
|
||||
ensure!(
|
||||
out.status.success(),
|
||||
"Command failed:\n--- stderr:\n{stderr}\n--- stdout:\n{stdout}"
|
||||
);
|
||||
|
||||
let required_deleted = get_required_deleted(&proj_dir).context("get REQUIRED-DELETED")?;
|
||||
|
||||
ensure!(
|
||||
required_deleted.is_empty(),
|
||||
"Some REQUIRE-DELETED have not been deleted: {required_deleted:?}"
|
||||
);
|
||||
|
||||
let end_roots = HashSet::<_, RandomState>::from_iter(
|
||||
get_roots(&proj_dir).context("getting final MINIMIZE-ROOTs")?,
|
||||
);
|
||||
for root in &start_roots {
|
||||
ensure!(
|
||||
end_roots.contains(root),
|
||||
"{root} was not found after minimization"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_roots(path: &Path) -> Result<Vec<String>> {
|
||||
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"~MINIMIZE-ROOT ([\w\-_]+)").unwrap());
|
||||
|
||||
grep(path, ®EX)
|
||||
}
|
||||
|
||||
fn get_required_deleted(path: &Path) -> Result<Vec<String>> {
|
||||
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"~REQUIRE-DELETED ([\w\-_]+)").unwrap());
|
||||
|
||||
grep(path, ®EX)
|
||||
}
|
||||
|
||||
fn grep(path: &Path, regex: &Regex) -> Result<Vec<String>> {
|
||||
let path = path.join("src");
|
||||
let mut results = Vec::new();
|
||||
let walk = walkdir::WalkDir::new(path);
|
||||
|
||||
for entry in walk {
|
||||
let entry = entry?;
|
||||
if !entry.metadata()?.is_file() {
|
||||
continue;
|
||||
}
|
||||
let src = fs::read_to_string(entry.path()).context("reading file")?;
|
||||
let captures = regex.captures_iter(&src);
|
||||
for cap in captures {
|
||||
let root_name = cap.get(1).unwrap();
|
||||
results.push(root_name.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue