[tests] Pull out common tests on i/o options into reusable modules
- Introduce modules for testing input/output options - Provide macros for generating test cases - Hide details of subprocess execution
This commit is contained in:
82
tests/common/common_args.rs
Normal file
82
tests/common/common_args.rs
Normal file
@@ -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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
263
tests/common/input_arg.rs
Normal file
263
tests/common/input_arg.rs
Normal file
@@ -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<F>(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<F>(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<F>(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<F>(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<F>(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<F>(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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
@@ -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 <FILE>";
|
||||
pub const MISSING_OUTPUT_ARG: &str =
|
||||
"The following required arguments were not provided\n -o <FILE>";
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
@@ -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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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::<OsString>::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<String> {
|
||||
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<String> {
|
||||
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<std::process::Output> {
|
||||
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<String> {
|
||||
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<std::process::Output> {
|
||||
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<PathBuf> {
|
||||
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<PathBuf> {
|
||||
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<PathBuf> {
|
||||
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<bool> {
|
||||
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<PathBuf> {
|
||||
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<PathBuf> {
|
||||
}
|
||||
|
||||
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<bool> {
|
||||
}
|
||||
|
||||
pub fn md5(md: &PathBuf) -> Result<String> {
|
||||
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(())
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
143
tests/common/output_option.rs
Normal file
143
tests/common/output_option.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use crate::common::*;
|
||||
|
||||
//-----------------------------------------
|
||||
// test invalid arguments
|
||||
|
||||
pub fn test_missing_output_option<F>(program: &str, mk_input: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&mut TestDir) -> Result<PathBuf>,
|
||||
{
|
||||
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<F>(program: &str, mk_input: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&mut TestDir) -> Result<PathBuf>,
|
||||
{
|
||||
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<F>(program: &str, mk_input: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&mut TestDir) -> Result<PathBuf>,
|
||||
{
|
||||
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<F>(program: &str, mk_input: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&mut TestDir) -> Result<PathBuf>,
|
||||
{
|
||||
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<F>(program: &str, mk_input: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&mut TestDir) -> Result<PathBuf>,
|
||||
{
|
||||
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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
Reference in New Issue
Block a user