[tests] Use traits to specify test parameters
To deal with variety in target attributes and their expected outputs, the test parameters are categorized into traits, thus the test program could define test parameters in a more structured way, without having to pass multiple tightly-coupled parameters to test functions.
This commit is contained in:
		@@ -22,18 +22,64 @@ const USAGE: &str = "Usage: cache_check [options] {device|file}\n\
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(CACHE_CHECK, USAGE);
 | 
			
		||||
test_accepts_version!(CACHE_CHECK);
 | 
			
		||||
test_rejects_bad_option!(CACHE_CHECK);
 | 
			
		||||
struct CacheCheck;
 | 
			
		||||
 | 
			
		||||
test_missing_input_arg!(CACHE_CHECK);
 | 
			
		||||
test_input_file_not_found!(CACHE_CHECK, ARG);
 | 
			
		||||
test_input_cannot_be_a_directory!(CACHE_CHECK, ARG);
 | 
			
		||||
test_unreadable_input_file!(CACHE_CHECK, ARG);
 | 
			
		||||
impl<'a> Program<'a> for CacheCheck {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "cache_check"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
test_help_message_for_tiny_input_file!(CACHE_CHECK, ARG);
 | 
			
		||||
test_spot_xml_data!(CACHE_CHECK, "cache_check", ARG);
 | 
			
		||||
test_corrupted_input_data!(CACHE_CHECK, ARG);
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        CACHE_CHECK
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::InputArg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> InputProgram<'a> for CacheCheck {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
 | 
			
		||||
        mk_valid_md(td)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_input_arg() -> &'a str {
 | 
			
		||||
        msg::MISSING_INPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn corrupted_input() -> &'a str {
 | 
			
		||||
        msg::BAD_SUPERBLOCK
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> BinaryInputProgram<'_> for CacheCheck {}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(CacheCheck);
 | 
			
		||||
test_accepts_version!(CacheCheck);
 | 
			
		||||
test_rejects_bad_option!(CacheCheck);
 | 
			
		||||
 | 
			
		||||
test_missing_input_arg!(CacheCheck);
 | 
			
		||||
test_input_file_not_found!(CacheCheck);
 | 
			
		||||
test_input_cannot_be_a_directory!(CacheCheck);
 | 
			
		||||
test_unreadable_input_file!(CacheCheck);
 | 
			
		||||
 | 
			
		||||
test_help_message_for_tiny_input_file!(CacheCheck);
 | 
			
		||||
test_spot_xml_data!(CacheCheck);
 | 
			
		||||
test_corrupted_input_data!(CacheCheck);
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,29 +4,35 @@ use thinp::version::tools_version;
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
// help
 | 
			
		||||
 | 
			
