diff --git a/tests/cache_check.rs b/tests/cache_check.rs index f9ec093..9a629d1 100644 --- a/tests/cache_check.rs +++ b/tests/cache_check.rs @@ -1,89 +1,47 @@ use anyhow::Result; -use duct::cmd; -use thinp::version::tools_version; mod common; +use common::common_args::*; +use common::input_arg::*; use common::test_dir::*; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = cache_check!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +const USAGE: &str = "Usage: cache_check [options] {device|file}\n\ + Options:\n \ + {-q|--quiet}\n \ + {-h|--help}\n \ + {-V|--version}\n \ + {--clear-needs-check-flag}\n \ + {--super-block-only}\n \ + {--skip-mappings}\n \ + {--skip-hints}\n \ + {--skip-discards}"; -#[test] -fn accepts_version() -> Result<()> { - let stdout = cache_check!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +//------------------------------------------ -const USAGE: &str = "Usage: cache_check [options] {device|file}\nOptions:\n {-q|--quiet}\n {-h|--help}\n {-V|--version}\n {--clear-needs-check-flag}\n {--super-block-only}\n {--skip-mappings}\n {--skip-hints}\n {--skip-discards}"; +test_accepts_help!(CACHE_CHECK, USAGE); +test_accepts_version!(CACHE_CHECK); +test_rejects_bad_option!(CACHE_CHECK); -#[test] -fn accepts_h() -> Result<()> { - let stdout = cache_check!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +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); -#[test] -fn accepts_help() -> Result<()> { - let stdout = cache_check!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +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); -#[test] -fn missing_input_arg() -> Result<()> { - let stderr = run_fail(cache_check!())?; - assert!(stderr.contains(msg::MISSING_INPUT_ARG)); - Ok(()) -} - -#[test] -fn input_file_not_found() -> Result<()> { - let stderr = run_fail(cache_check!("/arbitrary/filename"))?; - assert!(stderr.contains(msg::FILE_NOT_FOUND)); - Ok(()) -} - -#[test] -fn metadata_cannot_be_a_directory() -> Result<()> { - let stderr = run_fail(cache_check!("/tmp"))?; - assert!(stderr.contains("Not a block device or regular file")); - Ok(()) -} - -#[test] -fn unreadable_metadata() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_valid_md(&mut td)?; - cmd!("chmod", "-r", &md).run()?; - let stderr = run_fail(cache_check!(&md))?; - assert!(stderr.contains("Permission denied")); - Ok(()) -} - -#[test] -fn corrupt_metadata() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - run_fail(cache_check!(&md))?; - Ok(()) -} +//------------------------------------------ #[test] fn failing_q() -> Result<()> { let mut td = TestDir::new()?; let md = mk_zeroed_md(&mut td)?; - let output = cache_check!("-q", &md).unchecked().run()?; - assert!(!output.status.success()); + let output = run_fail_raw(CACHE_CHECK, &["-q", md.to_str().unwrap()])?; assert_eq!(output.stdout.len(), 0); assert_eq!(output.stderr.len(), 0); Ok(()) @@ -93,8 +51,7 @@ fn failing_q() -> Result<()> { fn failing_quiet() -> Result<()> { let mut td = TestDir::new()?; let md = mk_zeroed_md(&mut td)?; - let output = cache_check!("--quiet", &md).unchecked().run()?; - assert!(!output.status.success()); + let output = run_fail_raw(CACHE_CHECK, &["--quiet", md.to_str().unwrap()])?; assert_eq!(output.stdout.len(), 0); assert_eq!(output.stderr.len(), 0); Ok(()) diff --git a/tests/common/common_args.rs b/tests/common/common_args.rs new file mode 100644 index 0000000..f4b0a5d --- /dev/null +++ b/tests/common/common_args.rs @@ -0,0 +1,82 @@ +use crate::common::*; +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); + Ok(()) +} + +pub fn test_help_long(program: &str, usage: &str) -> Result<()> { + let stdout = run_ok(program, &["--help"])?; + assert_eq!(stdout, usage); + Ok(()) +} + +#[macro_export] +macro_rules! test_accepts_help { + ($program: ident, $usage: expr) => { + #[test] + fn accepts_h() -> Result<()> { + test_help_short($program, $usage) + } + + #[test] + fn accepts_help() -> Result<()> { + test_help_long($program, $usage) + } + }; +} + +//------------------------------------------ +// version + +pub fn test_version_short(program: &str) -> Result<()> { + let stdout = run_ok(program, &["-V"])?; + assert!(stdout.contains(tools_version())); + Ok(()) +} + +pub fn test_version_long(program: &str) -> Result<()> { + let stdout = run_ok(program, &["--version"])?; + assert!(stdout.contains(tools_version())); + Ok(()) +} + +#[macro_export] +macro_rules! test_accepts_version { + ($program: ident) => { + #[test] + fn accepts_v() -> Result<()> { + test_version_short($program) + } + + #[test] + fn accepts_version() -> Result<()> { + 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\'")); + Ok(()) +} + +#[macro_export] +macro_rules! test_rejects_bad_option { + ($program: ident) => { + #[test] + fn rejects_bad_option() -> Result<()> { + test_rejects_bad_option($program) + } + }; +} + +//------------------------------------------ diff --git a/tests/common/input_arg.rs b/tests/common/input_arg.rs new file mode 100644 index 0000000..30f4400 --- /dev/null +++ b/tests/common/input_arg.rs @@ -0,0 +1,263 @@ +use crate::common::thin_xml_generator::{write_xml, FragmentedS}; +use crate::common::*; + +//------------------------------------------ +// wrappers + +pub fn with_output_md_untouched( + td: &mut TestDir, + input: &str, + thunk: &dyn Fn(&[&str]) -> Result<()>, +) -> Result<()> { + let output = mk_zeroed_md(td)?; + ensure_untouched(&output, || { + let args = ["-i", input, "-o", output.to_str().unwrap()]; + thunk(&args) + }) +} + +pub fn input_arg_only( + _td: &mut TestDir, + input: &str, + thunk: &dyn Fn(&[&str]) -> Result<()>, +) -> Result<()> { + let args = [input]; + thunk(&args) +} + +//------------------------------------------ +// test invalid arguments + +pub fn test_missing_input_arg(program: &str) -> Result<()> { + let stderr = run_fail(program, &[])?; + assert!(stderr.contains(msg::MISSING_INPUT_ARG)); + Ok(()) +} + +#[macro_export] +macro_rules! test_missing_input_arg { + ($program: ident) => { + #[test] + fn missing_input_arg() -> Result<()> { + test_missing_input_arg($program) + } + }; +} + +pub fn test_missing_input_option(program: &str) -> Result<()> { + 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)); + Ok(()) + }) +} + +#[macro_export] +macro_rules! test_missing_input_option { + ($program: ident) => { + #[test] + fn missing_input_option() -> Result<()> { + test_missing_input_option($program) + } + }; +} + +pub fn test_input_file_not_found(program: &str, wrapper: F) -> Result<()> +where + F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>, +{ + let mut td = TestDir::new()?; + + wrapper(&mut td, "no-such-file", &|args: &[&str]| { + let stderr = run_fail(program, args)?; + assert!(stderr.contains(msg::FILE_NOT_FOUND)); + Ok(()) + }) +} + +#[macro_export] +macro_rules! test_input_file_not_found { + ($program: ident, ARG) => { + #[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) + } + }; +} + +pub fn test_input_cannot_be_a_directory(program: &str, wrapper: F) -> Result<()> +where + F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>, +{ + let mut td = TestDir::new()?; + + wrapper(&mut td, "/tmp", &|args: &[&str]| { + let stderr = run_fail(program, args)?; + assert!(stderr.contains("Not a block device or regular file")); + Ok(()) + }) +} + +#[macro_export] +macro_rules! test_input_cannot_be_a_directory { + ($program: ident, ARG) => { + #[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) + } + }; +} + +pub fn test_unreadable_input_file(program: &str, wrapper: F) -> Result<()> +where + F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>, +{ + let mut td = TestDir::new()?; + + // input an unreadable file + let input = mk_valid_md(&mut td)?; + duct::cmd!("chmod", "-r", &input).run()?; + + wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| { + let stderr = run_fail(program, args)?; + assert!(stderr.contains("Permission denied")); + Ok(()) + }) +} + +#[macro_export] +macro_rules! test_unreadable_input_file { + ($program: ident, ARG) => { + #[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 invalid content + +pub fn test_help_message_for_tiny_input_file(program: &str, wrapper: F) -> Result<()> +where + F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>, +{ + let mut td = TestDir::new()?; + + let input = td.mk_path("meta.bin"); + file_utils::create_sized_file(&input, 1024)?; + + wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| { + let stderr = run_fail(program, args)?; + assert!(stderr.contains("Metadata device/file too small. Is this binary metadata?")); + Ok(()) + }) +} + +#[macro_export] +macro_rules! test_help_message_for_tiny_input_file { + ($program: ident, ARG) => { + #[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) + } + }; +} + +pub fn test_spot_xml_data(program: &str, name: &str, wrapper: F) -> Result<()> +where + F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>, +{ + let mut td = TestDir::new()?; + + // input a large xml file + let input = td.mk_path("meta.xml"); + let mut gen = FragmentedS::new(4, 10240); + write_xml(&input, &mut gen)?; + + wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| { + let stderr = run_fail(program, args)?; + eprintln!("{}", stderr); + let msg = format!( + "This looks like XML. {} only checks the binary metadata format.", + name + ); + assert!(stderr.contains(&msg)); + Ok(()) + }) +} + +#[macro_export] +macro_rules! test_spot_xml_data { + ($program: ident, $name: expr, ARG) => { + #[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) + } + }; +} + +pub fn test_corrupted_input_data(program: &str, wrapper: F) -> Result<()> +where + F: Fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>, +{ + let mut td = TestDir::new()?; + let input = mk_zeroed_md(&mut td)?; + + wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| { + let stderr = run_fail(program, args)?; + assert!(stderr.contains("bad checksum in superblock")); + Ok(()) + }) +} + +#[macro_export] +macro_rules! test_corrupted_input_data { + ($program: ident, ARG) => { + #[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) + } + }; +} + +//------------------------------------------ diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 9dd69f6..71503b2 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,15 +1,16 @@ #![allow(dead_code)] use anyhow::Result; -use duct::{cmd, Expression}; use std::fs::OpenOptions; use std::io::{Read, Write}; use std::path::PathBuf; -use std::str::from_utf8; use thinp::file_utils; use thinp::io_engine::*; pub mod cache_xml_generator; +pub mod common_args; +pub mod input_arg; +pub mod output_option; pub mod test_dir; pub mod thin_xml_generator; @@ -28,8 +29,10 @@ pub mod msg { #[cfg(feature = "rust_tests")] pub mod 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"; - pub const MISSING_OUTPUT_ARG: &str = "The following required arguments were not provided"; + pub const MISSING_INPUT_ARG: &str = + "The following required arguments were not provided\n -i "; + pub const MISSING_OUTPUT_ARG: &str = + "The following required arguments were not provided\n -o "; } //------------------------------------------ @@ -64,150 +67,64 @@ macro_rules! path_to { }; } -// FIXME: write a macro to generate these commands -// Known issue of nested macro definition: https://github.com/rust-lang/rust/issues/35853 -// RFC: https://github.com/rust-lang/rfcs/blob/master/text/3086-macro-metavar-expr.md -#[macro_export] -macro_rules! thin_check { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to!("thin_check"), args).stdout_capture().stderr_capture() - } - }; -} +//------------------------------------------ -#[macro_export] -macro_rules! thin_restore { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to!("thin_restore"), args).stdout_capture().stderr_capture() - } - }; -} +pub const CACHE_CHECK: &str = path_to!("cache_check"); +pub const CACHE_DUMP: &str = path_to!("cache_dump"); -#[macro_export] -macro_rules! thin_dump { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to!("thin_dump"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_rmap { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to_cpp!("thin_rmap"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_repair { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to_cpp!("thin_repair"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_delta { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to_cpp!("thin_delta"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_metadata_pack { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to_rust!("thin_metadata_pack"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_metadata_unpack { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to_rust!("thin_metadata_unpack"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! cache_check { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to!("cache_check"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_generate_metadata { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to_cpp!("thin_generate_metadata"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_generate_mappings { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd(path_to_cpp!("thin_generate_mappings"), args).stdout_capture().stderr_capture() - } - }; -} - -#[macro_export] -macro_rules! thin_generate_damage { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd("bin/thin_generate_damage", args).stdout_capture().stderr_capture() - } - }; -} +pub const THIN_CHECK: &str = path_to!("thin_check"); +pub const THIN_DELTA: &str = path_to_cpp!("thin_delta"); // TODO: rust version +pub const THIN_DUMP: &str = path_to!("thin_dump"); +pub const THIN_METADATA_PACK: &str = path_to_rust!("thin_metadata_pack"); // rust-only +pub const THIN_METADATA_UNPACK: &str = path_to_rust!("thin_metadata_unpack"); // rust-only +pub const THIN_REPAIR: &str = path_to_cpp!("thin_repair"); // TODO: rust version +pub const THIN_RESTORE: &str = path_to!("thin_restore"); +pub const THIN_RMAP: &str = path_to_cpp!("thin_rmap"); // TODO: rust version +pub const THIN_GENERATE_METADATA: &str = path_to_cpp!("thin_generate_metadata"); // cpp-only +pub const THIN_GENERATE_MAPPINGS: &str = path_to_cpp!("thin_generate_mappings"); // cpp-only +pub const THIN_GENERATE_DAMAGE: &str = path_to_cpp!("thin_generate_damage"); // cpp-only //------------------------------------------ -// Returns stderr, a non zero status must be returned -pub fn run_fail(command: Expression) -> Result { - let output = command.stderr_capture().unchecked().run()?; - assert!(!output.status.success()); - Ok(from_utf8(&output.stderr[0..]).unwrap().to_string()) +// Returns stdout. The command must return zero. +pub fn run_ok(program: &str, args: &[&str]) -> Result { + let command = duct::cmd(program, args).stdout_capture().stderr_capture(); + let output = command.run()?; + assert!(output.status.success()); + let stdout = std::str::from_utf8(&output.stdout[..]) + .unwrap() + .trim_end_matches(|c| c == '\n' || c == '\r') + .to_string(); + Ok(stdout) } +// Returns the entire output. The command must return zero. +pub fn run_ok_raw(program: &str, args: &[&str]) -> Result { + let command = duct::cmd(program, args).stdout_capture().stderr_capture(); + let output = command.run()?; + assert!(output.status.success()); + Ok(output) +} + +// Returns stderr, a non zero status must be returned +pub fn run_fail(program: &str, args: &[&str]) -> Result { + let command = duct::cmd(program, args).stdout_capture().stderr_capture(); + let output = command.unchecked().run()?; + assert!(!output.status.success()); + let stderr = std::str::from_utf8(&output.stderr[..]).unwrap().to_string(); + Ok(stderr) +} + +// Returns the entire output, a non zero status must be returned +pub fn run_fail_raw(program: &str, args: &[&str]) -> Result { + let command = duct::cmd(program, args).stdout_capture().stderr_capture(); + let output = command.unchecked().run()?; + assert!(!output.status.success()); + Ok(output) +} + +//------------------------------------------ + pub fn mk_valid_xml(td: &mut TestDir) -> Result { let xml = td.mk_path("meta.xml"); let mut gen = SingleThinS::new(0, 1024, 2048, 2048); @@ -223,7 +140,9 @@ pub fn mk_valid_md(td: &mut TestDir) -> Result { write_xml(&xml, &mut gen)?; let _file = file_utils::create_sized_file(&md, 4096 * 4096); - thin_restore!("-i", xml, "-o", &md).run()?; + let args = ["-i", xml.to_str().unwrap(), "-o", md.to_str().unwrap()]; + run_ok(THIN_RESTORE, &args)?; + Ok(md) } @@ -234,13 +153,6 @@ pub fn mk_zeroed_md(td: &mut TestDir) -> Result { Ok(md) } -pub fn accepts_flag(flag: &str) -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_valid_md(&mut td)?; - thin_check!(flag, &md).run()?; - Ok(()) -} - pub fn superblock_all_zeroes(path: &PathBuf) -> Result { let mut input = OpenOptions::new().read(true).write(false).open(path)?; let mut buf = vec![0; 4096]; @@ -266,50 +178,58 @@ pub fn damage_superblock(path: &PathBuf) -> Result<()> { // FIXME: replace mk_valid_md with this? pub fn prep_metadata(td: &mut TestDir) -> Result { let md = mk_zeroed_md(td)?; - thin_generate_metadata!("-o", &md, "--format", "--nr-data-blocks", "102400").run()?; + let args = [ + "-o", + md.to_str().unwrap(), + "--format", + "--nr-data-blocks", + "102400", + ]; + run_ok(THIN_GENERATE_METADATA, &args)?; // Create a 2GB device - thin_generate_metadata!("-o", &md, "--create-thin", "1").run()?; - thin_generate_mappings!( + let args = ["-o", md.to_str().unwrap(), "--create-thin", "1"]; + run_ok(THIN_GENERATE_METADATA, &args)?; + let args = [ "-o", - &md, + md.to_str().unwrap(), "--dev-id", "1", "--size", - format!("{}", 1024 * 1024 * 2), + "2097152", "--rw=randwrite", - "--seq-nr=16" - ) - .run()?; + "--seq-nr=16", + ]; + run_ok(THIN_GENERATE_MAPPINGS, &args)?; // Take a few snapshots. let mut snap_id = 2; for _i in 0..10 { // take a snapshot - thin_generate_metadata!( + let args = [ "-o", - &md, + md.to_str().unwrap(), "--create-snap", - format!("{}", snap_id), + &snap_id.to_string(), "--origin", - "1" - ) - .run()?; + "1", + ]; + run_ok(THIN_GENERATE_METADATA, &args)?; // partially overwrite the origin (64MB) - thin_generate_mappings!( + let args = [ "-o", - &md, + md.to_str().unwrap(), "--dev-id", - format!("{}", 1), + "1", "--size", - format!("{}", 1024 * 1024 * 2), + "2097152", "--io-size", - format!("{}", 64 * 1024 * 2), + "131072", "--rw=randwrite", - "--seq-nr=16" - ) - .run()?; + "--seq-nr=16", + ]; + run_ok(THIN_GENERATE_MAPPINGS, &args)?; snap_id += 1; } @@ -317,7 +237,8 @@ pub fn prep_metadata(td: &mut TestDir) -> Result { } pub fn set_needs_check(md: &PathBuf) -> Result<()> { - thin_generate_metadata!("-o", &md, "--set-needs-check").run()?; + let args = ["-o", md.to_str().unwrap(), "--set-needs-check"]; + run_ok(THIN_GENERATE_METADATA, &args)?; Ok(()) } @@ -327,21 +248,19 @@ pub fn generate_metadata_leaks( expected: u32, actual: u32, ) -> Result<()> { - let output = thin_generate_damage!( + let args = [ "-o", - &md, + md.to_str().unwrap(), "--create-metadata-leaks", "--nr-blocks", - format!("{}", nr_blocks), + &nr_blocks.to_string(), "--expected", - format!("{}", expected), + &expected.to_string(), "--actual", - format!("{}", actual) - ) - .unchecked() - .run()?; + &actual.to_string(), + ]; + run_ok(THIN_GENERATE_DAMAGE, &args)?; - assert!(output.status.success()); Ok(()) } @@ -354,7 +273,7 @@ pub fn get_needs_check(md: &PathBuf) -> Result { } pub fn md5(md: &PathBuf) -> Result { - let output = cmd!("md5sum", "-b", &md).stdout_capture().run()?; + let output = duct::cmd!("md5sum", "-b", &md).stdout_capture().run()?; let csum = std::str::from_utf8(&output.stdout[0..])?.to_string(); let csum = csum.split_ascii_whitespace().next().unwrap().to_string(); Ok(csum) @@ -371,3 +290,5 @@ where assert_eq!(csum, md5(p)?); Ok(()) } + +//------------------------------------------ diff --git a/tests/common/output_option.rs b/tests/common/output_option.rs new file mode 100644 index 0000000..3ec3a97 --- /dev/null +++ b/tests/common/output_option.rs @@ -0,0 +1,143 @@ +use crate::common::*; + +//----------------------------------------- +// test invalid arguments + +pub fn test_missing_output_option(program: &str, mk_input: F) -> Result<()> +where + F: Fn(&mut TestDir) -> Result, +{ + 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)); + Ok(()) +} + +#[macro_export] +macro_rules! test_missing_output_option { + ($program: ident, $mk_input: ident) => { + #[test] + fn missing_output_option() -> Result<()> { + test_missing_output_option($program, $mk_input) + } + }; +} + +pub fn test_output_file_not_found(program: &str, mk_input: F) -> Result<()> +where + F: Fn(&mut TestDir) -> Result, +{ + let mut td = TestDir::new()?; + let input = mk_input(&mut td)?; + let stderr = run_fail( + program, + &["-i", input.to_str().unwrap(), "-o", "no-such-file"], + )?; + assert!(stderr.contains(msg::FILE_NOT_FOUND)); + Ok(()) +} + +#[macro_export] +macro_rules! test_output_file_not_found { + ($program: ident, $mk_input: ident) => { + #[test] + fn output_file_not_found() -> Result<()> { + test_output_file_not_found($program, $mk_input) + } + }; +} + +pub fn test_output_cannot_be_a_directory(program: &str, mk_input: F) -> Result<()> +where + F: Fn(&mut TestDir) -> Result, +{ + 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)); + Ok(()) +} + +#[macro_export] +macro_rules! test_output_cannot_be_a_directory { + ($program: ident, $mk_input: ident) => { + #[test] + fn output_cannot_be_a_directory() -> Result<()> { + test_output_cannot_be_a_directory($program, $mk_input) + } + }; +} + +pub fn test_unwritable_output_file(program: &str, mk_input: F) -> Result<()> +where + F: Fn(&mut TestDir) -> Result, +{ + let mut td = TestDir::new()?; + let input = mk_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, + &[ + "-i", + input.to_str().unwrap(), + "-o", + output.to_str().unwrap(), + ], + )?; + assert!(stderr.contains("Permission denied")); + Ok(()) +} + +#[macro_export] +macro_rules! test_unwritable_output_file { + ($program: ident, $mk_input: ident) => { + #[test] + fn unwritable_output_file() -> Result<()> { + test_unwritable_output_file($program, $mk_input) + } + }; +} + +//---------------------------------------- +// test invalid content + +// currently thin/cache_restore only +pub fn test_tiny_output_file(program: &str, mk_input: F) -> Result<()> +where + F: Fn(&mut TestDir) -> Result, +{ + let mut td = TestDir::new()?; + let input = mk_input(&mut td)?; + + let output = td.mk_path("meta.bin"); + let _file = file_utils::create_sized_file(&output, 4096); + + let stderr = run_fail( + program, + &[ + "-i", + input.to_str().unwrap(), + "-o", + output.to_str().unwrap(), + ], + )?; + assert!(stderr.contains("Output file too small")); + Ok(()) +} + +#[macro_export] +macro_rules! test_tiny_output_file { + ($program: ident, $mk_input: ident) => { + #[test] + fn tiny_output_file() -> Result<()> { + test_tiny_output_file($program, $mk_input) + } + }; +} + +//----------------------------------------- diff --git a/tests/thin_check.rs b/tests/thin_check.rs index b4a5fb1..159b447 100644 --- a/tests/thin_check.rs +++ b/tests/thin_check.rs @@ -1,49 +1,50 @@ use anyhow::Result; -use thinp::file_utils; -use thinp::version::tools_version; mod common; +use common::common_args::*; +use common::input_arg::*; use common::test_dir::*; -use common::thin_xml_generator::{write_xml, FragmentedS}; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = thin_check!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +const USAGE: &str = "Usage: thin_check [options] {device|file}\n\ + Options:\n \ + {-q|--quiet}\n \ + {-h|--help}\n \ + {-V|--version}\n \ + {-m|--metadata-snap}\n \ + {--auto-repair}\n \ + {--override-mapping-root}\n \ + {--clear-needs-check-flag}\n \ + {--ignore-non-fatal-errors}\n \ + {--skip-mappings}\n \ + {--super-block-only}"; -#[test] -fn accepts_version() -> Result<()> { - let stdout = thin_check!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +//------------------------------------------ -const USAGE: &str = "Usage: thin_check [options] {device|file}\nOptions:\n {-q|--quiet}\n {-h|--help}\n {-V|--version}\n {-m|--metadata-snap}\n {--auto-repair}\n {--override-mapping-root}\n {--clear-needs-check-flag}\n {--ignore-non-fatal-errors}\n {--skip-mappings}\n {--super-block-only}"; +test_accepts_help!(THIN_CHECK, USAGE); +test_accepts_version!(THIN_CHECK); +test_rejects_bad_option!(THIN_CHECK); -#[test] -fn accepts_h() -> Result<()> { - let stdout = thin_check!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +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] -fn accepts_help() -> Result<()> { - let stdout = thin_check!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +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] -fn rejects_bad_option() -> Result<()> { - let stderr = run_fail(thin_check!("--hedgehogs-only"))?; - assert!(stderr.contains("unrecognized option \'--hedgehogs-only\'")); +//------------------------------------------ +// test exclusive flags + +fn accepts_flag(flag: &str) -> Result<()> { + let mut td = TestDir::new()?; + let md = mk_valid_md(&mut td)?; + let md_path = md.to_str().unwrap(); + run_ok(THIN_CHECK, &[flag, md_path])?; Ok(()) } @@ -67,86 +68,74 @@ fn accepts_clear_needs_check_flag() -> Result<()> { accepts_flag("--clear-needs-check-flag") } +#[test] +fn accepts_auto_repair() -> Result<()> { + accepts_flag("--auto-repair") +} + +//------------------------------------------ +// test the --quiet flag + #[test] fn accepts_quiet() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; + let md_path = md.to_str().unwrap(); - let output = thin_check!("--quiet", &md).run()?; - assert!(output.status.success()); + let output = run_ok_raw(THIN_CHECK, &["--quiet", md_path])?; assert_eq!(output.stdout.len(), 0); assert_eq!(output.stderr.len(), 0); Ok(()) } -#[test] -fn accepts_auto_repair() -> Result<()> { - accepts_flag("--auto-repair") -} +//------------------------------------------ +// test superblock-block-only #[test] fn detects_corrupt_superblock_with_superblock_only() -> Result<()> { let mut td = TestDir::new()?; let md = mk_zeroed_md(&mut td)?; - let output = thin_check!("--super-block-only", &md).unchecked().run()?; - assert!(!output.status.success()); + let md_path = md.to_str().unwrap(); + let _stderr = run_fail(THIN_CHECK, &["--super-block-only", md_path])?; Ok(()) } -#[test] -fn prints_help_message_for_tiny_metadata() -> Result<()> { - let mut td = TestDir::new()?; - let md = td.mk_path("meta.bin"); - let _file = file_utils::create_sized_file(&md, 1024); - let stderr = run_fail(thin_check!(&md))?; - assert!(stderr.contains("Metadata device/file too small. Is this binary metadata?")); - Ok(()) -} - -#[test] -fn spot_xml_data() -> Result<()> { - let mut td = TestDir::new()?; - let xml = td.mk_path("meta.xml"); - - let mut gen = FragmentedS::new(4, 10240); - write_xml(&xml, &mut gen)?; - - let stderr = run_fail(thin_check!(&xml))?; - eprintln!("{}", stderr); - assert!( - stderr.contains("This looks like XML. thin_check only checks the binary metadata format.") - ); - Ok(()) -} +//------------------------------------------ +// test info outputs #[test] fn prints_info_fields() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - let stdout = thin_check!(&md).read()?; + let md_path = md.to_str().unwrap(); + let stdout = run_ok(THIN_CHECK, &[md_path])?; assert!(stdout.contains("TRANSACTION_ID=")); assert!(stdout.contains("METADATA_FREE_BLOCKS=")); Ok(()) } +//------------------------------------------ +// test compatibility between options + #[test] fn auto_repair_incompatible_opts() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - run_fail(thin_check!("--auto-repair", "-m", &md))?; - run_fail(thin_check!( - "--auto-repair", - "--override-mapping-root", - "123", - &md - ))?; - run_fail(thin_check!("--auto-repair", "--super-block-only", &md))?; - run_fail(thin_check!("--auto-repair", "--skip-mappings", &md))?; - run_fail(thin_check!( - "--auto-repair", - "--ignore-non-fatal-errors", - &md - ))?; + let md_path = md.to_str().unwrap(); + run_fail(THIN_CHECK, &["--auto-repair", "-m", md_path])?; + run_fail( + THIN_CHECK, + &["--auto-repair", "--override-mapping-root", "123", md_path], + )?; + run_fail( + THIN_CHECK, + &["--auto-repair", "--super-block-only", md_path], + )?; + run_fail(THIN_CHECK, &["--auto-repair", "--skip-mappings", md_path])?; + run_fail( + THIN_CHECK, + &["--auto-repair", "--ignore-non-fatal-errors", md_path], + )?; Ok(()) } @@ -154,36 +143,45 @@ fn auto_repair_incompatible_opts() -> Result<()> { fn clear_needs_check_incompatible_opts() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - run_fail(thin_check!("--clear-needs-check-flag", "-m", &md))?; - run_fail(thin_check!( - "--clear-needs-check-flag", - "--override-mapping-root", - "123", - &md - ))?; - run_fail(thin_check!( - "--clear-needs-check-flag", - "--super-block-only", - &md - ))?; - run_fail(thin_check!( - "--clear-needs-check-flag", - "--ignore-non-fatal-errors", - &md - ))?; + let md_path = md.to_str().unwrap(); + run_fail(THIN_CHECK, &["--clear-needs-check-flag", "-m", md_path])?; + run_fail( + THIN_CHECK, + &[ + "--clear-needs-check-flag", + "--override-mapping-root", + "123", + md_path, + ], + )?; + run_fail( + THIN_CHECK, + &["--clear-needs-check-flag", "--super-block-only", md_path], + )?; + run_fail( + THIN_CHECK, + &[ + "--clear-needs-check-flag", + "--ignore-non-fatal-errors", + md_path, + ], + )?; Ok(()) } //------------------------------------------ +// test clear-needs-check #[test] fn clear_needs_check() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; + let md_path = md.to_str().unwrap(); + set_needs_check(&md)?; assert!(get_needs_check(&md)?); - thin_check!("--clear-needs-check-flag", &md).run()?; + run_ok(THIN_CHECK, &["--clear-needs-check-flag", md_path])?; assert!(!get_needs_check(&md)?); Ok(()) } @@ -192,9 +190,11 @@ fn clear_needs_check() -> Result<()> { fn no_clear_needs_check_if_error() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; + let md_path = md.to_str().unwrap(); + set_needs_check(&md)?; generate_metadata_leaks(&md, 1, 0, 1)?; - run_fail(thin_check!("--clear-needs-check-flag", &md))?; + run_fail(THIN_CHECK, &["--clear-needs-check-flag", md_path])?; assert!(get_needs_check(&md)?); Ok(()) } @@ -203,22 +203,31 @@ fn no_clear_needs_check_if_error() -> Result<()> { fn clear_needs_check_if_skip_mappings() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; + let md_path = md.to_str().unwrap(); + set_needs_check(&md)?; generate_metadata_leaks(&md, 1, 0, 1)?; assert!(get_needs_check(&md)?); - thin_check!("--clear-needs-check-flag", "--skip-mappings", &md).run()?; + run_ok( + THIN_CHECK, + &["--clear-needs-check-flag", "--skip-mappings", md_path], + )?; assert!(!get_needs_check(&md)?); Ok(()) } +//------------------------------------------ +// test ignore-non-fatal-errors + #[test] fn metadata_leaks_are_non_fatal() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; + let md_path = md.to_str().unwrap(); generate_metadata_leaks(&md, 1, 0, 1)?; - run_fail(thin_check!(&md))?; - thin_check!("--ignore-non-fatal-errors", &md).run()?; + run_fail(THIN_CHECK, &[md_path])?; + run_ok(THIN_CHECK, &["--ignore-non-fatal-errors", md_path])?; Ok(()) } @@ -226,28 +235,34 @@ fn metadata_leaks_are_non_fatal() -> Result<()> { fn fatal_errors_cant_be_ignored() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; + let md_path = md.to_str().unwrap(); + generate_metadata_leaks(&md, 1, 1, 0)?; ensure_untouched(&md, || { - run_fail(thin_check!("--ignore-non-fatal-errors", &md))?; + run_fail(THIN_CHECK, &["--ignore-non-fatal-errors", md_path])?; Ok(()) }) } +//------------------------------------------ +// test auto-repair + #[test] fn auto_repair() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; + let md_path = md.to_str().unwrap(); // auto-repair should have no effect on good metadata. ensure_untouched(&md, || { - thin_check!("--auto-repair", &md).run()?; + run_ok(THIN_CHECK, &["--auto-repair", md_path])?; Ok(()) })?; generate_metadata_leaks(&md, 16, 0, 1)?; - run_fail(thin_check!(&md))?; - thin_check!("--auto-repair", &md).run()?; - thin_check!(&md).run()?; + run_fail(THIN_CHECK, &[md_path])?; + run_ok(THIN_CHECK, &["--auto-repair", md_path])?; + run_ok(THIN_CHECK, &[md_path])?; Ok(()) } @@ -255,10 +270,11 @@ fn auto_repair() -> Result<()> { fn auto_repair_has_limits() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; - generate_metadata_leaks(&md, 16, 1, 0)?; + let md_path = md.to_str().unwrap(); + generate_metadata_leaks(&md, 16, 1, 0)?; ensure_untouched(&md, || { - run_fail(thin_check!("--auto-repair", &md))?; + run_fail(THIN_CHECK, &["--auto-repair", md_path])?; Ok(()) })?; Ok(()) @@ -268,8 +284,10 @@ fn auto_repair_has_limits() -> Result<()> { fn auto_repair_clears_needs_check() -> Result<()> { let mut td = TestDir::new()?; let md = prep_metadata(&mut td)?; + let md_path = md.to_str().unwrap(); + set_needs_check(&md)?; - thin_check!("--auto-repair", &md).run()?; + run_ok(THIN_CHECK, &["--auto-repair", md_path])?; assert!(!get_needs_check(&md)?); Ok(()) } diff --git a/tests/thin_delta.rs b/tests/thin_delta.rs index 2d570de..cdd203d 100644 --- a/tests/thin_delta.rs +++ b/tests/thin_delta.rs @@ -1,54 +1,35 @@ use anyhow::Result; -use thinp::version::tools_version; mod common; + +use common::common_args::*; use common::test_dir::*; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = thin_delta!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +const USAGE: &str = "Usage: thin_delta [options] \n\ + Options:\n \ + {--thin1, --snap1, --root1}\n \ + {--thin2, --snap2, --root2}\n \ + {-m, --metadata-snap} [block#]\n \ + {--verbose}\n \ + {-h|--help}\n \ + {-V|--version}"; -#[test] -fn accepts_version() -> Result<()> { - let stdout = thin_delta!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +//------------------------------------------ -const USAGE: &str = "Usage: thin_delta [options] \nOptions:\n {--thin1, --snap1, --root1}\n {--thin2, --snap2, --root2}\n {-m, --metadata-snap} [block#]\n {--verbose}\n {-h|--help}\n {-V|--version}"; +test_accepts_help!(THIN_DELTA, USAGE); +test_accepts_version!(THIN_DELTA); +test_rejects_bad_option!(THIN_DELTA); -#[test] -fn accepts_h() -> Result<()> { - let stdout = thin_delta!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} - -#[test] -fn accepts_help() -> Result<()> { - let stdout = thin_delta!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} - -#[test] -fn rejects_bad_option() -> Result<()> { - let stderr = run_fail(thin_delta!("--hedgehogs-only"))?; - assert!(stderr.contains("unrecognized option \'--hedgehogs-only\'")); - Ok(()) -} +//------------------------------------------ #[test] fn snap1_unspecified() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - let stderr = run_fail(thin_delta!("--snap2", "45", &md))?; + let stderr = run_fail(THIN_DELTA, &["--snap2", "45", md.to_str().unwrap()])?; assert!(stderr.contains("--snap1 or --root1 not specified")); Ok(()) } @@ -57,15 +38,17 @@ fn snap1_unspecified() -> Result<()> { fn snap2_unspecified() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - let stderr = run_fail(thin_delta!("--snap1", "45", &md))?; + let stderr = run_fail(THIN_DELTA, &["--snap1", "45", md.to_str().unwrap()])?; assert!(stderr.contains("--snap2 or --root2 not specified")); Ok(()) } #[test] fn dev_unspecified() -> Result<()> { - let stderr = run_fail(thin_delta!("--snap1", "45", "--snap2", "46"))?; + let stderr = run_fail(THIN_DELTA, &["--snap1", "45", "--snap2", "46"])?; // TODO: replace with msg::MISSING_INPUT_ARG once the rust version is ready assert!(stderr.contains("No input file provided")); Ok(()) } + +//------------------------------------------ diff --git a/tests/thin_dump.rs b/tests/thin_dump.rs index 90b4298..1cb6819 100644 --- a/tests/thin_dump.rs +++ b/tests/thin_dump.rs @@ -2,29 +2,48 @@ use anyhow::Result; use std::fs::OpenOptions; use std::io::Write; use std::str::from_utf8; -use thinp::file_utils; mod common; + +use common::common_args::*; +use common::input_arg::*; use common::test_dir::*; use common::*; //------------------------------------------ -#[test] -fn small_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = td.mk_path("meta.bin"); - file_utils::create_sized_file(&md, 512)?; - let _stderr = run_fail(thin_dump!(&md))?; - Ok(()) -} +const USAGE: &str = "Usage: thin_dump [options] {device|file}\n\ + Options:\n \ + {-h|--help}\n \ + {-f|--format} {xml|human_readable|custom}\n \ + {-r|--repair}\n \ + {-m|--metadata-snap} [block#]\n \ + {-o }\n \ + {--dev-id} \n \ + {--skip-mappings}\n \ + {-V|--version}"; + +//------------------------------------------ + +test_accepts_help!(THIN_DUMP, USAGE); +test_accepts_version!(THIN_DUMP); +test_rejects_bad_option!(THIN_DUMP); + +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 dump & restore cycle #[test] fn dump_restore_cycle() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - let output = thin_dump!(&md).run()?; + let md_path = md.to_str().unwrap(); + let output = run_ok_raw(THIN_DUMP, &[md_path])?; let xml = td.mk_path("meta.xml"); let mut file = OpenOptions::new() @@ -36,29 +55,40 @@ fn dump_restore_cycle() -> Result<()> { drop(file); let md2 = mk_zeroed_md(&mut td)?; - thin_restore!("-i", &xml, "-o", &md2).run()?; + let md2_path = md2.to_str().unwrap(); + let xml_path = xml.to_str().unwrap(); + run_ok(THIN_RESTORE, &["-i", xml_path, "-o", md2_path])?; - let output2 = thin_dump!(&md2).run()?; + let output2 = run_ok_raw(THIN_DUMP, &[md2_path])?; assert_eq!(output.stdout, output2.stdout); Ok(()) } +//------------------------------------------ +// test no stderr with a normal dump + #[test] fn no_stderr() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - let output = thin_dump!(&md).run()?; + let md_path = md.to_str().unwrap(); + let output = run_ok_raw(THIN_DUMP, &[md_path])?; assert_eq!(output.stderr.len(), 0); Ok(()) } +//------------------------------------------ +// test superblock overriding & repair +// TODO: share with thin_repair + fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - let output = thin_dump!(&md, flag, value).run()?; + let md_path = md.to_str().unwrap(); + let output = run_ok_raw(THIN_DUMP, &[md_path, flag, value])?; assert_eq!(output.stderr.len(), 0); assert!(from_utf8(&output.stdout[0..])?.contains(pattern)); @@ -80,44 +110,56 @@ fn override_nr_data_blocks() -> Result<()> { override_something("--nr-data-blocks", "234500", "nr_data_blocks=\"234500\"") } +// FIXME: duplicate with superblock_succeeds in thin_repair.rs #[test] fn repair_superblock() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - let before = thin_dump!( - "--transaction-id=5", - "--data-block-size=128", - "--nr-data-blocks=4096000", - &md - ) - .run()?; + let before = run_ok_raw( + THIN_DUMP, + &[ + "--transaction-id=5", + "--data-block-size=128", + "--nr-data-blocks=4096000", + md.to_str().unwrap(), + ], + )?; damage_superblock(&md)?; - let after = thin_dump!( - "--repair", - "--transaction-id=5", - "--data-block-size=128", - "--nr-data-blocks=4096000", - &md - ) - .run()?; + let after = run_ok_raw( + THIN_DUMP, + &[ + "--repair", + "--transaction-id=5", + "--data-block-size=128", + "--nr-data-blocks=4096000", + md.to_str().unwrap(), + ], + )?; assert_eq!(after.stderr.len(), 0); assert_eq!(before.stdout, after.stdout); Ok(()) } +//------------------------------------------ +// test compatibility between options +// TODO: share with thin_repair + #[test] fn missing_transaction_id() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; damage_superblock(&md)?; - let stderr = run_fail(thin_dump!( - "--repair", - "--data-block-size=128", - "--nr-data-blocks=4096000", - &md - ))?; + let stderr = run_fail( + THIN_DUMP, + &[ + "--repair", + "--data-block-size=128", + "--nr-data-blocks=4096000", + md.to_str().unwrap(), + ], + )?; assert!(stderr.contains("transaction id")); Ok(()) } @@ -127,12 +169,15 @@ fn missing_data_block_size() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; damage_superblock(&md)?; - let stderr = run_fail(thin_dump!( - "--repair", - "--transaction-id=5", - "--nr-data-blocks=4096000", - &md - ))?; + let stderr = run_fail( + THIN_DUMP, + &[ + "--repair", + "--transaction-id=5", + "--nr-data-blocks=4096000", + md.to_str().unwrap(), + ], + )?; assert!(stderr.contains("data block size")); Ok(()) } @@ -142,12 +187,17 @@ fn missing_nr_data_blocks() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; damage_superblock(&md)?; - let stderr = run_fail(thin_dump!( - "--repair", - "--transaction-id=5", - "--data-block-size=128", - &md - ))?; + let stderr = run_fail( + THIN_DUMP, + &[ + "--repair", + "--transaction-id=5", + "--data-block-size=128", + md.to_str().unwrap(), + ], + )?; assert!(stderr.contains("nr data blocks")); Ok(()) } + +//------------------------------------------ diff --git a/tests/thin_metadata_pack.rs b/tests/thin_metadata_pack.rs index 84e3187..92917df 100644 --- a/tests/thin_metadata_pack.rs +++ b/tests/thin_metadata_pack.rs @@ -1,77 +1,39 @@ use anyhow::Result; -use thinp::version::tools_version; mod common; -use common::test_dir::*; + +use common::common_args::*; +use common::input_arg::*; +use common::output_option::*; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = thin_metadata_pack!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} - -#[test] -fn accepts_version() -> Result<()> { - let stdout = thin_metadata_pack!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} - -const USAGE: &str = "thin_metadata_pack 0.9.0\nProduces a compressed file of thin metadata. Only packs metadata blocks that are actually used.\n\nUSAGE:\n thin_metadata_pack -i -o \n\nFLAGS:\n -h, --help Prints help information\n -V, --version Prints version information\n\nOPTIONS:\n -i Specify thinp metadata binary device/file\n -o Specify packed output file"; - -#[test] -fn accepts_h() -> Result<()> { - let stdout = thin_metadata_pack!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} - -#[test] -fn accepts_help() -> Result<()> { - let stdout = thin_metadata_pack!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} - -#[test] -fn rejects_bad_option() -> Result<()> { - let stderr = run_fail(thin_metadata_pack!("--hedgehogs-only"))?; - assert!(stderr.contains("Found argument \'--hedgehogs-only\'")); - Ok(()) -} - -#[test] -fn missing_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_metadata_pack!("-o", &md))?; - assert!( - stderr.contains("error: The following required arguments were not provided:\n -i ") - ); - Ok(()) -} - -#[test] -fn no_such_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_metadata_pack!("-i", "no-such-file", "-o", &md))?; - assert!(stderr.contains("Couldn't find input file")); - Ok(()) -} - -#[test] -fn missing_output_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_metadata_pack!("-i", &md))?; - assert!(stderr - .contains("error: The following required arguments were not provided:\n -o ")); - Ok(()) -} +const USAGE: &str = concat!( + "thin_metadata_pack ", + include_str!("../VERSION"), + "Produces a compressed file of thin metadata. Only packs metadata blocks that are actually used.\n\ + \n\ + USAGE:\n \ + thin_metadata_pack -i -o \n\ + \n\ + FLAGS:\n \ + -h, --help Prints help information\n \ + -V, --version Prints version information\n\ + \n\ + OPTIONS:\n \ + -i Specify thinp metadata binary device/file\n \ + -o Specify packed output file" +); + +//------------------------------------------ + +test_accepts_help!(THIN_METADATA_PACK, USAGE); +test_accepts_version!(THIN_METADATA_PACK); +test_rejects_bad_option!(THIN_METADATA_PACK); + +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); //------------------------------------------ diff --git a/tests/thin_metadata_unpack.rs b/tests/thin_metadata_unpack.rs index 7a5d5ee..c2fc068 100644 --- a/tests/thin_metadata_unpack.rs +++ b/tests/thin_metadata_unpack.rs @@ -1,98 +1,64 @@ use anyhow::Result; -use thinp::version::tools_version; mod common; + +use common::common_args::*; +use common::input_arg::*; +use common::output_option::*; use common::test_dir::*; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = thin_metadata_unpack!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +const USAGE: &str = concat!( + "thin_metadata_unpack ", + include_str!("../VERSION"), + "Unpack a compressed file of thin metadata.\n\ + \n\ + USAGE:\n \ + thin_metadata_unpack -i -o \n\ + \n\ + FLAGS:\n \ + -h, --help Prints help information\n \ + -V, --version Prints version information\n\ + \n\ + OPTIONS:\n \ + -i Specify thinp metadata binary device/file\n \ + -o Specify packed output file" +); -#[test] -fn accepts_version() -> Result<()> { - let stdout = thin_metadata_unpack!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +//------------------------------------------ -const USAGE: &str = "thin_metadata_unpack 0.9.0\nUnpack a compressed file of thin metadata.\n\nUSAGE:\n thin_metadata_unpack -i -o \n\nFLAGS:\n -h, --help Prints help information\n -V, --version Prints version information\n\nOPTIONS:\n -i Specify thinp metadata binary device/file\n -o Specify packed output file"; +test_accepts_help!(THIN_METADATA_UNPACK, USAGE); +test_accepts_version!(THIN_METADATA_UNPACK); +test_rejects_bad_option!(THIN_METADATA_UNPACK); -#[test] -fn accepts_h() -> Result<()> { - let stdout = thin_metadata_unpack!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +test_missing_input_option!(THIN_METADATA_PACK); +test_input_file_not_found!(THIN_METADATA_UNPACK, OPTION); +test_corrupted_input_data!(THIN_METADATA_UNPACK, OPTION); -#[test] -fn accepts_help() -> Result<()> { - let stdout = thin_metadata_unpack!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +test_missing_output_option!(THIN_METADATA_UNPACK, mk_valid_md); -#[test] -fn rejects_bad_option() -> Result<()> { - let stderr = run_fail(thin_metadata_unpack!("--hedgehogs-only"))?; - assert!(stderr.contains("Found argument \'--hedgehogs-only\'")); - Ok(()) -} +//------------------------------------------ -#[test] -fn missing_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_metadata_unpack!("-o", &md))?; - assert!( - stderr.contains("error: The following required arguments were not provided:\n -i ") - ); - Ok(()) -} - -#[test] -fn no_such_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_metadata_unpack!("-i", "no-such-file", "-o", &md))?; - assert!(stderr.contains("Couldn't find input file")); - Ok(()) -} - -#[test] -fn missing_output_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_metadata_unpack!("-i", &md))?; - assert!(stderr - .contains("error: The following required arguments were not provided:\n -o ")); - Ok(()) -} - -#[test] -fn garbage_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_metadata_unpack!("-i", &md, "-o", "junk"))?; - assert!(stderr.contains("Not a pack file.")); - Ok(()) -} +// TODO: share with thin_restore/cache_restore/era_restore #[test] fn end_to_end() -> Result<()> { let mut td = TestDir::new()?; let md_in = mk_valid_md(&mut td)?; let md_out = mk_zeroed_md(&mut td)?; - thin_metadata_pack!("-i", &md_in, "-o", "meta.pack").run()?; - thin_metadata_unpack!("-i", "meta.pack", "-o", &md_out).run()?; + run_ok( + THIN_METADATA_PACK, + &["-i", md_in.to_str().unwrap(), "-o", "meta.pack"], + )?; + run_ok( + THIN_METADATA_UNPACK, + &["-i", "meta.pack", "-o", md_out.to_str().unwrap()], + )?; - let dump1 = thin_dump!(&md_in).read()?; - let dump2 = thin_dump!(&md_out).read()?; + let dump1 = run_ok(THIN_DUMP, &[md_in.to_str().unwrap()])?; + let dump2 = run_ok(THIN_DUMP, &[md_out.to_str().unwrap()])?; assert_eq!(dump1, dump2); Ok(()) } diff --git a/tests/thin_repair.rs b/tests/thin_repair.rs index 3dcc506..1884e6f 100644 --- a/tests/thin_repair.rs +++ b/tests/thin_repair.rs @@ -1,91 +1,67 @@ use anyhow::Result; -use std::str::from_utf8; -use thinp::version::tools_version; mod common; + +use common::common_args::*; +use common::input_arg::*; +use common::output_option::*; use common::test_dir::*; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = thin_repair!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +const USAGE: &str = "Usage: thin_repair [options] {device|file}\n\ + Options:\n \ + {-h|--help}\n \ + {-i|--input} \n \ + {-o|--output} \n \ + {--transaction-id} \n \ + {--data-block-size} \n \ + {--nr-data-blocks} \n \ + {-V|--version}"; -#[test] -fn accepts_version() -> Result<()> { - let stdout = thin_repair!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +//----------------------------------------- -const USAGE: &str = "Usage: thin_repair [options] {device|file}\nOptions:\n {-h|--help}\n {-i|--input} \n {-o|--output} \n {--transaction-id} \n {--data-block-size} \n {--nr-data-blocks} \n {-V|--version}"; +test_accepts_help!(THIN_REPAIR, USAGE); +test_accepts_version!(THIN_REPAIR); +test_rejects_bad_option!(THIN_REPAIR); -#[test] -fn accepts_h() -> Result<()> { - let stdout = thin_repair!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +test_input_file_not_found!(THIN_REPAIR, OPTION); +test_corrupted_input_data!(THIN_REPAIR, OPTION); -#[test] -fn accepts_help() -> Result<()> { - let stdout = thin_repair!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +test_missing_output_option!(THIN_REPAIR, mk_valid_md); + +//----------------------------------------- +// test output to a small file + +// TODO: share with thin_restore #[test] fn dont_repair_xml() -> Result<()> { let mut td = TestDir::new()?; let md = mk_zeroed_md(&mut td)?; let xml = mk_valid_xml(&mut td)?; - run_fail(thin_repair!("-i", &xml, "-o", &md))?; + let xml_path = xml.to_str().unwrap(); + let md_path = md.to_str().unwrap(); + run_fail(THIN_REPAIR, &["-i", xml_path, "-o", md_path])?; Ok(()) } -#[test] -fn input_file_not_found() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_repair!("-i", "no-such-file", "-o", &md))?; - assert!(superblock_all_zeroes(&md)?); - // TODO: replace with msg::FILE_NOT_FOUND once the rust version is ready - assert!(stderr.contains("No such file or directory")); - Ok(()) -} +//----------------------------------------- -#[test] -fn garbage_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let md2 = mk_zeroed_md(&mut td)?; - run_fail(thin_repair!("-i", &md, "-o", &md2))?; - assert!(superblock_all_zeroes(&md2)?); - Ok(()) -} - -#[test] -fn missing_output_arg() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_valid_md(&mut td)?; - let stderr = run_fail(thin_repair!("-i", &md))?; - // TODO: replace with msg::MISSING_OUTPUT_ARG once the rust version is ready - assert!(stderr.contains("No output file provided.")); - Ok(()) -} +// TODO: share with thin_dump fn override_thing(flag: &str, val: &str, pattern: &str) -> Result<()> { let mut td = TestDir::new()?; let md1 = mk_valid_md(&mut td)?; let md2 = mk_zeroed_md(&mut td)?; - let output = thin_repair!(flag, val, "-i", &md1, "-o", &md2).run()?; + let md1_path = md1.to_str().unwrap(); + let md2_path = md2.to_str().unwrap(); + let output = run_ok_raw(THIN_REPAIR, &[flag, val, "-i", md1_path, "-o", md2_path])?; assert_eq!(output.stderr.len(), 0); - let output = thin_dump!(&md2).run()?; - assert!(from_utf8(&output.stdout[0..])?.contains(pattern)); + let md2_path = md2.to_str().unwrap(); + let output = run_ok(THIN_DUMP, &[md2_path])?; + assert!(output.contains(pattern)); Ok(()) } @@ -104,42 +80,63 @@ fn override_nr_data_blocks() -> Result<()> { override_thing("--nr-data-blocks", "234500", "nr_data_blocks=\"234500\"") } +// FIXME: that's repair_superblock in thin_dump.rs #[test] fn superblock_succeeds() -> Result<()> { let mut td = TestDir::new()?; let md1 = mk_valid_md(&mut td)?; - let original = thin_dump!( - "--transaction-id=5", - "--data-block-size=128", - "--nr-data-blocks=4096000", - &md1 - ) - .run()?; + let md1_path = md1.to_str().unwrap(); + let original = run_ok_raw( + THIN_DUMP, + &[ + "--transaction-id=5", + "--data-block-size=128", + "--nr-data-blocks=4096000", + md1_path, + ], + )?; assert_eq!(original.stderr.len(), 0); damage_superblock(&md1)?; let md2 = mk_zeroed_md(&mut td)?; - thin_repair!( - "--transaction-id=5", - "--data-block-size=128", - "--nr-data-blocks=4096000", - "-i", - &md1, - "-o", - &md2 - ) - .run()?; - let repaired = thin_dump!(&md2).run()?; + let md2_path = md2.to_str().unwrap(); + run_ok( + THIN_REPAIR, + &[ + "--transaction-id=5", + "--data-block-size=128", + "--nr-data-blocks=4096000", + "-i", + md1_path, + "-o", + md2_path, + ], + )?; + let repaired = run_ok_raw(THIN_DUMP, &[md2_path])?; assert_eq!(repaired.stderr.len(), 0); assert_eq!(original.stdout, repaired.stdout); Ok(()) } +//----------------------------------------- + +// TODO: share with thin_dump + fn missing_thing(flag1: &str, flag2: &str, pattern: &str) -> Result<()> { let mut td = TestDir::new()?; let md1 = mk_valid_md(&mut td)?; damage_superblock(&md1)?; let md2 = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_repair!(flag1, flag2, "-i", &md1, "-o", &md2))?; + let stderr = run_fail( + THIN_REPAIR, + &[ + flag1, + flag2, + "-i", + md1.to_str().unwrap(), + "-o", + md2.to_str().unwrap(), + ], + )?; assert!(stderr.contains(pattern)); Ok(()) } @@ -170,3 +167,5 @@ fn missing_nr_data_blocks() -> Result<()> { "nr data blocks", ) } + +//----------------------------------------- diff --git a/tests/thin_restore.rs b/tests/thin_restore.rs index 0fc23bf..79336e4 100644 --- a/tests/thin_restore.rs +++ b/tests/thin_restore.rs @@ -1,101 +1,51 @@ use anyhow::Result; -use std::str::from_utf8; -use thinp::file_utils; -use thinp::version::tools_version; mod common; + +use common::common_args::*; +use common::input_arg::*; +use common::output_option::*; use common::test_dir::*; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = thin_restore!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +const USAGE: &str = "Usage: thin_restore [options]\n\ + Options:\n \ + {-h|--help}\n \ + {-i|--input} \n \ + {-o|--output} \n \ + {--transaction-id} \n \ + {--data-block-size} \n \ + {--nr-data-blocks} \n \ + {-q|--quiet}\n \ + {-V|--version}"; -#[test] -fn accepts_version() -> Result<()> { - let stdout = thin_restore!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +//------------------------------------------ -const USAGE: &str = "Usage: thin_restore [options]\nOptions:\n {-h|--help}\n {-i|--input} \n {-o|--output} \n {--transaction-id} \n {--data-block-size} \n {--nr-data-blocks} \n {-q|--quiet}\n {-V|--version}"; +test_accepts_help!(THIN_RESTORE, USAGE); +test_accepts_version!(THIN_RESTORE); -#[test] -fn accepts_h() -> Result<()> { - let stdout = thin_restore!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +test_missing_input_option!(THIN_RESTORE); +test_input_file_not_found!(THIN_RESTORE, OPTION); +test_corrupted_input_data!(THIN_RESTORE, OPTION); -#[test] -fn accepts_help() -> Result<()> { - let stdout = thin_restore!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} +test_missing_output_option!(THIN_RESTORE, mk_valid_xml); +test_tiny_output_file!(THIN_RESTORE, mk_valid_xml); -#[test] -fn missing_input_arg() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_restore!("-o", &md))?; - assert!(stderr.contains(msg::MISSING_INPUT_ARG)); - Ok(()) -} +//----------------------------------------- -#[test] -fn input_file_not_found() -> Result<()> { - let mut td = TestDir::new()?; - let md = mk_zeroed_md(&mut td)?; - let stderr = run_fail(thin_restore!("-i", "no-such-file", "-o", &md))?; - assert!(superblock_all_zeroes(&md)?); - assert!(stderr.contains(msg::FILE_NOT_FOUND)); - Ok(()) -} - -#[test] -fn garbage_input_file() -> Result<()> { - let mut td = TestDir::new()?; - let xml = mk_zeroed_md(&mut td)?; - let md = mk_zeroed_md(&mut td)?; - let _stderr = run_fail(thin_restore!("-i", &xml, "-o", &md))?; - assert!(superblock_all_zeroes(&md)?); - Ok(()) -} - -#[test] -fn missing_output_arg() -> Result<()> { - let mut td = TestDir::new()?; - let xml = mk_valid_xml(&mut td)?; - let stderr = run_fail(thin_restore!("-i", &xml))?; - assert!(stderr.contains(msg::MISSING_OUTPUT_ARG)); - Ok(()) -} - -#[test] -fn tiny_output_file() -> Result<()> { - let mut td = TestDir::new()?; - let xml = mk_valid_xml(&mut td)?; - let md = td.mk_path("meta.bin"); - let _file = file_utils::create_sized_file(&md, 4096); - let stderr = run_fail(thin_restore!("-i", &xml, "-o", &md))?; - assert!(stderr.contains("Output file too small")); - Ok(()) -} +// TODO: share with cache_restore, era_restore fn quiet_flag(flag: &str) -> Result<()> { let mut td = TestDir::new()?; let xml = mk_valid_xml(&mut td)?; let md = mk_zeroed_md(&mut td)?; - let output = thin_restore!("-i", &xml, "-o", &md, flag).run()?; + let xml_path = xml.to_str().unwrap(); + let md_path = md.to_str().unwrap(); + let output = run_ok_raw(THIN_RESTORE, &["-i", xml_path, "-o", md_path, flag])?; - assert!(output.status.success()); assert_eq!(output.stdout.len(), 0); assert_eq!(output.stderr.len(), 0); Ok(()) @@ -111,15 +61,21 @@ fn accepts_quiet() -> Result<()> { quiet_flag("--quiet") } +//----------------------------------------- + +// TODO: share with thin_dump + fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> { let mut td = TestDir::new()?; let xml = mk_valid_xml(&mut td)?; let md = mk_zeroed_md(&mut td)?; - thin_restore!("-i", &xml, "-o", &md, flag, value).run()?; + let xml_path = xml.to_str().unwrap(); + let md_path = md.to_str().unwrap(); + run_ok(THIN_RESTORE, &["-i", xml_path, "-o", md_path, flag, value])?; - let output = thin_dump!(&md).run()?; - assert!(from_utf8(&output.stdout)?.contains(pattern)); + let output = run_ok(THIN_DUMP, &[md_path])?; + assert!(output.contains(pattern)); Ok(()) } @@ -137,3 +93,5 @@ fn override_data_block_size() -> Result<()> { fn override_nr_data_blocks() -> Result<()> { override_something("--nr-data-blocks", "234500", "nr_data_blocks=\"234500\"") } + +//----------------------------------------- diff --git a/tests/thin_rmap.rs b/tests/thin_rmap.rs index 26bb571..eab535d 100644 --- a/tests/thin_rmap.rs +++ b/tests/thin_rmap.rs @@ -1,54 +1,36 @@ use anyhow::Result; -use thinp::version::tools_version; mod common; + +use common::common_args::*; use common::test_dir::*; use common::*; //------------------------------------------ -#[test] -fn accepts_v() -> Result<()> { - let stdout = thin_rmap!("-V").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +const USAGE: &str = "Usage: thin_rmap [options] {device|file}\n\ + Options:\n \ + {-h|--help}\n \ + {-V|--version}\n \ + {--region }*\n\ + Where:\n \ + is of the form ..\n \ + for example 5..45 denotes blocks 5 to 44 inclusive, but not block 45"; -#[test] -fn accepts_version() -> Result<()> { - let stdout = thin_rmap!("--version").read()?; - assert!(stdout.contains(tools_version())); - Ok(()) -} +//------------------------------------------ -const USAGE: &str = "Usage: thin_rmap [options] {device|file}\nOptions:\n {-h|--help}\n {-V|--version}\n {--region }*\nWhere:\n is of the form ..\n for example 5..45 denotes blocks 5 to 44 inclusive, but not block 45"; +test_accepts_help!(THIN_RMAP, USAGE); +test_accepts_version!(THIN_RMAP); +test_rejects_bad_option!(THIN_RMAP); -#[test] -fn accepts_h() -> Result<()> { - let stdout = thin_rmap!("-h").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} - -#[test] -fn accepts_help() -> Result<()> { - let stdout = thin_rmap!("--help").read()?; - assert_eq!(stdout, USAGE); - Ok(()) -} - -#[test] -fn rejects_bad_option() -> Result<()> { - let stderr = run_fail(thin_rmap!("--hedgehogs-only"))?; - assert!(stderr.contains("unrecognized option \'--hedgehogs-only\'")); - Ok(()) -} +//------------------------------------------ #[test] fn valid_region_format_should_pass() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - thin_rmap!("--region", "23..7890", &md).run()?; + let md_path = md.to_str().unwrap(); + run_ok(THIN_RMAP, &["--region", "23..7890", md_path])?; Ok(()) } @@ -67,7 +49,7 @@ fn invalid_regions_should_fail() -> Result<()> { for r in &invalid_regions { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - run_fail(thin_rmap!(r, &md))?; + run_fail(THIN_RMAP, &[&r.to_string(), md.to_str().unwrap()])?; } Ok(()) } @@ -76,7 +58,16 @@ fn invalid_regions_should_fail() -> Result<()> { fn multiple_regions_should_pass() -> Result<()> { let mut td = TestDir::new()?; let md = mk_valid_md(&mut td)?; - thin_rmap!("--region", "1..23", "--region", "45..78", &md).run()?; + run_ok( + THIN_RMAP, + &[ + "--region", + "1..23", + "--region", + "45..78", + md.to_str().unwrap(), + ], + )?; Ok(()) } @@ -84,7 +75,7 @@ fn multiple_regions_should_pass() -> Result<()> { fn junk_input() -> Result<()> { let mut td = TestDir::new()?; let xml = mk_valid_xml(&mut td)?; - run_fail(thin_rmap!("--region", "0..-1", &xml))?; + run_fail(THIN_RMAP, &["--region", "0..-1", xml.to_str().unwrap()])?; Ok(()) }