test infra

This commit is contained in:
nora 2022-12-20 20:47:51 +01:00
parent 1398d3d211
commit 75108c8553
6 changed files with 145 additions and 36 deletions

View file

@ -3,6 +3,7 @@ use rustfix::diagnostics::Diagnostic;
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
collections::HashSet, collections::HashSet,
ffi::OsStr,
fmt::{Debug, Display}, fmt::{Debug, Display},
path::PathBuf, path::PathBuf,
process::Command, process::Command,
@ -39,6 +40,7 @@ struct BuildInner {
verify: Verify, verify: Verify,
env: Vec<EnvVar>, env: Vec<EnvVar>,
allow_color: bool, allow_color: bool,
project_dir: Option<PathBuf>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -78,10 +80,19 @@ impl Build {
verify, verify,
env: options.env.clone(), env: options.env.clone(),
allow_color: !options.no_color, allow_color: !options.no_color,
project_dir: options.project_dir.clone(),
}), }),
} }
} }
fn cmd(&self, name: impl AsRef<OsStr>) -> Command {
let mut cmd = Command::new(name);
if let Some(path) = &self.inner.project_dir {
cmd.current_dir(path);
}
cmd
}
pub fn build(&self) -> Result<BuildResult> { pub fn build(&self) -> Result<BuildResult> {
let inner = &self.inner; let inner = &self.inner;
@ -96,8 +107,12 @@ impl Build {
let (is_ice, output) = match &inner.mode { let (is_ice, output) = match &inner.mode {
BuildMode::Cargo { args } => { BuildMode::Cargo { args } => {
let mut cmd = Command::new("cargo"); let mut cmd = self.cmd("cargo");
cmd.args(["build", "--color=always"]); cmd.arg("build");
if inner.allow_color {
cmd.arg("--color=always");
}
for arg in args.iter().flatten() { for arg in args.iter().flatten() {
cmd.arg(arg); cmd.arg(arg);
@ -118,7 +133,7 @@ impl Build {
) )
} }
BuildMode::Script(script_path) => { BuildMode::Script(script_path) => {
let mut cmd = Command::new(script_path); let mut cmd = self.cmd(script_path);
for env in &inner.env { for env in &inner.env {
cmd.env(&env.key, &env.value); cmd.env(&env.key, &env.value);
@ -131,10 +146,14 @@ impl Build {
(outputs.status.success(), output) (outputs.status.success(), output)
} }
BuildMode::Rustc => { BuildMode::Rustc => {
let mut cmd = Command::new("rustc"); let mut cmd = self.cmd("rustc");
cmd.args(["--edition", "2021"]); cmd.args(["--edition", "2021"]);
cmd.arg(&inner.input_path); cmd.arg(&inner.input_path);
if inner.allow_color {
cmd.arg("--color=always");
}
for env in &inner.env { for env in &inner.env {
cmd.env(&env.key, &env.value); cmd.env(&env.key, &env.value);
} }
@ -170,7 +189,7 @@ impl Build {
let diags = match &inner.mode { let diags = match &inner.mode {
BuildMode::Cargo { args } => { BuildMode::Cargo { args } => {
let mut cmd = Command::new("cargo"); let mut cmd = self.cmd("cargo");
cmd.args(["build", "--message-format=json"]); cmd.args(["build", "--message-format=json"]);
for arg in args.iter().flatten() { for arg in args.iter().flatten() {
@ -195,7 +214,7 @@ impl Build {
.collect() .collect()
} }
BuildMode::Rustc => { BuildMode::Rustc => {
let mut cmd = std::process::Command::new("rustc"); let mut cmd = self.cmd("rustc");
cmd.args(["--edition", "2021", "--error-format=json"]); cmd.args(["--edition", "2021", "--error-format=json"]);
cmd.arg(&inner.input_path); cmd.arg(&inner.input_path);

View file

@ -13,47 +13,52 @@ mod processor;
mod expand; mod expand;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::Parser;
use dylib_flag::RustFunction; use dylib_flag::RustFunction;
use processor::Minimizer; use processor::Minimizer;
use crate::processor::Processor; use crate::processor::Processor;
// Export so that the user doesn't have to add clap themselves.
pub use clap::Parser;
#[derive(clap::Parser)] #[derive(clap::Parser)]
#[command(version, about, name = "cargo", bin_name = "cargo")] #[command(version, about, name = "cargo", bin_name = "cargo")]
enum Cargo { pub enum Cargo {
Minimize(Options), Minimize(Options),
} }
#[derive(clap::Args, Debug)] #[derive(clap::Args, Debug)]
pub struct Options { pub struct Options {
#[arg(short, long)] #[arg(short, long)]
script_path: Option<PathBuf>, pub script_path: Option<PathBuf>,
#[arg(long)] #[arg(long)]
cargo_args: Option<String>, pub cargo_args: Option<String>,
#[arg(long)] #[arg(long)]
no_color: bool, pub no_color: bool,
#[arg(long)] #[arg(long)]
rustc: bool, pub rustc: bool,
#[arg(long)] #[arg(long)]
no_verify: bool, pub no_verify: bool,
#[arg(long)] #[arg(long)]
verify_fn: Option<RustFunction>, pub verify_fn: Option<RustFunction>,
#[arg(long)] #[arg(long)]
env: Vec<EnvVar>, pub env: Vec<EnvVar>,
#[arg(long)]
pub project_dir: Option<PathBuf>,
#[arg(default_value = "src")] #[arg(default_value = "src")]
path: PathBuf, pub path: PathBuf,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct EnvVar { pub struct EnvVar {
key: String, pub key: String,
value: String, pub value: String,
} }
impl FromStr for EnvVar { impl FromStr for EnvVar {
@ -72,12 +77,10 @@ impl FromStr for EnvVar {
} }
} }
pub fn minimize() -> Result<()> { pub fn minimize(options: Options) -> Result<()> {
let Cargo::Minimize(options) = Cargo::parse();
let build = build::Build::new(&options); let build = build::Build::new(&options);
let mut minimizer = Minimizer::new_glob_dir(options, build); let mut minimizer = Minimizer::new_glob_dir(options, build)?;
minimizer.run_passes([ minimizer.run_passes([
Box::<privatize::Privatize>::default() as Box<dyn Processor>, Box::<privatize::Privatize>::default() as Box<dyn Processor>,
@ -88,3 +91,19 @@ pub fn minimize() -> Result<()> {
Ok(()) Ok(())
} }
impl Default for Options {
fn default() -> Self {
Self {
script_path: None,
cargo_args: None,
no_color: false,
rustc: false,
no_verify: false,
verify_fn: None,
env: Vec::new(),
project_dir: None,
path: PathBuf::from("/the/wrong/path/you/need/to/change/it"),
}
}
}

View file

@ -1,8 +1,11 @@
use anyhow::Result; use anyhow::Result;
use cargo_minimize::{Cargo, Parser};
use tracing::{error, info, Level}; use tracing::{error, info, Level};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
fn main() -> Result<()> { fn main() -> Result<()> {
let Cargo::Minimize(options) = Cargo::parse();
let registry = Registry::default().with( let registry = Registry::default().with(
EnvFilter::builder() EnvFilter::builder()
.with_default_directive(Level::INFO.into()) .with_default_directive(Level::INFO.into())
@ -18,7 +21,7 @@ fn main() -> Result<()> {
registry.with(tree_layer).init(); registry.with(tree_layer).init();
if let Err(err) = cargo_minimize::minimize() { if let Err(err) = cargo_minimize::minimize(options) {
error!("An error occured:\n{err}"); error!("An error occured:\n{err}");
} }

View file

@ -4,7 +4,7 @@ mod reaper;
pub(crate) use self::files::SourceFile; pub(crate) use self::files::SourceFile;
use crate::{build::Build, processor::files::Changes, Options}; use crate::{build::Build, processor::files::Changes, Options};
use anyhow::{Context, Result}; use anyhow::{bail, Context, Result};
use owo_colors::OwoColorize; use owo_colors::OwoColorize;
use std::{collections::HashSet, ffi::OsStr, fmt::Debug}; use std::{collections::HashSet, ffi::OsStr, fmt::Debug};
@ -49,7 +49,7 @@ pub(crate) struct Minimizer {
} }
impl Minimizer { impl Minimizer {
pub(crate) fn new_glob_dir(options: Options, build: Build) -> Self { pub(crate) fn new_glob_dir(options: Options, build: Build) -> Result<Self> {
let path = &options.path; let path = &options.path;
let walk = walkdir::WalkDir::new(path); let walk = walkdir::WalkDir::new(path);
@ -69,13 +69,17 @@ impl Minimizer {
.inspect(|file| { .inspect(|file| {
info!("Collecting file: {}", file.path.display()); info!("Collecting file: {}", file.path.display());
}) })
.collect(); .collect::<Vec<_>>();
Self { if files.is_empty() {
bail!("Did not find any files for path {}", path.display());
}
Ok(Self {
files, files,
build, build,
options, options,
} })
} }
pub(crate) fn run_passes<'a>( pub(crate) fn run_passes<'a>(

View file

@ -1,11 +1,51 @@
use anyhow::Result; use std::process::Command;
use anyhow::{bail, Result};
use cargo_minimize::Options;
fn canonicalize(code: &str) -> Result<String> {
let ast = syn::parse_file(code)?;
Ok(prettyplease::unparse(&ast))
}
pub fn run_test(code: &str, minimizes_to: &str, options: impl FnOnce(&mut Options)) -> Result<()> {
let dir = tempfile::tempdir()?;
let mut cargo = Command::new("cargo");
cargo.args(["new", "project"]);
cargo.current_dir(dir.path());
let output = cargo.output()?;
if !output.status.success() {
bail!(
"Failed to create cargo project, {}",
String::from_utf8(output.stderr)?
);
}
let cargo_dir = dir.path().join("project");
let main_rs = cargo_dir.join("src/main.rs");
std::fs::write(&main_rs, code)?;
let mut opts = Options::default();
let path = cargo_dir.join("src");
opts.project_dir = Some(cargo_dir);
opts.path = path;
options(&mut opts);
cargo_minimize::minimize(opts)?;
let minimized_main_rs = std::fs::read_to_string(main_rs)?;
let actual = canonicalize(&minimized_main_rs)?;
let expectecd = canonicalize(minimizes_to)?;
assert_eq!(actual, expectecd);
fn run_test(_: &str) -> Result<()> {
let _ = tempfile::tempdir()?;
Ok(()) Ok(())
} }
#[test]
fn smoke() {
run_test("").unwrap();
}

24
tests/minimize.rs Normal file
View file

@ -0,0 +1,24 @@
mod helper;
use anyhow::Result;
use helper::run_test;
#[test]
fn hello_world_no_verify() -> Result<()> {
run_test(
r##"
fn main() {
println!("Hello, world!");
}
"##,
r##"
fn main() {
loop {}
}
"##,
|opts| {
opts.no_verify = true;
},
)
}