		||||
pub fn test_help_short(program: &str, usage: &str) -> Result<()> {
 | 
			
		||||
    let stdout = run_ok(program, &["-h"])?;
 | 
			
		||||
    assert_eq!(stdout, usage);
 | 
			
		||||
pub fn test_help_short<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    P: Program<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let stdout = run_ok(P::path(), &["-h"])?;
 | 
			
		||||
    assert_eq!(stdout, P::usage());
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_help_long(program: &str, usage: &str) -> Result<()> {
 | 
			
		||||
    let stdout = run_ok(program, &["--help"])?;
 | 
			
		||||
    assert_eq!(stdout, usage);
 | 
			
		||||
pub fn test_help_long<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    P: Program<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let stdout = run_ok(P::path(), &["--help"])?;
 | 
			
		||||
    assert_eq!(stdout, P::usage());
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_accepts_help {
 | 
			
		||||
    ($program: ident, $usage: expr) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn accepts_h() -> Result<()> {
 | 
			
		||||
            test_help_short($program, $usage)
 | 
			
		||||
            test_help_short::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn accepts_help() -> Result<()> {
 | 
			
		||||
            test_help_long($program, $usage)
 | 
			
		||||
            test_help_long::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -34,14 +40,20 @@ macro_rules! test_accepts_help {
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
// version
 | 
			
		||||
 | 
			
		||||
pub fn test_version_short(program: &str) -> Result<()> {
 | 
			
		||||
    let stdout = run_ok(program, &["-V"])?;
 | 
			
		||||
pub fn test_version_short<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    P: Program<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let stdout = run_ok(P::path(), &["-V"])?;
 | 
			
		||||
    assert!(stdout.contains(tools_version()));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_version_long(program: &str) -> Result<()> {
 | 
			
		||||
    let stdout = run_ok(program, &["--version"])?;
 | 
			
		||||
pub fn test_version_long<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    P: Program<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let stdout = run_ok(P::path(), &["--version"])?;
 | 
			
		||||
    assert!(stdout.contains(tools_version()));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -51,21 +63,25 @@ macro_rules! test_accepts_version {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn accepts_v() -> Result<()> {
 | 
			
		||||
            test_version_short($program)
 | 
			
		||||
            test_version_short::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn accepts_version() -> Result<()> {
 | 
			
		||||
            test_version_long($program)
 | 
			
		||||
            test_version_long::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
pub fn test_rejects_bad_option(program: &str) -> Result<()> {
 | 
			
		||||
    let stderr = run_fail(program, &["--hedgehogs-only"])?;
 | 
			
		||||
    assert!(stderr.contains("unrecognized option \'--hedgehogs-only\'"));
 | 
			
		||||
pub fn test_rejects_bad_option<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    P: Program<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let option = "--hedgehogs-only";
 | 
			
		||||
    let stderr = run_fail(P::path(), &[option])?;
 | 
			
		||||
    assert!(stderr.contains(&P::bad_option_hint(option)));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +90,7 @@ macro_rules! test_rejects_bad_option {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn rejects_bad_option() -> Result<()> {
 | 
			
		||||
            test_rejects_bad_option($program)
 | 
			
		||||
            test_rejects_bad_option::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,9 @@ use crate::common::*;
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
// wrappers
 | 
			
		||||
 | 
			
		||||
pub fn with_output_md_untouched(
 | 
			
		||||
type ArgsBuilder = fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>;
 | 
			
		||||
 | 
			
		||||
fn with_output_md_untouched(
 | 
			
		||||
    td: &mut TestDir,
 | 
			
		||||
    input: &str,
 | 
			
		||||
    thunk: &dyn Fn(&[&str]) -> Result<()>,
 | 
			
		||||
@@ -16,7 +18,19 @@ pub fn with_output_md_untouched(
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn input_arg_only(
 | 
			
		||||
fn with_output_superblock_zeroed(
 | 
			
		||||
    td: &mut TestDir,
 | 
			
		||||
    input: &str,
 | 
			
		||||
    thunk: &dyn Fn(&[&str]) -> Result<()>,
 | 
			
		||||
) -> Result<()> {
 | 
			
		||||
    let output = mk_zeroed_md(td)?;
 | 
			
		||||
    ensure_superblock_zeroed(&output, || {
 | 
			
		||||
        let args = ["-i", input, "-o", output.to_str().unwrap()];
 | 
			
		||||
        thunk(&args)
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn input_arg_only(
 | 
			
		||||
    _td: &mut TestDir,
 | 
			
		||||
    input: &str,
 | 
			
		||||
    thunk: &dyn Fn(&[&str]) -> Result<()>,
 | 
			
		||||
@@ -25,12 +39,22 @@ pub fn input_arg_only(
 | 
			
		||||
    thunk(&args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn build_args_fn(t: ArgType) -> Result<ArgsBuilder> {
 | 
			
		||||
    match t {
 | 
			
		||||
        ArgType::InputArg => Ok(input_arg_only),
 | 
			
		||||
        ArgType::IoOptions => Ok(with_output_md_untouched),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
// test invalid arguments
 | 
			
		||||
 | 
			
		||||
pub fn test_missing_input_arg(program: &str) -> Result<()> {
 | 
			
		||||
    let stderr = run_fail(program, &[])?;
 | 
			
		||||
    assert!(stderr.contains(msg::MISSING_INPUT_ARG));
 | 
			
		||||
pub fn test_missing_input_arg<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    P: InputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let stderr = run_fail(P::path(), &[])?;
 | 
			
		||||
    assert!(stderr.contains(P::missing_input_arg()));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -39,18 +63,21 @@ macro_rules! test_missing_input_arg {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn missing_input_arg() -> Result<()> {
 | 
			
		||||
            test_missing_input_arg($program)
 | 
			
		||||
            test_missing_input_arg::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_missing_input_option(program: &str) -> Result<()> {
 | 
			
		||||
pub fn test_missing_input_option<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    P: InputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
    let output = mk_zeroed_md(&mut td)?;
 | 
			
		||||
    ensure_untouched(&output, || {
 | 
			
		||||
        let args = ["-o", output.to_str().unwrap()];
 | 
			
		||||
        let stderr = run_fail(program, &args)?;
 | 
			
		||||
        assert!(stderr.contains(msg::MISSING_INPUT_ARG));
 | 
			
		||||
        let stderr = run_fail(P::path(), &args)?;
 | 
			
		||||
        assert!(stderr.contains(P::missing_input_arg()));
 | 
			
		||||
        Ok(())
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
@@ -60,48 +87,44 @@ macro_rules! test_missing_input_option {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn missing_input_option() -> Result<()> {
 | 
			
		||||
            test_missing_input_option($program)
 | 
			
		||||
            test_missing_input_option::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_input_file_not_found<F>(program: &str, wrapper: F) -> Result<()>
 | 
			
		||||
pub fn test_input_file_not_found<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>,
 | 
			
		||||
    P: InputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
 | 
			
		||||
    let wrapper = build_args_fn(P::arg_type())?;
 | 
			
		||||
    wrapper(&mut td, "no-such-file", &|args: &[&str]| {
 | 
			
		||||
        let stderr = run_fail(program, args)?;
 | 
			
		||||
        assert!(stderr.contains(msg::FILE_NOT_FOUND));
 | 
			
		||||
        let stderr = run_fail(P::path(), args)?;
 | 
			
		||||
        assert!(stderr.contains(P::file_not_found()));
 | 
			
		||||
        Ok(())
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_input_file_not_found {
 | 
			
		||||
    ($program: ident, ARG) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn input_file_not_found() -> Result<()> {
 | 
			
		||||
            test_input_file_not_found($program, input_arg_only)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($program: ident, OPTION) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn input_file_not_found() -> Result<()> {
 | 
			
		||||
            test_input_file_not_found($program, with_output_md_untouched)
 | 
			
		||||
            test_input_file_not_found::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_input_cannot_be_a_directory<F>(program: &str, wrapper: F) -> Result<()>
 | 
			
		||||
pub fn test_input_cannot_be_a_directory<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>,
 | 
			
		||||
    P: InputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
 | 
			
		||||
    let wrapper = build_args_fn(P::arg_type())?;
 | 
			
		||||
    wrapper(&mut td, "/tmp", &|args: &[&str]| {
 | 
			
		||||
        let stderr = run_fail(program, args)?;
 | 
			
		||||
        let stderr = run_fail(P::path(), args)?;
 | 
			
		||||
        assert!(stderr.contains("Not a block device or regular file"));
 | 
			
		||||
        Ok(())
 | 
			
		||||
    })
 | 
			
		||||
@@ -109,23 +132,17 @@ where
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_input_cannot_be_a_directory {
 | 
			
		||||
    ($program: ident, ARG) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn input_cannot_be_a_directory() -> Result<()> {
 | 
			
		||||
            test_input_cannot_be_a_directory($program, input_arg_only)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($program: ident, OPTION) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn input_cannot_be_a_directory() -> Result<()> {
 | 
			
		||||
            test_input_cannot_be_a_directory($program, with_output_md_untouched)
 | 
			
		||||
            test_input_cannot_be_a_directory::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_unreadable_input_file<F>(program: &str, wrapper: F) -> Result<()>
 | 
			
		||||
pub fn test_unreadable_input_file<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>,
 | 
			
		||||
    P: InputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
 | 
			
		||||
@@ -133,8 +150,9 @@ where
 | 
			
		||||
    let input = mk_valid_md(&mut td)?;
 | 
			
		||||
    duct::cmd!("chmod", "-r", &input).run()?;
 | 
			
		||||
 | 
			
		||||
    let wrapper = build_args_fn(P::arg_type())?;
 | 
			
		||||
    wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
 | 
			
		||||
        let stderr = run_fail(program, args)?;
 | 
			
		||||
        let stderr = run_fail(P::path(), args)?;
 | 
			
		||||
        assert!(stderr.contains("Permission denied"));
 | 
			
		||||
        Ok(())
 | 
			
		||||
    })
 | 
			
		||||
@@ -142,16 +160,10 @@ where
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_unreadable_input_file {
 | 
			
		||||
    ($program: ident, ARG) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn unreadable_input_file() -> Result<()> {
 | 
			
		||||
            test_unreadable_input_file($program, input_arg_only)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($program: ident, OPTION) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn unreadable_input_file() -> Result<()> {
 | 
			
		||||
            test_unreadable_input_file($program, with_output_md_untouched)
 | 
			
		||||
            test_unreadable_input_file::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -159,17 +171,18 @@ macro_rules! test_unreadable_input_file {
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
// test invalid content
 | 
			
		||||
 | 
			
		||||
pub fn test_help_message_for_tiny_input_file<F>(program: &str, wrapper: F) -> Result<()>
 | 
			
		||||
pub fn test_help_message_for_tiny_input_file<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>,
 | 
			
		||||
    P: BinaryInputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
 | 
			
		||||
    let input = td.mk_path("meta.bin");
 | 
			
		||||
    file_utils::create_sized_file(&input, 1024)?;
 | 
			
		||||
 | 
			
		||||
    let wrapper = build_args_fn(P::arg_type())?;
 | 
			
		||||
    wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
 | 
			
		||||
        let stderr = run_fail(program, args)?;
 | 
			
		||||
        let stderr = run_fail(P::path(), args)?;
 | 
			
		||||
        assert!(stderr.contains("Metadata device/file too small.  Is this binary metadata?"));
 | 
			
		||||
        Ok(())
 | 
			
		||||
    })
 | 
			
		||||
@@ -177,23 +190,17 @@ where
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_help_message_for_tiny_input_file {
 | 
			
		||||
    ($program: ident, ARG) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn prints_help_message_for_tiny_input_file() -> Result<()> {
 | 
			
		||||
            test_help_message_for_tiny_input_file($program, input_arg_only)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($program: ident, OPTION) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn prints_help_message_for_tiny_input_file() -> Result<()> {
 | 
			
		||||
            test_help_message_for_tiny_input_file($program, with_output_md_untouched)
 | 
			
		||||
            test_help_message_for_tiny_input_file::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_spot_xml_data<F>(program: &str, name: &str, wrapper: F) -> Result<()>
 | 
			
		||||
pub fn test_spot_xml_data<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>,
 | 
			
		||||
    P: BinaryInputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
 | 
			
		||||
@@ -202,12 +209,13 @@ where
 | 
			
		||||
    let mut gen = FragmentedS::new(4, 10240);
 | 
			
		||||
    write_xml(&input, &mut gen)?;
 | 
			
		||||
 | 
			
		||||
    let wrapper = build_args_fn(P::arg_type())?;
 | 
			
		||||
    wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
 | 
			
		||||
        let stderr = run_fail(program, args)?;
 | 
			
		||||
        let stderr = run_fail(P::path(), args)?;
 | 
			
		||||
        eprintln!("{}", stderr);
 | 
			
		||||
        let msg = format!(
 | 
			
		||||
            "This looks like XML.  {} only checks the binary metadata format.",
 | 
			
		||||
            name
 | 
			
		||||
            P::name()
 | 
			
		||||
        );
 | 
			
		||||
        assert!(stderr.contains(&msg));
 | 
			
		||||
        Ok(())
 | 
			
		||||
@@ -216,46 +224,38 @@ where
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_spot_xml_data {
 | 
			
		||||
    ($program: ident, $name: expr, ARG) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn spot_xml_data() -> Result<()> {
 | 
			
		||||
            test_spot_xml_data($program, $name, input_arg_only)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($program: ident, $name: expr, OPTION) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn spot_xml_data() -> Result<()> {
 | 
			
		||||
            test_spot_xml_data($program, $name, with_output_md_untouched)
 | 
			
		||||
            test_spot_xml_data::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_corrupted_input_data<F>(program: &str, wrapper: F) -> Result<()>
 | 
			
		||||
pub fn test_corrupted_input_data<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>,
 | 
			
		||||
    P: InputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
    let input = mk_zeroed_md(&mut td)?;
 | 
			
		||||
 | 
			
		||||
    let wrapper = match P::arg_type() {
 | 
			
		||||
        ArgType::InputArg => input_arg_only,
 | 
			
		||||
        ArgType::IoOptions => with_output_superblock_zeroed,
 | 
			
		||||
    };
 | 
			
		||||
    wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
 | 
			
		||||
        let stderr = run_fail(program, args)?;
 | 
			
		||||
        assert!(stderr.contains("bad checksum in superblock"));
 | 
			
		||||
        let stderr = run_fail(P::path(), args)?;
 | 
			
		||||
        assert!(stderr.contains(P::corrupted_input()));
 | 
			
		||||
        Ok(())
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_corrupted_input_data {
 | 
			
		||||
    ($program: ident, ARG) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn corrupted_input_data() -> Result<()> {
 | 
			
		||||
            test_corrupted_input_data($program, input_arg_only)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($program: ident, OPTION) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn corrupted_input_data() -> Result<()> {
 | 
			
		||||
            test_corrupted_input_data($program, with_output_md_untouched)
 | 
			
		||||
            test_corrupted_input_data::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,22 +19,33 @@ use test_dir::TestDir;
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
#[cfg(not(feature = "rust_tests"))]
 | 
			
		||||
pub mod msg {
 | 
			
		||||
pub mod cpp_msg {
 | 
			
		||||
    pub const FILE_NOT_FOUND: &str = "No such file or directory";
 | 
			
		||||
    pub const MISSING_INPUT_ARG: &str = "No input file provided";
 | 
			
		||||
    pub const MISSING_OUTPUT_ARG: &str = "No output file provided";
 | 
			
		||||
    pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
 | 
			
		||||
 | 
			
		||||
    pub fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        format!("unrecognized option '{}'", option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "rust_tests")]
 | 
			
		||||
pub mod msg {
 | 
			
		||||
pub mod rust_msg {
 | 
			
		||||
    pub const FILE_NOT_FOUND: &str = "Couldn't find input file";
 | 
			
		||||
    pub const MISSING_INPUT_ARG: &str =
 | 
			
		||||
        "The following required arguments were not provided\n    -i <FILE>";
 | 
			
		||||
    pub const MISSING_OUTPUT_ARG: &str =
 | 
			
		||||
        "The following required arguments were not provided\n    -o <FILE>";
 | 
			
		||||
    pub const MISSING_INPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
 | 
			
		||||
    pub const MISSING_OUTPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
 | 
			
		||||
    pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
 | 
			
		||||
 | 
			
		||||
    pub fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        format!("Found argument '{}' which wasn't expected", option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(feature = "rust_tests"))]
 | 
			
		||||
pub use cpp_msg as msg;
 | 
			
		||||
#[cfg(feature = "rust_tests")]
 | 
			
		||||
pub use rust_msg as msg;
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
@@ -86,6 +97,42 @@ pub const THIN_GENERATE_DAMAGE: &str = path_to_cpp!("thin_generate_damage"); //
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
pub enum ArgType {
 | 
			
		||||
    InputArg,
 | 
			
		||||
    IoOptions,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Program<'a> {
 | 
			
		||||
    fn name() -> &'a str;
 | 
			
		||||
    fn path() -> &'a str;
 | 
			
		||||
    fn usage() -> &'a str;
 | 
			
		||||
    fn arg_type() -> ArgType;
 | 
			
		||||
 | 
			
		||||
    // error messages
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait InputProgram<'a>: Program<'a> {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<PathBuf>;
 | 
			
		||||
 | 
			
		||||
    // error messages
 | 
			
		||||
    fn missing_input_arg() -> &'a str;
 | 
			
		||||
    fn file_not_found() -> &'a str;
 | 
			
		||||
    fn corrupted_input() -> &'a str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait BinaryInputProgram<'a>: InputProgram<'a> {}
 | 
			
		||||
 | 
			
		||||
pub trait OutputProgram<'a>: InputProgram<'a> {
 | 
			
		||||
    // error messages
 | 
			
		||||
    fn missing_output_arg() -> &'a str;
 | 
			
		||||
    fn file_not_found() -> &'a str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait BinaryOutputProgram<'a>: OutputProgram<'a> {}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Returns stdout. The command must return zero.
 | 
			
		||||
pub fn run_ok(program: &str, args: &[&str]) -> Result<String> {
 | 
			
		||||
    let command = duct::cmd(program, args).stdout_capture().stderr_capture();
 | 
			
		||||
@@ -291,4 +338,13 @@ where
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn ensure_superblock_zeroed<F>(p: &PathBuf, thunk: F) -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn() -> Result<()>,
 | 
			
		||||
{
 | 
			
		||||
    thunk()?;
 | 
			
		||||
    assert!(superblock_all_zeroes(p)?);
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -3,85 +3,85 @@ use crate::common::*;
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
// test invalid arguments
 | 
			
		||||
 | 
			
		||||
pub fn test_missing_output_option<F>(program: &str, mk_input: F) -> Result<()>
 | 
			
		||||
pub fn test_missing_output_option<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir) -> Result<PathBuf>,
 | 
			
		||||
    P: OutputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
    let input = mk_input(&mut td)?;
 | 
			
		||||
    let stderr = run_fail(program, &["-i", input.to_str().unwrap()])?;
 | 
			
		||||
    assert!(stderr.contains(msg::MISSING_OUTPUT_ARG));
 | 
			
		||||
    let input = P::mk_valid_input(&mut td)?;
 | 
			
		||||
    let stderr = run_fail(P::path(), &["-i", input.to_str().unwrap()])?;
 | 
			
		||||
    assert!(stderr.contains(P::missing_output_arg()));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_missing_output_option {
 | 
			
		||||
    ($program: ident, $mk_input: ident) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn missing_output_option() -> Result<()> {
 | 
			
		||||
            test_missing_output_option($program, $mk_input)
 | 
			
		||||
            test_missing_output_option::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_output_file_not_found<F>(program: &str, mk_input: F) -> Result<()>
 | 
			
		||||
pub fn test_output_file_not_found<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir) -> Result<PathBuf>,
 | 
			
		||||
    P: OutputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
    let input = mk_input(&mut td)?;
 | 
			
		||||
    let input = P::mk_valid_input(&mut td)?;
 | 
			
		||||
    let stderr = run_fail(
 | 
			
		||||
        program,
 | 
			
		||||
        P::path(),
 | 
			
		||||
        &["-i", input.to_str().unwrap(), "-o", "no-such-file"],
 | 
			
		||||
    )?;
 | 
			
		||||
    assert!(stderr.contains(msg::FILE_NOT_FOUND));
 | 
			
		||||
    assert!(stderr.contains(<P as OutputProgram>::file_not_found()));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_output_file_not_found {
 | 
			
		||||
    ($program: ident, $mk_input: ident) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn output_file_not_found() -> Result<()> {
 | 
			
		||||
            test_output_file_not_found($program, $mk_input)
 | 
			
		||||
            test_output_file_not_found::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_output_cannot_be_a_directory<F>(program: &str, mk_input: F) -> Result<()>
 | 
			
		||||
pub fn test_output_cannot_be_a_directory<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir) -> Result<PathBuf>,
 | 
			
		||||
    P: OutputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
    let input = mk_input(&mut td)?;
 | 
			
		||||
    let stderr = run_fail(program, &["-i", input.to_str().unwrap(), "-o", "/tmp"])?;
 | 
			
		||||
    assert!(stderr.contains(msg::FILE_NOT_FOUND));
 | 
			
		||||
    let input = P::mk_valid_input(&mut td)?;
 | 
			
		||||
    let stderr = run_fail(P::path(), &["-i", input.to_str().unwrap(), "-o", "/tmp"])?;
 | 
			
		||||
    assert!(stderr.contains("Not a block device or regular file"));
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_output_cannot_be_a_directory {
 | 
			
		||||
    ($program: ident, $mk_input: ident) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn output_cannot_be_a_directory() -> Result<()> {
 | 
			
		||||
            test_output_cannot_be_a_directory($program, $mk_input)
 | 
			
		||||
            test_output_cannot_be_a_directory::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn test_unwritable_output_file<F>(program: &str, mk_input: F) -> Result<()>
 | 
			
		||||
pub fn test_unwritable_output_file<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir) -> Result<PathBuf>,
 | 
			
		||||
    P: OutputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
    let input = mk_input(&mut td)?;
 | 
			
		||||
    let input = P::mk_valid_input(&mut td)?;
 | 
			
		||||
 | 
			
		||||
    let output = td.mk_path("meta.bin");
 | 
			
		||||
    let _file = file_utils::create_sized_file(&output, 4096);
 | 
			
		||||
    duct::cmd!("chmod", "-w", &output).run()?;
 | 
			
		||||
 | 
			
		||||
    let stderr = run_fail(
 | 
			
		||||
        program,
 | 
			
		||||
        P::path(),
 | 
			
		||||
        &[
 | 
			
		||||
            "-i",
 | 
			
		||||
            input.to_str().unwrap(),
 | 
			
		||||
@@ -95,10 +95,10 @@ where
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_unwritable_output_file {
 | 
			
		||||
    ($program: ident, $mk_input: ident) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn unwritable_output_file() -> Result<()> {
 | 
			
		||||
            test_unwritable_output_file($program, $mk_input)
 | 
			
		||||
            test_unwritable_output_file::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -107,18 +107,18 @@ macro_rules! test_unwritable_output_file {
 | 
			
		||||
// test invalid content
 | 
			
		||||
 | 
			
		||||
// currently thin/cache_restore only
 | 
			
		||||
pub fn test_tiny_output_file<F>(program: &str, mk_input: F) -> Result<()>
 | 
			
		||||
pub fn test_tiny_output_file<'a, P>() -> Result<()>
 | 
			
		||||
where
 | 
			
		||||
    F: Fn(&mut TestDir) -> Result<PathBuf>,
 | 
			
		||||
    P: BinaryOutputProgram<'a>,
 | 
			
		||||
{
 | 
			
		||||
    let mut td = TestDir::new()?;
 | 
			
		||||
    let input = mk_input(&mut td)?;
 | 
			
		||||
    let input = P::mk_valid_input(&mut td)?;
 | 
			
		||||
 | 
			
		||||
    let output = td.mk_path("meta.bin");
 | 
			
		||||
    let _file = file_utils::create_sized_file(&output, 4096);
 | 
			
		||||
 | 
			
		||||
    let stderr = run_fail(
 | 
			
		||||
        program,
 | 
			
		||||
        P::path(),
 | 
			
		||||
        &[
 | 
			
		||||
            "-i",
 | 
			
		||||
            input.to_str().unwrap(),
 | 
			
		||||
@@ -132,10 +132,10 @@ where
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_tiny_output_file {
 | 
			
		||||
    ($program: ident, $mk_input: ident) => {
 | 
			
		||||
    ($program: ident) => {
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn tiny_output_file() -> Result<()> {
 | 
			
		||||
            test_tiny_output_file($program, $mk_input)
 | 
			
		||||
            test_tiny_output_file::<$program>()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,20 +22,66 @@ const USAGE: &str = "Usage: thin_check [options] {device|file}\n\
 | 
			
		||||
                       {--skip-mappings}\n  \
 | 
			
		||||
                       {--super-block-only}";
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
 | 
			
		||||
struct ThinCheck;
 | 
			
		||||
 | 
			
		||||
impl<'a> Program<'a> for ThinCheck {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_check"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_CHECK
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::InputArg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> InputProgram<'a> for ThinCheck {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
 | 
			
		||||
        mk_valid_md(td)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_input_arg() -> &'a str {
 | 
			
		||||
        msg::MISSING_INPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn corrupted_input() -> &'a str {
 | 
			
		||||
        msg::BAD_SUPERBLOCK
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> BinaryInputProgram<'_> for ThinCheck {}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_CHECK, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_CHECK);
 | 
			
		||||
test_rejects_bad_option!(THIN_CHECK);
 | 
			
		||||
test_accepts_help!(ThinCheck);
 | 
			
		||||
test_accepts_version!(ThinCheck);
 | 
			
		||||
test_rejects_bad_option!(ThinCheck);
 | 
			
		||||
 | 
			
		||||
test_missing_input_arg!(THIN_CHECK);
 | 
			
		||||
test_input_file_not_found!(THIN_CHECK, ARG);
 | 
			
		||||
test_input_cannot_be_a_directory!(THIN_CHECK, ARG);
 | 
			
		||||
test_unreadable_input_file!(THIN_CHECK, ARG);
 | 
			
		||||
test_missing_input_arg!(ThinCheck);
 | 
			
		||||
test_input_file_not_found!(ThinCheck);
 | 
			
		||||
test_input_cannot_be_a_directory!(ThinCheck);
 | 
			
		||||
test_unreadable_input_file!(ThinCheck);
 | 
			
		||||
 | 
			
		||||
test_help_message_for_tiny_input_file!(THIN_CHECK, ARG);
 | 
			
		||||
test_spot_xml_data!(THIN_CHECK, "thin_check", ARG);
 | 
			
		||||
test_corrupted_input_data!(THIN_CHECK, ARG);
 | 
			
		||||
test_help_message_for_tiny_input_file!(ThinCheck);
 | 
			
		||||
test_spot_xml_data!(ThinCheck);
 | 
			
		||||
test_corrupted_input_data!(ThinCheck);
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
// test exclusive flags
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,35 @@ const USAGE: &str = "Usage: thin_delta [options] <device or file>\n\
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_DELTA, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_DELTA);
 | 
			
		||||
test_rejects_bad_option!(THIN_DELTA);
 | 
			
		||||
struct ThinDelta;
 | 
			
		||||
 | 
			
		||||
impl<'a> Program<'a> for ThinDelta {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_delta"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_DELTA
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::InputArg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        cpp_msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(ThinDelta);
 | 
			
		||||
test_accepts_version!(ThinDelta);
 | 
			
		||||
test_rejects_bad_option!(ThinDelta);
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,16 +23,60 @@ const USAGE: &str = "Usage: thin_dump [options] {device|file}\n\
 | 
			
		||||
                       {--skip-mappings}\n  \
 | 
			
		||||
                       {-V|--version}";
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
 | 
			
		||||
struct ThinDump;
 | 
			
		||||
 | 
			
		||||
impl<'a> Program<'a> for ThinDump {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_dump"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_DUMP
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::InputArg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> InputProgram<'a> for ThinDump {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
 | 
			
		||||
        mk_valid_md(td)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_input_arg() -> &'a str {
 | 
			
		||||
        msg::MISSING_INPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn corrupted_input() -> &'a str {
 | 
			
		||||
        msg::BAD_SUPERBLOCK
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_DUMP, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_DUMP);
 | 
			
		||||
test_rejects_bad_option!(THIN_DUMP);
 | 
			
		||||
test_accepts_help!(ThinDump);
 | 
			
		||||
test_accepts_version!(ThinDump);
 | 
			
		||||
test_rejects_bad_option!(ThinDump);
 | 
			
		||||
 | 
			
		||||
test_missing_input_arg!(THIN_DUMP);
 | 
			
		||||
test_input_file_not_found!(THIN_DUMP, ARG);
 | 
			
		||||
test_input_cannot_be_a_directory!(THIN_DUMP, ARG);
 | 
			
		||||
test_unreadable_input_file!(THIN_DUMP, ARG);
 | 
			
		||||
test_missing_input_arg!(ThinDump);
 | 
			
		||||
test_input_file_not_found!(ThinDump);
 | 
			
		||||
test_input_cannot_be_a_directory!(ThinDump);
 | 
			
		||||
test_unreadable_input_file!(ThinDump);
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
// test dump & restore cycle
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ mod common;
 | 
			
		||||
use common::common_args::*;
 | 
			
		||||
use common::input_arg::*;
 | 
			
		||||
use common::output_option::*;
 | 
			
		||||
use common::test_dir::*;
 | 
			
		||||
use common::*;
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
@@ -28,12 +29,66 @@ const USAGE: &str = concat!(
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_METADATA_PACK, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_METADATA_PACK);
 | 
			
		||||
test_rejects_bad_option!(THIN_METADATA_PACK);
 | 
			
		||||
struct ThinMetadataPack;
 | 
			
		||||
 | 
			
		||||
test_missing_input_option!(THIN_METADATA_PACK);
 | 
			
		||||
test_missing_output_option!(THIN_METADATA_PACK, mk_valid_md);
 | 
			
		||||
test_input_file_not_found!(THIN_METADATA_PACK, OPTION);
 | 
			
		||||
impl<'a> Program<'a> for ThinMetadataPack {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_metadata_pack"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_METADATA_PACK
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::IoOptions
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        rust_msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> InputProgram<'a> for ThinMetadataPack {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
 | 
			
		||||
        mk_valid_md(td)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        rust_msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_input_arg() -> &'a str {
 | 
			
		||||
        rust_msg::MISSING_INPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn corrupted_input() -> &'a str {
 | 
			
		||||
        rust_msg::BAD_SUPERBLOCK
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> OutputProgram<'a> for ThinMetadataPack {
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        rust_msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_output_arg() -> &'a str {
 | 
			
		||||
        rust_msg::MISSING_OUTPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(ThinMetadataPack);
 | 
			
		||||
test_accepts_version!(ThinMetadataPack);
 | 
			
		||||
test_rejects_bad_option!(ThinMetadataPack);
 | 
			
		||||
 | 
			
		||||
test_missing_input_option!(ThinMetadataPack);
 | 
			
		||||
test_missing_output_option!(ThinMetadataPack);
 | 
			
		||||
test_input_file_not_found!(ThinMetadataPack);
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -29,15 +29,69 @@ const USAGE: &str = concat!(
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_METADATA_UNPACK, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_METADATA_UNPACK);
 | 
			
		||||
test_rejects_bad_option!(THIN_METADATA_UNPACK);
 | 
			
		||||
struct ThinMetadataUnpack;
 | 
			
		||||
 | 
			
		||||
test_missing_input_option!(THIN_METADATA_PACK);
 | 
			
		||||
test_input_file_not_found!(THIN_METADATA_UNPACK, OPTION);
 | 
			
		||||
test_corrupted_input_data!(THIN_METADATA_UNPACK, OPTION);
 | 
			
		||||
impl<'a> Program<'a> for ThinMetadataUnpack {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_metadata_pack"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
test_missing_output_option!(THIN_METADATA_UNPACK, mk_valid_md);
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_METADATA_UNPACK
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::IoOptions
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        rust_msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> InputProgram<'a> for ThinMetadataUnpack {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
 | 
			
		||||
        mk_zeroed_md(td) // FIXME: make a real pack file
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        rust_msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_input_arg() -> &'a str {
 | 
			
		||||
        rust_msg::MISSING_INPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn corrupted_input() -> &'a str {
 | 
			
		||||
        "Not a pack file"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> OutputProgram<'a> for ThinMetadataUnpack {
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        rust_msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_output_arg() -> &'a str {
 | 
			
		||||
        rust_msg::MISSING_OUTPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(ThinMetadataUnpack);
 | 
			
		||||
test_accepts_version!(ThinMetadataUnpack);
 | 
			
		||||
test_rejects_bad_option!(ThinMetadataUnpack);
 | 
			
		||||
 | 
			
		||||
test_missing_input_option!(ThinMetadataUnpack);
 | 
			
		||||
test_input_file_not_found!(ThinMetadataUnpack);
 | 
			
		||||
test_corrupted_input_data!(ThinMetadataUnpack);
 | 
			
		||||
 | 
			
		||||
test_missing_output_option!(ThinMetadataUnpack);
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,14 +22,68 @@ const USAGE: &str = "Usage: thin_repair [options] {device|file}\n\
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_REPAIR, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_REPAIR);
 | 
			
		||||
test_rejects_bad_option!(THIN_REPAIR);
 | 
			
		||||
struct ThinRepair;
 | 
			
		||||
 | 
			
		||||
test_input_file_not_found!(THIN_REPAIR, OPTION);
 | 
			
		||||
test_corrupted_input_data!(THIN_REPAIR, OPTION);
 | 
			
		||||
impl<'a> Program<'a> for ThinRepair {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_repair"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
test_missing_output_option!(THIN_REPAIR, mk_valid_md);
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_REPAIR
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::IoOptions
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        cpp_msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> InputProgram<'a> for ThinRepair {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
 | 
			
		||||
        mk_valid_md(td)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        cpp_msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_input_arg() -> &'a str {
 | 
			
		||||
        cpp_msg::MISSING_INPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn corrupted_input() -> &'a str {
 | 
			
		||||
        "The following field needs to be provided on the command line due to corruption in the superblock"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> OutputProgram<'a> for ThinRepair {
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        cpp_msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_output_arg() -> &'a str {
 | 
			
		||||
        cpp_msg::MISSING_OUTPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(ThinRepair);
 | 
			
		||||
test_accepts_version!(ThinRepair);
 | 
			
		||||
test_rejects_bad_option!(ThinRepair);
 | 
			
		||||
 | 
			
		||||
test_input_file_not_found!(ThinRepair);
 | 
			
		||||
test_corrupted_input_data!(ThinRepair);
 | 
			
		||||
 | 
			
		||||
test_missing_output_option!(ThinRepair);
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
// test output to a small file
 | 
			
		||||
 
 | 
			
		||||
@@ -23,15 +23,71 @@ const USAGE: &str = "Usage: thin_restore [options]\n\
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_RESTORE, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_RESTORE);
 | 
			
		||||
struct ThinRestore;
 | 
			
		||||
 | 
			
		||||
test_missing_input_option!(THIN_RESTORE);
 | 
			
		||||
test_input_file_not_found!(THIN_RESTORE, OPTION);
 | 
			
		||||
test_corrupted_input_data!(THIN_RESTORE, OPTION);
 | 
			
		||||
impl<'a> Program<'a> for ThinRestore {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_restore"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
test_missing_output_option!(THIN_RESTORE, mk_valid_xml);
 | 
			
		||||
test_tiny_output_file!(THIN_RESTORE, mk_valid_xml);
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_RESTORE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::IoOptions
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> InputProgram<'a> for ThinRestore {
 | 
			
		||||
    fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
 | 
			
		||||
        mk_valid_md(td)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_input_arg() -> &'a str {
 | 
			
		||||
        msg::MISSING_INPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn corrupted_input() -> &'a str {
 | 
			
		||||
        "" // we don't intent to verify error messages of XML parsing
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> OutputProgram<'a> for ThinRestore {
 | 
			
		||||
    fn file_not_found() -> &'a str {
 | 
			
		||||
        msg::FILE_NOT_FOUND
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn missing_output_arg() -> &'a str {
 | 
			
		||||
        msg::MISSING_OUTPUT_ARG
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> BinaryOutputProgram<'_> for ThinRestore {}
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(ThinRestore);
 | 
			
		||||
test_accepts_version!(ThinRestore);
 | 
			
		||||
 | 
			
		||||
test_missing_input_option!(ThinRestore);
 | 
			
		||||
test_input_file_not_found!(ThinRestore);
 | 
			
		||||
test_corrupted_input_data!(ThinRestore);
 | 
			
		||||
 | 
			
		||||
test_missing_output_option!(ThinRestore);
 | 
			
		||||
test_tiny_output_file!(ThinRestore);
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,35 @@ const USAGE: &str = "Usage: thin_rmap [options] {device|file}\n\
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(THIN_RMAP, USAGE);
 | 
			
		||||
test_accepts_version!(THIN_RMAP);
 | 
			
		||||
test_rejects_bad_option!(THIN_RMAP);
 | 
			
		||||
struct ThinRmap;
 | 
			
		||||
 | 
			
		||||
impl<'a> Program<'a> for ThinRmap {
 | 
			
		||||
    fn name() -> &'a str {
 | 
			
		||||
        "thin_rmap"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn path() -> &'a str {
 | 
			
		||||
        THIN_RMAP
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn usage() -> &'a str {
 | 
			
		||||
        USAGE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arg_type() -> ArgType {
 | 
			
		||||
        ArgType::InputArg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bad_option_hint(option: &str) -> String {
 | 
			
		||||
        cpp_msg::bad_option_hint(option)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
test_accepts_help!(ThinRmap);
 | 
			
		||||
test_accepts_version!(ThinRmap);
 | 
			
		||||
test_rejects_bad_option!(ThinRmap);
 | 
			
		||||
 | 
			
		||||
//------------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user