diff --git a/assay-proc-macro/src/lib.rs b/assay-proc-macro/src/lib.rs index 917fed8..ffcd80b 100644 --- a/assay-proc-macro/src/lib.rs +++ b/assay-proc-macro/src/lib.rs @@ -14,7 +14,7 @@ use syn::{ }; struct AssayAttribute { - include: Option>, + include: Option)>>, should_panic: bool, env: Option>, setup: Option, @@ -48,7 +48,29 @@ impl Parse for AssayAttribute { Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. - }) => Some(lit_str.value()), + }) => { + let value = lit_str.value(); + Some((value, None)) + } + Expr::Tuple(tuple) => { + let mut elements = Vec::new(); + for e in tuple.elems.into_iter() { + if let Expr::Lit(ExprLit { + lit: Lit::Str(lit_str), + .. + }) = e + { + elements.push(lit_str.value()); + } else { + return None; + } + } + if elements.len() == 2 { + Some((elements[0].clone(), Some(elements[1].clone()))) + } else { + None + } + } _ => None, }) .collect(), @@ -112,10 +134,14 @@ pub fn assay(attr: TokenStream, item: TokenStream) -> TokenStream { let mut out = quote! { let fs = assay::PrivateFS::new()?; }; - for file in include { + for (source_path, destination_path) in include { + let destination_fragment = match destination_path { + None => quote!(std::option::Option::<::std::path::PathBuf>::None), + Some(p) => quote!(std::option::Option::Some(#p)), + }; out = quote! { #out - fs.include(#file)?; + fs.include(#source_path, #destination_fragment)?; }; } out diff --git a/src/lib.rs b/src/lib.rs index 27b5225..67c172a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,11 +22,12 @@ pub use pretty_assertions_sorted::{assert_eq, assert_eq_sorted, assert_ne}; #[doc(hidden)] pub use rusty_fork::{fork, rusty_fork_id, rusty_fork_test_name, ChildWrapper}; +use std::fs::create_dir_all; use std::{ env, error::Error, - fs::{copy, create_dir_all}, - path::{Component, Path, PathBuf}, + fs::copy, + path::{Path, PathBuf}, }; use tempfile::{Builder, TempDir}; @@ -47,39 +48,68 @@ impl PrivateFS { }) } - pub fn include(&self, path: impl AsRef) -> Result<(), Box> { + pub fn include( + &self, + source_path: S, + destination_path: Option, + ) -> Result<(), Box> + where + S: AsRef, + D: AsRef, + { // Get our pathbuf to the file to include - let mut inner_path = path.as_ref().to_owned(); + let mut inner_path = source_path.as_ref().to_owned(); // If the path given is not absolute then it's relative to the dir we // ran the test from let is_relative = inner_path.is_relative(); if is_relative { - inner_path = self.ran_from.join(&path); + inner_path = self.ran_from.join(&source_path); + } + + if !inner_path.is_file() { + panic!( + "The source path passed to `#[include()]` must point to a file. {:?} is not a file.", + inner_path + ); } // Get our working directory let dir = self.directory.path().to_owned(); - // Make the relative path of the file in relation to our temp file - // system based on if it was absolute or not - let relative = if !is_relative { - inner_path - .components() - .filter(|c| *c != Component::RootDir) - .collect::() - } else { - path.as_ref().into() + let destination_path = match destination_path { + None => { + // If the destination path is unspecified, we mount the file in the root directory + // of the test's private filesystem + match inner_path.file_name() { + Some(filename) => dir.join(filename), + None => { + panic!( + "Failed to extract the filename from the source path, {:?}.", + inner_path + ) + } + } + } + Some(p) => { + let p = p.as_ref(); + if !p.is_relative() { + panic!( + "The destination path for included files must be a relative path. {:?} isn't.", + p + ); + } + // If the relative path to the file includes parent directories create + // them + if let Some(parent) = p.parent() { + create_dir_all(dir.join(parent))?; + } + dir.join(p) + } }; - // If the relative path to the file includes parent directories create - // them - if let Some(parent) = relative.parent() { - create_dir_all(dir.join(parent))?; - } - // Copy the file over from the file system into the temp file system - copy(inner_path, dir.join(relative))?; + copy(inner_path, destination_path)?; Ok(()) } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index bc5b1de..4afe682 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -31,10 +31,11 @@ fn private_2() { assert_eq!("This is a test\nprivate 2\n", &fs::read_to_string("test")?); } -#[assay(include = ["Cargo.toml", "src/lib.rs"])] +#[assay(include = ["Cargo.toml", "src/lib.rs", ("HOW_TO_USE.md", "docs/GUIDE.md")])] fn include() { - assert!(fs::metadata("src/lib.rs")?.is_file()); + assert!(fs::metadata("lib.rs")?.is_file()); assert!(fs::metadata("Cargo.toml")?.is_file()); + assert!(fs::metadata("docs/GUIDE.md")?.is_file()); } #[assay(should_panic)] @@ -95,7 +96,7 @@ fn setup_teardown_test_2() { #[assay( setup = setup_func_2(), - include = ["Cargo.toml", "src/lib.rs"], + include = ["Cargo.toml", ("src/lib.rs", "src/lib.rs")], env = [ ("GOODBOY", "Bukka"), ("BADDOGS", "false") @@ -133,7 +134,7 @@ async fn one_test_to_call_it_all_2() { assert_eq!(env::var("BADDOGS")?, "false"); assert_eq!(fs::read_to_string("setup")?, "Value: 5"); assert!(PathBuf::from("Cargo.toml").exists()); - assert!(PathBuf::from("src/lib.rs").exists()); + assert!(PathBuf::from("lib.rs").exists()); // Removing this actually causes the test to fail panic!();