Merge pull request #182 from mingnus/2021-06-23-functional-tests
Clean up function test commons
This commit is contained in:
commit
6d31d4def2
@ -3,9 +3,12 @@ use anyhow::Result;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::fixture::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -29,8 +32,8 @@ impl<'a> Program<'a> for CacheCheck {
|
|||||||
"cache_check"
|
"cache_check"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
CACHE_CHECK
|
CACHE_CHECK.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -48,7 +51,7 @@ impl<'a> Program<'a> for CacheCheck {
|
|||||||
|
|
||||||
impl<'a> InputProgram<'a> for CacheCheck {
|
impl<'a> InputProgram<'a> for CacheCheck {
|
||||||
fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
|
fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
|
||||||
mk_valid_md(td)
|
common::thin::mk_valid_md(td) // FIXME: create cache metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_not_found() -> &'a str {
|
fn file_not_found() -> &'a str {
|
||||||
@ -87,7 +90,7 @@ test_corrupted_input_data!(CacheCheck);
|
|||||||
fn failing_q() -> Result<()> {
|
fn failing_q() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_zeroed_md(&mut td)?;
|
let md = mk_zeroed_md(&mut td)?;
|
||||||
let output = run_fail_raw(CACHE_CHECK, &["-q", md.to_str().unwrap()])?;
|
let output = run_fail_raw(CACHE_CHECK, args!["-q", &md])?;
|
||||||
assert_eq!(output.stdout.len(), 0);
|
assert_eq!(output.stdout.len(), 0);
|
||||||
assert_eq!(output.stderr.len(), 0);
|
assert_eq!(output.stderr.len(), 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -97,7 +100,7 @@ fn failing_q() -> Result<()> {
|
|||||||
fn failing_quiet() -> Result<()> {
|
fn failing_quiet() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_zeroed_md(&mut td)?;
|
let md = mk_zeroed_md(&mut td)?;
|
||||||
let output = run_fail_raw(CACHE_CHECK, &["--quiet", md.to_str().unwrap()])?;
|
let output = run_fail_raw(CACHE_CHECK, args!["--quiet", &md])?;
|
||||||
assert_eq!(output.stdout.len(), 0);
|
assert_eq!(output.stdout.len(), 0);
|
||||||
assert_eq!(output.stderr.len(), 0);
|
assert_eq!(output.stderr.len(), 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -4,8 +4,10 @@ mod common;
|
|||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
|
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -25,8 +27,8 @@ impl<'a> Program<'a> for CacheDump {
|
|||||||
"cache_dump"
|
"cache_dump"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
CACHE_DUMP
|
CACHE_DUMP.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -44,7 +46,7 @@ impl<'a> Program<'a> for CacheDump {
|
|||||||
|
|
||||||
impl<'a> InputProgram<'a> for CacheDump {
|
impl<'a> InputProgram<'a> for CacheDump {
|
||||||
fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
|
fn mk_valid_input(td: &mut TestDir) -> Result<std::path::PathBuf> {
|
||||||
mk_valid_md(td)
|
common::thin::mk_valid_md(td) // FIXME: generate cache metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_not_found() -> &'a str {
|
fn file_not_found() -> &'a str {
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
use crate::common::*;
|
use anyhow::Result;
|
||||||
|
|
||||||
use thinp::version::tools_version;
|
use thinp::version::tools_version;
|
||||||
|
|
||||||
|
use crate::args;
|
||||||
|
use crate::common::process::*;
|
||||||
|
use crate::common::program::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
// help
|
// help
|
||||||
|
|
||||||
@ -8,7 +13,7 @@ pub fn test_help_short<'a, P>() -> Result<()>
|
|||||||
where
|
where
|
||||||
P: Program<'a>,
|
P: Program<'a>,
|
||||||
{
|
{
|
||||||
let stdout = run_ok(P::path(), &["-h"])?;
|
let stdout = run_ok(P::path(), args!["-h"])?;
|
||||||
assert_eq!(stdout, P::usage());
|
assert_eq!(stdout, P::usage());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -17,7 +22,7 @@ pub fn test_help_long<'a, P>() -> Result<()>
|
|||||||
where
|
where
|
||||||
P: Program<'a>,
|
P: Program<'a>,
|
||||||
{
|
{
|
||||||
let stdout = run_ok(P::path(), &["--help"])?;
|
let stdout = run_ok(P::path(), args!["--help"])?;
|
||||||
assert_eq!(stdout, P::usage());
|
assert_eq!(stdout, P::usage());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -44,7 +49,7 @@ pub fn test_version_short<'a, P>() -> Result<()>
|
|||||||
where
|
where
|
||||||
P: Program<'a>,
|
P: Program<'a>,
|
||||||
{
|
{
|
||||||
let stdout = run_ok(P::path(), &["-V"])?;
|
let stdout = run_ok(P::path(), args!["-V"])?;
|
||||||
assert!(stdout.contains(tools_version()));
|
assert!(stdout.contains(tools_version()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -53,7 +58,7 @@ pub fn test_version_long<'a, P>() -> Result<()>
|
|||||||
where
|
where
|
||||||
P: Program<'a>,
|
P: Program<'a>,
|
||||||
{
|
{
|
||||||
let stdout = run_ok(P::path(), &["--version"])?;
|
let stdout = run_ok(P::path(), args!["--version"])?;
|
||||||
assert!(stdout.contains(tools_version()));
|
assert!(stdout.contains(tools_version()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -80,7 +85,7 @@ where
|
|||||||
P: Program<'a>,
|
P: Program<'a>,
|
||||||
{
|
{
|
||||||
let option = "--hedgehogs-only";
|
let option = "--hedgehogs-only";
|
||||||
let stderr = run_fail(P::path(), &[option])?;
|
let stderr = run_fail(P::path(), args![option])?;
|
||||||
assert!(stderr.contains(&P::bad_option_hint(option)));
|
assert!(stderr.contains(&P::bad_option_hint(option)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
69
tests/common/fixture.rs
Normal file
69
tests/common/fixture.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use thinp::file_utils;
|
||||||
|
|
||||||
|
use crate::common::test_dir::TestDir;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub fn mk_zeroed_md(td: &mut TestDir) -> Result<PathBuf> {
|
||||||
|
let md = td.mk_path("meta.bin");
|
||||||
|
eprintln!("path = {:?}", md);
|
||||||
|
let _file = file_utils::create_sized_file(&md, 1024 * 1024 * 16);
|
||||||
|
Ok(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn damage_superblock(path: &PathBuf) -> Result<()> {
|
||||||
|
let mut output = OpenOptions::new().read(false).write(true).open(path)?;
|
||||||
|
let buf = [0u8; 512];
|
||||||
|
output.write_all(&buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub fn md5(md: &PathBuf) -> Result<String> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This checksums the file before and after the thunk is run to
|
||||||
|
// ensure it is unchanged.
|
||||||
|
pub fn ensure_untouched<F>(p: &PathBuf, thunk: F) -> Result<()>
|
||||||
|
where
|
||||||
|
F: Fn() -> Result<()>,
|
||||||
|
{
|
||||||
|
let csum = md5(p)?;
|
||||||
|
thunk()?;
|
||||||
|
assert_eq!(csum, md5(p)?);
|
||||||
|
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];
|
||||||
|
input.read_exact(&mut buf[0..])?;
|
||||||
|
for b in buf {
|
||||||
|
if b != 0 {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_superblock_zeroed<F>(p: &PathBuf, thunk: F) -> Result<()>
|
||||||
|
where
|
||||||
|
F: Fn() -> Result<()>,
|
||||||
|
{
|
||||||
|
thunk()?;
|
||||||
|
assert!(superblock_all_zeroes(p)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
@ -1,41 +1,50 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
use thinp::file_utils;
|
||||||
|
|
||||||
|
use crate::args;
|
||||||
|
use crate::common::fixture::*;
|
||||||
|
use crate::common::process::*;
|
||||||
|
use crate::common::program::*;
|
||||||
|
use crate::common::test_dir::*;
|
||||||
use crate::common::thin_xml_generator::{write_xml, FragmentedS};
|
use crate::common::thin_xml_generator::{write_xml, FragmentedS};
|
||||||
use crate::common::*;
|
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
// wrappers
|
// wrappers
|
||||||
|
|
||||||
type ArgsBuilder = fn(&mut TestDir, &str, &dyn Fn(&[&str]) -> Result<()>) -> Result<()>;
|
type ArgsBuilder = fn(&mut TestDir, &OsStr, &dyn Fn(&[&OsStr]) -> Result<()>) -> Result<()>;
|
||||||
|
|
||||||
fn with_output_md_untouched(
|
fn with_output_md_untouched(
|
||||||
td: &mut TestDir,
|
td: &mut TestDir,
|
||||||
input: &str,
|
input: &OsStr,
|
||||||
thunk: &dyn Fn(&[&str]) -> Result<()>,
|
thunk: &dyn Fn(&[&OsStr]) -> Result<()>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let output = mk_zeroed_md(td)?;
|
let output = mk_zeroed_md(td)?;
|
||||||
ensure_untouched(&output, || {
|
ensure_untouched(&output, || {
|
||||||
let args = ["-i", input, "-o", output.to_str().unwrap()];
|
let args = args!["-i", input, "-o", &output];
|
||||||
thunk(&args)
|
thunk(&args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_output_superblock_zeroed(
|
fn with_output_superblock_zeroed(
|
||||||
td: &mut TestDir,
|
td: &mut TestDir,
|
||||||
input: &str,
|
input: &OsStr,
|
||||||
thunk: &dyn Fn(&[&str]) -> Result<()>,
|
thunk: &dyn Fn(&[&OsStr]) -> Result<()>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let output = mk_zeroed_md(td)?;
|
let output = mk_zeroed_md(td)?;
|
||||||
ensure_superblock_zeroed(&output, || {
|
ensure_superblock_zeroed(&output, || {
|
||||||
let args = ["-i", input, "-o", output.to_str().unwrap()];
|
let args = args!["-i", input, "-o", &output];
|
||||||
thunk(&args)
|
thunk(&args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_arg_only(
|
fn input_arg_only(
|
||||||
_td: &mut TestDir,
|
_td: &mut TestDir,
|
||||||
input: &str,
|
input: &OsStr,
|
||||||
thunk: &dyn Fn(&[&str]) -> Result<()>,
|
thunk: &dyn Fn(&[&OsStr]) -> Result<()>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let args = [input];
|
let args = args![input];
|
||||||
thunk(&args)
|
thunk(&args)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +62,8 @@ pub fn test_missing_input_arg<'a, P>() -> Result<()>
|
|||||||
where
|
where
|
||||||
P: InputProgram<'a>,
|
P: InputProgram<'a>,
|
||||||
{
|
{
|
||||||
let stderr = run_fail(P::path(), &[])?;
|
let args: [&str; 0] = [];
|
||||||
|
let stderr = run_fail(P::path(), &args)?;
|
||||||
assert!(stderr.contains(P::missing_input_arg()));
|
assert!(stderr.contains(P::missing_input_arg()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -75,7 +85,7 @@ where
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let output = mk_zeroed_md(&mut td)?;
|
let output = mk_zeroed_md(&mut td)?;
|
||||||
ensure_untouched(&output, || {
|
ensure_untouched(&output, || {
|
||||||
let args = ["-o", output.to_str().unwrap()];
|
let args = args!["-o", &output];
|
||||||
let stderr = run_fail(P::path(), &args)?;
|
let stderr = run_fail(P::path(), &args)?;
|
||||||
assert!(stderr.contains(P::missing_input_arg()));
|
assert!(stderr.contains(P::missing_input_arg()));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -99,7 +109,7 @@ where
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
|
|
||||||
let wrapper = build_args_fn(P::arg_type())?;
|
let wrapper = build_args_fn(P::arg_type())?;
|
||||||
wrapper(&mut td, "no-such-file", &|args: &[&str]| {
|
wrapper(&mut td, "no-such-file".as_ref(), &|args: &[&OsStr]| {
|
||||||
let stderr = run_fail(P::path(), args)?;
|
let stderr = run_fail(P::path(), args)?;
|
||||||
assert!(stderr.contains(P::file_not_found()));
|
assert!(stderr.contains(P::file_not_found()));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -123,7 +133,7 @@ where
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
|
|
||||||
let wrapper = build_args_fn(P::arg_type())?;
|
let wrapper = build_args_fn(P::arg_type())?;
|
||||||
wrapper(&mut td, "/tmp", &|args: &[&str]| {
|
wrapper(&mut td, "/tmp".as_ref(), &|args: &[&OsStr]| {
|
||||||
let stderr = run_fail(P::path(), args)?;
|
let stderr = run_fail(P::path(), args)?;
|
||||||
assert!(stderr.contains("Not a block device or regular file"));
|
assert!(stderr.contains("Not a block device or regular file"));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -147,11 +157,11 @@ where
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
|
|
||||||
// input an unreadable file
|
// input an unreadable file
|
||||||
let input = mk_valid_md(&mut td)?;
|
let input = P::mk_valid_input(&mut td)?;
|
||||||
duct::cmd!("chmod", "-r", &input).run()?;
|
duct::cmd!("chmod", "-r", &input).run()?;
|
||||||
|
|
||||||
let wrapper = build_args_fn(P::arg_type())?;
|
let wrapper = build_args_fn(P::arg_type())?;
|
||||||
wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
|
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
|
||||||
let stderr = run_fail(P::path(), args)?;
|
let stderr = run_fail(P::path(), args)?;
|
||||||
assert!(stderr.contains("Permission denied"));
|
assert!(stderr.contains("Permission denied"));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -181,7 +191,7 @@ where
|
|||||||
file_utils::create_sized_file(&input, 1024)?;
|
file_utils::create_sized_file(&input, 1024)?;
|
||||||
|
|
||||||
let wrapper = build_args_fn(P::arg_type())?;
|
let wrapper = build_args_fn(P::arg_type())?;
|
||||||
wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
|
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
|
||||||
let stderr = run_fail(P::path(), args)?;
|
let stderr = run_fail(P::path(), args)?;
|
||||||
assert!(stderr.contains("Metadata device/file too small. Is this binary metadata?"));
|
assert!(stderr.contains("Metadata device/file too small. Is this binary metadata?"));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -210,7 +220,7 @@ where
|
|||||||
write_xml(&input, &mut gen)?;
|
write_xml(&input, &mut gen)?;
|
||||||
|
|
||||||
let wrapper = build_args_fn(P::arg_type())?;
|
let wrapper = build_args_fn(P::arg_type())?;
|
||||||
wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
|
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
|
||||||
let stderr = run_fail(P::path(), args)?;
|
let stderr = run_fail(P::path(), args)?;
|
||||||
eprintln!("{}", stderr);
|
eprintln!("{}", stderr);
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
@ -243,7 +253,7 @@ where
|
|||||||
ArgType::InputArg => input_arg_only,
|
ArgType::InputArg => input_arg_only,
|
||||||
ArgType::IoOptions => with_output_superblock_zeroed,
|
ArgType::IoOptions => with_output_superblock_zeroed,
|
||||||
};
|
};
|
||||||
wrapper(&mut td, input.to_str().unwrap(), &|args: &[&str]| {
|
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
|
||||||
let stderr = run_fail(P::path(), args)?;
|
let stderr = run_fail(P::path(), args)?;
|
||||||
assert!(stderr.contains(P::corrupted_input()));
|
assert!(stderr.contains(P::corrupted_input()));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,350 +1,15 @@
|
|||||||
|
// suppress all the false alarms by cargo test
|
||||||
|
// https://github.com/rust-lang/rust/issues/46379
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use std::fs::OpenOptions;
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use thinp::file_utils;
|
|
||||||
use thinp::io_engine::*;
|
|
||||||
|
|
||||||
pub mod cache_xml_generator;
|
pub mod cache_xml_generator;
|
||||||
pub mod common_args;
|
pub mod common_args;
|
||||||
|
pub mod fixture;
|
||||||
pub mod input_arg;
|
pub mod input_arg;
|
||||||
pub mod output_option;
|
pub mod output_option;
|
||||||
|
pub mod process;
|
||||||
|
pub mod program;
|
||||||
|
pub mod target;
|
||||||
pub mod test_dir;
|
pub mod test_dir;
|
||||||
|
pub mod thin;
|
||||||
pub mod thin_xml_generator;
|
pub mod thin_xml_generator;
|
||||||
|
|
||||||
use crate::common::thin_xml_generator::{write_xml, SingleThinS};
|
|
||||||
use test_dir::TestDir;
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
|
|
||||||
pub mod cpp_msg {
|
|
||||||
pub const FILE_NOT_FOUND: &str = "No such file or directory";
|
|
||||||
pub const MISSING_INPUT_ARG: &str = "No input file provided";
|
|
||||||
pub const MISSING_OUTPUT_ARG: &str = "No output file provided";
|
|
||||||
pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
|
|
||||||
|
|
||||||
pub fn bad_option_hint(option: &str) -> String {
|
|
||||||
format!("unrecognized option '{}'", option)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod rust_msg {
|
|
||||||
pub const FILE_NOT_FOUND: &str = "Couldn't find input file";
|
|
||||||
pub const MISSING_INPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
|
|
||||||
pub const MISSING_OUTPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
|
|
||||||
pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
|
|
||||||
|
|
||||||
pub fn bad_option_hint(option: &str) -> String {
|
|
||||||
format!("Found argument '{}' which wasn't expected", option)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "rust_tests"))]
|
|
||||||
pub use cpp_msg as msg;
|
|
||||||
#[cfg(feature = "rust_tests")]
|
|
||||||
pub use rust_msg as msg;
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! path_to_cpp {
|
|
||||||
($name: literal) => {
|
|
||||||
concat!("bin/", $name)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! path_to_rust {
|
|
||||||
($name: literal) => {
|
|
||||||
env!(concat!("CARGO_BIN_EXE_", $name))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "rust_tests"))]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! path_to {
|
|
||||||
($name: literal) => {
|
|
||||||
path_to_cpp!($name)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rust_tests")]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! path_to {
|
|
||||||
($name: literal) => {
|
|
||||||
path_to_rust!($name)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
|
|
||||||
pub const CACHE_CHECK: &str = path_to!("cache_check");
|
|
||||||
pub const CACHE_DUMP: &str = path_to!("cache_dump");
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
|
|
||||||
pub enum ArgType {
|
|
||||||
InputArg,
|
|
||||||
IoOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Program<'a> {
|
|
||||||
fn name() -> &'a str;
|
|
||||||
fn path() -> &'a str;
|
|
||||||
fn usage() -> &'a str;
|
|
||||||
fn arg_type() -> ArgType;
|
|
||||||
|
|
||||||
// error messages
|
|
||||||
fn bad_option_hint(option: &str) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait InputProgram<'a>: Program<'a> {
|
|
||||||
fn mk_valid_input(td: &mut TestDir) -> Result<PathBuf>;
|
|
||||||
|
|
||||||
// error messages
|
|
||||||
fn missing_input_arg() -> &'a str;
|
|
||||||
fn file_not_found() -> &'a str;
|
|
||||||
fn corrupted_input() -> &'a str;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait BinaryInputProgram<'a>: InputProgram<'a> {}
|
|
||||||
|
|
||||||
pub trait OutputProgram<'a>: InputProgram<'a> {
|
|
||||||
// error messages
|
|
||||||
fn missing_output_arg() -> &'a str;
|
|
||||||
fn file_not_found() -> &'a str;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait BinaryOutputProgram<'a>: OutputProgram<'a> {}
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
|
|
||||||
// Returns stdout. The command must return zero.
|
|
||||||
pub fn run_ok(program: &str, args: &[&str]) -> Result<String> {
|
|
||||||
let command = duct::cmd(program, args).stdout_capture().stderr_capture();
|
|
||||||
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);
|
|
||||||
write_xml(&xml, &mut gen)?;
|
|
||||||
Ok(xml)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mk_valid_md(td: &mut TestDir) -> Result<PathBuf> {
|
|
||||||
let xml = td.mk_path("meta.xml");
|
|
||||||
let md = td.mk_path("meta.bin");
|
|
||||||
|
|
||||||
let mut gen = SingleThinS::new(0, 1024, 20480, 20480);
|
|
||||||
write_xml(&xml, &mut gen)?;
|
|
||||||
|
|
||||||
let _file = file_utils::create_sized_file(&md, 4096 * 4096);
|
|
||||||
let args = ["-i", xml.to_str().unwrap(), "-o", md.to_str().unwrap()];
|
|
||||||
run_ok(THIN_RESTORE, &args)?;
|
|
||||||
|
|
||||||
Ok(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mk_zeroed_md(td: &mut TestDir) -> Result<PathBuf> {
|
|
||||||
let md = td.mk_path("meta.bin");
|
|
||||||
eprintln!("path = {:?}", md);
|
|
||||||
let _file = file_utils::create_sized_file(&md, 1024 * 1024 * 16);
|
|
||||||
Ok(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
input.read_exact(&mut buf[0..])?;
|
|
||||||
for b in buf {
|
|
||||||
if b != 0 {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn damage_superblock(path: &PathBuf) -> Result<()> {
|
|
||||||
let mut output = OpenOptions::new().read(false).write(true).open(path)?;
|
|
||||||
let buf = [0u8; 512];
|
|
||||||
output.write_all(&buf)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------
|
|
||||||
|
|
||||||
// FIXME: replace mk_valid_md with this?
|
|
||||||
pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> {
|
|
||||||
let md = mk_zeroed_md(td)?;
|
|
||||||
let args = [
|
|
||||||
"-o",
|
|
||||||
md.to_str().unwrap(),
|
|
||||||
"--format",
|
|
||||||
"--nr-data-blocks",
|
|
||||||
"102400",
|
|
||||||
];
|
|
||||||
run_ok(THIN_GENERATE_METADATA, &args)?;
|
|
||||||
|
|
||||||
// Create a 2GB device
|
|
||||||
let args = ["-o", md.to_str().unwrap(), "--create-thin", "1"];
|
|
||||||
run_ok(THIN_GENERATE_METADATA, &args)?;
|
|
||||||
let args = [
|
|
||||||
"-o",
|
|
||||||
md.to_str().unwrap(),
|
|
||||||
"--dev-id",
|
|
||||||
"1",
|
|
||||||
"--size",
|
|
||||||
"2097152",
|
|
||||||
"--rw=randwrite",
|
|
||||||
"--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
|
|
||||||
let args = [
|
|
||||||
"-o",
|
|
||||||
md.to_str().unwrap(),
|
|
||||||
"--create-snap",
|
|
||||||
&snap_id.to_string(),
|
|
||||||
"--origin",
|
|
||||||
"1",
|
|
||||||
];
|
|
||||||
run_ok(THIN_GENERATE_METADATA, &args)?;
|
|
||||||
|
|
||||||
// partially overwrite the origin (64MB)
|
|
||||||
let args = [
|
|
||||||
"-o",
|
|
||||||
md.to_str().unwrap(),
|
|
||||||
"--dev-id",
|
|
||||||
"1",
|
|
||||||
"--size",
|
|
||||||
"2097152",
|
|
||||||
"--io-size",
|
|
||||||
"131072",
|
|
||||||
"--rw=randwrite",
|
|
||||||
"--seq-nr=16",
|
|
||||||
];
|
|
||||||
run_ok(THIN_GENERATE_MAPPINGS, &args)?;
|
|
||||||
snap_id += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_needs_check(md: &PathBuf) -> Result<()> {
|
|
||||||
let args = ["-o", md.to_str().unwrap(), "--set-needs-check"];
|
|
||||||
run_ok(THIN_GENERATE_METADATA, &args)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_metadata_leaks(
|
|
||||||
md: &PathBuf,
|
|
||||||
nr_blocks: u64,
|
|
||||||
expected: u32,
|
|
||||||
actual: u32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let args = [
|
|
||||||
"-o",
|
|
||||||
md.to_str().unwrap(),
|
|
||||||
"--create-metadata-leaks",
|
|
||||||
"--nr-blocks",
|
|
||||||
&nr_blocks.to_string(),
|
|
||||||
"--expected",
|
|
||||||
&expected.to_string(),
|
|
||||||
"--actual",
|
|
||||||
&actual.to_string(),
|
|
||||||
];
|
|
||||||
run_ok(THIN_GENERATE_DAMAGE, &args)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_needs_check(md: &PathBuf) -> Result<bool> {
|
|
||||||
use thinp::thin::superblock::*;
|
|
||||||
|
|
||||||
let engine = SyncIoEngine::new(&md, 1, false)?;
|
|
||||||
let sb = read_superblock(&engine, SUPERBLOCK_LOCATION)?;
|
|
||||||
Ok(sb.flags.needs_check)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn md5(md: &PathBuf) -> Result<String> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This checksums the file before and after the thunk is run to
|
|
||||||
// ensure it is unchanged.
|
|
||||||
pub fn ensure_untouched<F>(p: &PathBuf, thunk: F) -> Result<()>
|
|
||||||
where
|
|
||||||
F: Fn() -> Result<()>,
|
|
||||||
{
|
|
||||||
let csum = md5(p)?;
|
|
||||||
thunk()?;
|
|
||||||
assert_eq!(csum, md5(p)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ensure_superblock_zeroed<F>(p: &PathBuf, thunk: F) -> Result<()>
|
|
||||||
where
|
|
||||||
F: Fn() -> Result<()>,
|
|
||||||
{
|
|
||||||
thunk()?;
|
|
||||||
assert!(superblock_all_zeroes(p)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
use crate::common::*;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use thinp::file_utils;
|
||||||
|
|
||||||
|
use crate::args;
|
||||||
|
use crate::common::process::*;
|
||||||
|
use crate::common::program::*;
|
||||||
|
use crate::common::test_dir::*;
|
||||||
|
|
||||||
//-----------------------------------------
|
//-----------------------------------------
|
||||||
// test invalid arguments
|
// test invalid arguments
|
||||||
@ -9,7 +16,7 @@ where
|
|||||||
{
|
{
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let input = P::mk_valid_input(&mut td)?;
|
let input = P::mk_valid_input(&mut td)?;
|
||||||
let stderr = run_fail(P::path(), &["-i", input.to_str().unwrap()])?;
|
let stderr = run_fail(P::path(), args!["-i", &input])?;
|
||||||
assert!(stderr.contains(P::missing_output_arg()));
|
assert!(stderr.contains(P::missing_output_arg()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -30,10 +37,7 @@ where
|
|||||||
{
|
{
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let input = P::mk_valid_input(&mut td)?;
|
let input = P::mk_valid_input(&mut td)?;
|
||||||
let stderr = run_fail(
|
let stderr = run_fail(P::path(), args!["-i", &input, "-o", "no-such-file"])?;
|
||||||
P::path(),
|
|
||||||
&["-i", input.to_str().unwrap(), "-o", "no-such-file"],
|
|
||||||
)?;
|
|
||||||
assert!(stderr.contains(<P as OutputProgram>::file_not_found()));
|
assert!(stderr.contains(<P as OutputProgram>::file_not_found()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -54,7 +58,7 @@ where
|
|||||||
{
|
{
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let input = P::mk_valid_input(&mut td)?;
|
let input = P::mk_valid_input(&mut td)?;
|
||||||
let stderr = run_fail(P::path(), &["-i", input.to_str().unwrap(), "-o", "/tmp"])?;
|
let stderr = run_fail(P::path(), args!["-i", &input, "-o", "/tmp"])?;
|
||||||
assert!(stderr.contains("Not a block device or regular file"));
|
assert!(stderr.contains("Not a block device or regular file"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -80,15 +84,7 @@ where
|
|||||||
let _file = file_utils::create_sized_file(&output, 4096);
|
let _file = file_utils::create_sized_file(&output, 4096);
|
||||||
duct::cmd!("chmod", "-w", &output).run()?;
|
duct::cmd!("chmod", "-w", &output).run()?;
|
||||||
|
|
||||||
let stderr = run_fail(
|
let stderr = run_fail(P::path(), args!["-i", &input, "-o", &output])?;
|
||||||
P::path(),
|
|
||||||
&[
|
|
||||||
"-i",
|
|
||||||
input.to_str().unwrap(),
|
|
||||||
"-o",
|
|
||||||
output.to_str().unwrap(),
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
assert!(stderr.contains("Permission denied"));
|
assert!(stderr.contains("Permission denied"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -117,15 +113,7 @@ where
|
|||||||
let output = td.mk_path("meta.bin");
|
let output = td.mk_path("meta.bin");
|
||||||
let _file = file_utils::create_sized_file(&output, 4096);
|
let _file = file_utils::create_sized_file(&output, 4096);
|
||||||
|
|
||||||
let stderr = run_fail(
|
let stderr = run_fail(P::path(), args!["-i", &input, "-o", &output])?;
|
||||||
P::path(),
|
|
||||||
&[
|
|
||||||
"-i",
|
|
||||||
input.to_str().unwrap(),
|
|
||||||
"-o",
|
|
||||||
output.to_str().unwrap(),
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
assert!(stderr.contains("Output file too small"));
|
assert!(stderr.contains("Output file too small"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
82
tests/common/process.rs
Normal file
82
tests/common/process.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! args {
|
||||||
|
( $( $arg: expr ),* ) => {
|
||||||
|
{
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
let args = [$( OsStr::new($arg) ),*];
|
||||||
|
args
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns stdout. The command must return zero.
|
||||||
|
pub fn run_ok<S, I>(program: S, args: I) -> Result<String>
|
||||||
|
where
|
||||||
|
S: AsRef<OsStr>,
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Into<OsString>,
|
||||||
|
{
|
||||||
|
let command = duct::cmd(program.as_ref(), 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<S, I>(program: S, args: I) -> Result<std::process::Output>
|
||||||
|
where
|
||||||
|
S: AsRef<OsStr>,
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Into<OsString>,
|
||||||
|
{
|
||||||
|
let command = duct::cmd(program.as_ref(), 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<S, I>(program: S, args: I) -> Result<String>
|
||||||
|
where
|
||||||
|
S: AsRef<OsStr>,
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Into<OsString>,
|
||||||
|
{
|
||||||
|
let command = duct::cmd(program.as_ref(), 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<S, I>(program: S, args: I) -> Result<std::process::Output>
|
||||||
|
where
|
||||||
|
S: AsRef<OsStr>,
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Into<OsString>,
|
||||||
|
{
|
||||||
|
let command = duct::cmd(program.as_ref(), args)
|
||||||
|
.stdout_capture()
|
||||||
|
.stderr_capture();
|
||||||
|
let output = command.unchecked().run()?;
|
||||||
|
assert!(!output.status.success());
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
43
tests/common/program.rs
Normal file
43
tests/common/program.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::common::test_dir::TestDir;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub enum ArgType {
|
||||||
|
InputArg,
|
||||||
|
IoOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Program<'a> {
|
||||||
|
fn name() -> &'a str;
|
||||||
|
fn path() -> &'a OsStr;
|
||||||
|
fn usage() -> &'a str;
|
||||||
|
fn arg_type() -> ArgType;
|
||||||
|
|
||||||
|
// error messages
|
||||||
|
fn bad_option_hint(option: &str) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputProgram<'a>: Program<'a> {
|
||||||
|
fn mk_valid_input(td: &mut TestDir) -> Result<PathBuf>;
|
||||||
|
|
||||||
|
// error messages
|
||||||
|
fn missing_input_arg() -> &'a str;
|
||||||
|
fn file_not_found() -> &'a str;
|
||||||
|
fn corrupted_input() -> &'a str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BinaryInputProgram<'a>: InputProgram<'a> {}
|
||||||
|
|
||||||
|
pub trait OutputProgram<'a>: InputProgram<'a> {
|
||||||
|
// error messages
|
||||||
|
fn missing_output_arg() -> &'a str;
|
||||||
|
fn file_not_found() -> &'a str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BinaryOutputProgram<'a>: OutputProgram<'a> {}
|
||||||
|
|
||||||
|
//------------------------------------------
|
79
tests/common/target.rs
Normal file
79
tests/common/target.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! path_to_cpp {
|
||||||
|
($name: literal) => {
|
||||||
|
concat!("bin/", $name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! path_to_rust {
|
||||||
|
($name: literal) => {
|
||||||
|
env!(concat!("CARGO_BIN_EXE_", $name))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rust_tests"))]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! path_to {
|
||||||
|
($name: literal) => {
|
||||||
|
path_to_cpp!($name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rust_tests")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! path_to {
|
||||||
|
($name: literal) => {
|
||||||
|
path_to_rust!($name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub const CACHE_CHECK: &str = path_to!("cache_check");
|
||||||
|
pub const CACHE_DUMP: &str = path_to!("cache_dump");
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub mod cpp_msg {
|
||||||
|
pub const FILE_NOT_FOUND: &str = "No such file or directory";
|
||||||
|
pub const MISSING_INPUT_ARG: &str = "No input file provided";
|
||||||
|
pub const MISSING_OUTPUT_ARG: &str = "No output file provided";
|
||||||
|
pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
|
||||||
|
|
||||||
|
pub fn bad_option_hint(option: &str) -> String {
|
||||||
|
format!("unrecognized option '{}'", option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rust_msg {
|
||||||
|
pub const FILE_NOT_FOUND: &str = "Couldn't find input file";
|
||||||
|
pub const MISSING_INPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
|
||||||
|
pub const MISSING_OUTPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
|
||||||
|
pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
|
||||||
|
|
||||||
|
pub fn bad_option_hint(option: &str) -> String {
|
||||||
|
format!("Found argument '{}' which wasn't expected", option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rust_tests"))]
|
||||||
|
pub use cpp_msg as msg;
|
||||||
|
#[cfg(feature = "rust_tests")]
|
||||||
|
pub use rust_msg as msg;
|
||||||
|
|
||||||
|
//------------------------------------------
|
127
tests/common/thin.rs
Normal file
127
tests/common/thin.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use thinp::file_utils;
|
||||||
|
use thinp::io_engine::*;
|
||||||
|
|
||||||
|
use crate::args;
|
||||||
|
use crate::common::fixture::*;
|
||||||
|
use crate::common::process::*;
|
||||||
|
use crate::common::target::*;
|
||||||
|
use crate::common::test_dir::TestDir;
|
||||||
|
use crate::common::thin_xml_generator::{write_xml, SingleThinS};
|
||||||
|
|
||||||
|
//-----------------------------------------------
|
||||||
|
|
||||||
|
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);
|
||||||
|
write_xml(&xml, &mut gen)?;
|
||||||
|
Ok(xml)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_valid_md(td: &mut TestDir) -> Result<PathBuf> {
|
||||||
|
let xml = td.mk_path("meta.xml");
|
||||||
|
let md = td.mk_path("meta.bin");
|
||||||
|
|
||||||
|
let mut gen = SingleThinS::new(0, 1024, 20480, 20480);
|
||||||
|
write_xml(&xml, &mut gen)?;
|
||||||
|
|
||||||
|
let _file = file_utils::create_sized_file(&md, 4096 * 4096);
|
||||||
|
let args = args!["-i", &xml, "-o", &md];
|
||||||
|
run_ok(THIN_RESTORE, &args)?;
|
||||||
|
|
||||||
|
Ok(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------
|
||||||
|
|
||||||
|
// FIXME: replace mk_valid_md with this?
|
||||||
|
pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> {
|
||||||
|
let md = mk_zeroed_md(td)?;
|
||||||
|
let args = args!["-o", &md, "--format", "--nr-data-blocks", "102400"];
|
||||||
|
run_ok(THIN_GENERATE_METADATA, &args)?;
|
||||||
|
|
||||||
|
// Create a 2GB device
|
||||||
|
let args = args!["-o", &md, "--create-thin", "1"];
|
||||||
|
run_ok(THIN_GENERATE_METADATA, &args)?;
|
||||||
|
let args = args![
|
||||||
|
"-o",
|
||||||
|
&md,
|
||||||
|
"--dev-id",
|
||||||
|
"1",
|
||||||
|
"--size",
|
||||||
|
"2097152",
|
||||||
|
"--rw=randwrite",
|
||||||
|
"--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
|
||||||
|
let snap_id_str = snap_id.to_string();
|
||||||
|
let args = args!["-o", &md, "--create-snap", &snap_id_str, "--origin", "1"];
|
||||||
|
run_ok(THIN_GENERATE_METADATA, &args)?;
|
||||||
|
|
||||||
|
// partially overwrite the origin (64MB)
|
||||||
|
let args = args![
|
||||||
|
"-o",
|
||||||
|
&md,
|
||||||
|
"--dev-id",
|
||||||
|
"1",
|
||||||
|
"--size",
|
||||||
|
"2097152",
|
||||||
|
"--io-size",
|
||||||
|
"131072",
|
||||||
|
"--rw=randwrite",
|
||||||
|
"--seq-nr=16"
|
||||||
|
];
|
||||||
|
run_ok(THIN_GENERATE_MAPPINGS, &args)?;
|
||||||
|
snap_id += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_needs_check(md: &PathBuf) -> Result<()> {
|
||||||
|
let args = args!["-o", &md, "--set-needs-check"];
|
||||||
|
run_ok(THIN_GENERATE_METADATA, &args)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_metadata_leaks(
|
||||||
|
md: &PathBuf,
|
||||||
|
nr_blocks: u64,
|
||||||
|
expected: u32,
|
||||||
|
actual: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let nr_blocks_str = nr_blocks.to_string();
|
||||||
|
let expected_str = expected.to_string();
|
||||||
|
let actual_str = actual.to_string();
|
||||||
|
let args = args![
|
||||||
|
"-o",
|
||||||
|
&md,
|
||||||
|
"--create-metadata-leaks",
|
||||||
|
"--nr-blocks",
|
||||||
|
&nr_blocks_str,
|
||||||
|
"--expected",
|
||||||
|
&expected_str,
|
||||||
|
"--actual",
|
||||||
|
&actual_str
|
||||||
|
];
|
||||||
|
run_ok(THIN_GENERATE_DAMAGE, &args)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_needs_check(md: &PathBuf) -> Result<bool> {
|
||||||
|
use thinp::thin::superblock::*;
|
||||||
|
|
||||||
|
let engine = SyncIoEngine::new(&md, 1, false)?;
|
||||||
|
let sb = read_superblock(&engine, SUPERBLOCK_LOCATION)?;
|
||||||
|
Ok(sb.flags.needs_check)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------
|
@ -3,9 +3,13 @@ use anyhow::Result;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::fixture::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -31,8 +35,8 @@ impl<'a> Program<'a> for ThinCheck {
|
|||||||
"thin_check"
|
"thin_check"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_CHECK
|
THIN_CHECK.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -89,8 +93,7 @@ test_corrupted_input_data!(ThinCheck);
|
|||||||
fn accepts_flag(flag: &str) -> Result<()> {
|
fn accepts_flag(flag: &str) -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
run_ok(THIN_CHECK, args![flag, &md])?;
|
||||||
run_ok(THIN_CHECK, &[flag, md_path])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,9 +129,8 @@ fn accepts_auto_repair() -> Result<()> {
|
|||||||
fn accepts_quiet() -> Result<()> {
|
fn accepts_quiet() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
let output = run_ok_raw(THIN_CHECK, &["--quiet", md_path])?;
|
let output = run_ok_raw(THIN_CHECK, args!["--quiet", &md])?;
|
||||||
assert_eq!(output.stdout.len(), 0);
|
assert_eq!(output.stdout.len(), 0);
|
||||||
assert_eq!(output.stderr.len(), 0);
|
assert_eq!(output.stderr.len(), 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -141,8 +143,7 @@ fn accepts_quiet() -> Result<()> {
|
|||||||
fn detects_corrupt_superblock_with_superblock_only() -> Result<()> {
|
fn detects_corrupt_superblock_with_superblock_only() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_zeroed_md(&mut td)?;
|
let md = mk_zeroed_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
let _stderr = run_fail(THIN_CHECK, args!["--super-block-only", &md])?;
|
||||||
let _stderr = run_fail(THIN_CHECK, &["--super-block-only", md_path])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,8 +154,7 @@ fn detects_corrupt_superblock_with_superblock_only() -> Result<()> {
|
|||||||
fn prints_info_fields() -> Result<()> {
|
fn prints_info_fields() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
let stdout = run_ok(THIN_CHECK, args![&md])?;
|
||||||
let stdout = run_ok(THIN_CHECK, &[md_path])?;
|
|
||||||
assert!(stdout.contains("TRANSACTION_ID="));
|
assert!(stdout.contains("TRANSACTION_ID="));
|
||||||
assert!(stdout.contains("METADATA_FREE_BLOCKS="));
|
assert!(stdout.contains("METADATA_FREE_BLOCKS="));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -167,20 +167,19 @@ fn prints_info_fields() -> Result<()> {
|
|||||||
fn auto_repair_incompatible_opts() -> Result<()> {
|
fn auto_repair_incompatible_opts() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
run_fail(THIN_CHECK, args!["--auto-repair", "-m", &md])?;
|
||||||
run_fail(THIN_CHECK, &["--auto-repair", "-m", md_path])?;
|
|
||||||
run_fail(
|
run_fail(
|
||||||
THIN_CHECK,
|
THIN_CHECK,
|
||||||
&["--auto-repair", "--override-mapping-root", "123", md_path],
|
args!["--auto-repair", "--override-mapping-root", "123", &md],
|
||||||
)?;
|
)?;
|
||||||
run_fail(
|
run_fail(
|
||||||
THIN_CHECK,
|
THIN_CHECK,
|
||||||
&["--auto-repair", "--super-block-only", md_path],
|
args!["--auto-repair", "--super-block-only", &md],
|
||||||
)?;
|
)?;
|
||||||
run_fail(THIN_CHECK, &["--auto-repair", "--skip-mappings", md_path])?;
|
run_fail(THIN_CHECK, args!["--auto-repair", "--skip-mappings", &md])?;
|
||||||
run_fail(
|
run_fail(
|
||||||
THIN_CHECK,
|
THIN_CHECK,
|
||||||
&["--auto-repair", "--ignore-non-fatal-errors", md_path],
|
args!["--auto-repair", "--ignore-non-fatal-errors", &md],
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -189,28 +188,23 @@ fn auto_repair_incompatible_opts() -> Result<()> {
|
|||||||
fn clear_needs_check_incompatible_opts() -> Result<()> {
|
fn clear_needs_check_incompatible_opts() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
run_fail(THIN_CHECK, args!["--clear-needs-check-flag", "-m", &md])?;
|
||||||
run_fail(THIN_CHECK, &["--clear-needs-check-flag", "-m", md_path])?;
|
|
||||||
run_fail(
|
run_fail(
|
||||||
THIN_CHECK,
|
THIN_CHECK,
|
||||||
&[
|
args![
|
||||||
"--clear-needs-check-flag",
|
"--clear-needs-check-flag",
|
||||||
"--override-mapping-root",
|
"--override-mapping-root",
|
||||||
"123",
|
"123",
|
||||||
md_path,
|
&md
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
run_fail(
|
run_fail(
|
||||||
THIN_CHECK,
|
THIN_CHECK,
|
||||||
&["--clear-needs-check-flag", "--super-block-only", md_path],
|
args!["--clear-needs-check-flag", "--super-block-only", &md],
|
||||||
)?;
|
)?;
|
||||||
run_fail(
|
run_fail(
|
||||||
THIN_CHECK,
|
THIN_CHECK,
|
||||||
&[
|
args!["--clear-needs-check-flag", "--ignore-non-fatal-errors", &md],
|
||||||
"--clear-needs-check-flag",
|
|
||||||
"--ignore-non-fatal-errors",
|
|
||||||
md_path,
|
|
||||||
],
|
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -222,12 +216,11 @@ fn clear_needs_check_incompatible_opts() -> Result<()> {
|
|||||||
fn clear_needs_check() -> Result<()> {
|
fn clear_needs_check() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
set_needs_check(&md)?;
|
set_needs_check(&md)?;
|
||||||
|
|
||||||
assert!(get_needs_check(&md)?);
|
assert!(get_needs_check(&md)?);
|
||||||
run_ok(THIN_CHECK, &["--clear-needs-check-flag", md_path])?;
|
run_ok(THIN_CHECK, args!["--clear-needs-check-flag", &md])?;
|
||||||
assert!(!get_needs_check(&md)?);
|
assert!(!get_needs_check(&md)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -236,11 +229,10 @@ fn clear_needs_check() -> Result<()> {
|
|||||||
fn no_clear_needs_check_if_error() -> Result<()> {
|
fn no_clear_needs_check_if_error() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
set_needs_check(&md)?;
|
set_needs_check(&md)?;
|
||||||
generate_metadata_leaks(&md, 1, 0, 1)?;
|
generate_metadata_leaks(&md, 1, 0, 1)?;
|
||||||
run_fail(THIN_CHECK, &["--clear-needs-check-flag", md_path])?;
|
run_fail(THIN_CHECK, args!["--clear-needs-check-flag", &md])?;
|
||||||
assert!(get_needs_check(&md)?);
|
assert!(get_needs_check(&md)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -249,7 +241,6 @@ fn no_clear_needs_check_if_error() -> Result<()> {
|
|||||||
fn clear_needs_check_if_skip_mappings() -> Result<()> {
|
fn clear_needs_check_if_skip_mappings() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
set_needs_check(&md)?;
|
set_needs_check(&md)?;
|
||||||
generate_metadata_leaks(&md, 1, 0, 1)?;
|
generate_metadata_leaks(&md, 1, 0, 1)?;
|
||||||
@ -257,7 +248,7 @@ fn clear_needs_check_if_skip_mappings() -> Result<()> {
|
|||||||
assert!(get_needs_check(&md)?);
|
assert!(get_needs_check(&md)?);
|
||||||
run_ok(
|
run_ok(
|
||||||
THIN_CHECK,
|
THIN_CHECK,
|
||||||
&["--clear-needs-check-flag", "--skip-mappings", md_path],
|
args!["--clear-needs-check-flag", "--skip-mappings", &md],
|
||||||
)?;
|
)?;
|
||||||
assert!(!get_needs_check(&md)?);
|
assert!(!get_needs_check(&md)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -270,10 +261,9 @@ fn clear_needs_check_if_skip_mappings() -> Result<()> {
|
|||||||
fn metadata_leaks_are_non_fatal() -> Result<()> {
|
fn metadata_leaks_are_non_fatal() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
generate_metadata_leaks(&md, 1, 0, 1)?;
|
generate_metadata_leaks(&md, 1, 0, 1)?;
|
||||||
run_fail(THIN_CHECK, &[md_path])?;
|
run_fail(THIN_CHECK, args![&md])?;
|
||||||
run_ok(THIN_CHECK, &["--ignore-non-fatal-errors", md_path])?;
|
run_ok(THIN_CHECK, args!["--ignore-non-fatal-errors", &md])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,11 +271,10 @@ fn metadata_leaks_are_non_fatal() -> Result<()> {
|
|||||||
fn fatal_errors_cant_be_ignored() -> Result<()> {
|
fn fatal_errors_cant_be_ignored() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
generate_metadata_leaks(&md, 1, 1, 0)?;
|
generate_metadata_leaks(&md, 1, 1, 0)?;
|
||||||
ensure_untouched(&md, || {
|
ensure_untouched(&md, || {
|
||||||
run_fail(THIN_CHECK, &["--ignore-non-fatal-errors", md_path])?;
|
run_fail(THIN_CHECK, args!["--ignore-non-fatal-errors", &md])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -297,18 +286,17 @@ fn fatal_errors_cant_be_ignored() -> Result<()> {
|
|||||||
fn auto_repair() -> Result<()> {
|
fn auto_repair() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
// auto-repair should have no effect on good metadata.
|
// auto-repair should have no effect on good metadata.
|
||||||
ensure_untouched(&md, || {
|
ensure_untouched(&md, || {
|
||||||
run_ok(THIN_CHECK, &["--auto-repair", md_path])?;
|
run_ok(THIN_CHECK, args!["--auto-repair", &md])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
generate_metadata_leaks(&md, 16, 0, 1)?;
|
generate_metadata_leaks(&md, 16, 0, 1)?;
|
||||||
run_fail(THIN_CHECK, &[md_path])?;
|
run_fail(THIN_CHECK, args![&md])?;
|
||||||
run_ok(THIN_CHECK, &["--auto-repair", md_path])?;
|
run_ok(THIN_CHECK, args!["--auto-repair", &md])?;
|
||||||
run_ok(THIN_CHECK, &[md_path])?;
|
run_ok(THIN_CHECK, args![&md])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,11 +304,10 @@ fn auto_repair() -> Result<()> {
|
|||||||
fn auto_repair_has_limits() -> Result<()> {
|
fn auto_repair_has_limits() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
generate_metadata_leaks(&md, 16, 1, 0)?;
|
generate_metadata_leaks(&md, 16, 1, 0)?;
|
||||||
ensure_untouched(&md, || {
|
ensure_untouched(&md, || {
|
||||||
run_fail(THIN_CHECK, &["--auto-repair", md_path])?;
|
run_fail(THIN_CHECK, args!["--auto-repair", &md])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -330,10 +317,9 @@ fn auto_repair_has_limits() -> Result<()> {
|
|||||||
fn auto_repair_clears_needs_check() -> Result<()> {
|
fn auto_repair_clears_needs_check() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = prep_metadata(&mut td)?;
|
let md = prep_metadata(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
|
|
||||||
set_needs_check(&md)?;
|
set_needs_check(&md)?;
|
||||||
run_ok(THIN_CHECK, &["--auto-repair", md_path])?;
|
run_ok(THIN_CHECK, args!["--auto-repair", &md])?;
|
||||||
assert!(!get_needs_check(&md)?);
|
assert!(!get_needs_check(&md)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,11 @@ use anyhow::Result;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -26,8 +29,8 @@ impl<'a> Program<'a> for ThinDelta {
|
|||||||
"thin_delta"
|
"thin_delta"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_DELTA
|
THIN_DELTA.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -55,7 +58,7 @@ test_rejects_bad_option!(ThinDelta);
|
|||||||
fn snap1_unspecified() -> Result<()> {
|
fn snap1_unspecified() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let stderr = run_fail(THIN_DELTA, &["--snap2", "45", md.to_str().unwrap()])?;
|
let stderr = run_fail(THIN_DELTA, args!["--snap2", "45", &md])?;
|
||||||
assert!(stderr.contains("--snap1 or --root1 not specified"));
|
assert!(stderr.contains("--snap1 or --root1 not specified"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -64,14 +67,14 @@ fn snap1_unspecified() -> Result<()> {
|
|||||||
fn snap2_unspecified() -> Result<()> {
|
fn snap2_unspecified() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let stderr = run_fail(THIN_DELTA, &["--snap1", "45", md.to_str().unwrap()])?;
|
let stderr = run_fail(THIN_DELTA, args!["--snap1", "45", &md])?;
|
||||||
assert!(stderr.contains("--snap2 or --root2 not specified"));
|
assert!(stderr.contains("--snap2 or --root2 not specified"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dev_unspecified() -> Result<()> {
|
fn dev_unspecified() -> Result<()> {
|
||||||
let stderr = run_fail(THIN_DELTA, &["--snap1", "45", "--snap2", "46"])?;
|
let stderr = run_fail(THIN_DELTA, args!["--snap1", "45", "--snap2", "46"])?;
|
||||||
// TODO: replace with msg::MISSING_INPUT_ARG once the rust version is ready
|
// TODO: replace with msg::MISSING_INPUT_ARG once the rust version is ready
|
||||||
assert!(stderr.contains("No input file provided"));
|
assert!(stderr.contains("No input file provided"));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -6,9 +6,13 @@ use std::str::from_utf8;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::fixture::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -32,8 +36,8 @@ impl<'a> Program<'a> for ThinDump {
|
|||||||
"thin_dump"
|
"thin_dump"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_DUMP
|
THIN_DUMP.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -86,8 +90,7 @@ fn dump_restore_cycle() -> Result<()> {
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
|
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
let output = run_ok_raw(THIN_DUMP, args![&md])?;
|
||||||
let output = run_ok_raw(THIN_DUMP, &[md_path])?;
|
|
||||||
|
|
||||||
let xml = td.mk_path("meta.xml");
|
let xml = td.mk_path("meta.xml");
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
@ -99,11 +102,9 @@ fn dump_restore_cycle() -> Result<()> {
|
|||||||
drop(file);
|
drop(file);
|
||||||
|
|
||||||
let md2 = mk_zeroed_md(&mut td)?;
|
let md2 = mk_zeroed_md(&mut td)?;
|
||||||
let md2_path = md2.to_str().unwrap();
|
run_ok(THIN_RESTORE, args!["-i", &xml, "-o", &md2])?;
|
||||||
let xml_path = xml.to_str().unwrap();
|
|
||||||
run_ok(THIN_RESTORE, &["-i", xml_path, "-o", md2_path])?;
|
|
||||||
|
|
||||||
let output2 = run_ok_raw(THIN_DUMP, &[md2_path])?;
|
let output2 = run_ok_raw(THIN_DUMP, args![&md2])?;
|
||||||
assert_eq!(output.stdout, output2.stdout);
|
assert_eq!(output.stdout, output2.stdout);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -117,8 +118,7 @@ fn no_stderr() -> Result<()> {
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
|
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
let output = run_ok_raw(THIN_DUMP, args![&md])?;
|
||||||
let output = run_ok_raw(THIN_DUMP, &[md_path])?;
|
|
||||||
|
|
||||||
assert_eq!(output.stderr.len(), 0);
|
assert_eq!(output.stderr.len(), 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -131,8 +131,7 @@ fn no_stderr() -> Result<()> {
|
|||||||
fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> {
|
fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
let output = run_ok_raw(THIN_DUMP, args![&md, flag, value])?;
|
||||||
let output = run_ok_raw(THIN_DUMP, &[md_path, flag, value])?;
|
|
||||||
|
|
||||||
assert_eq!(output.stderr.len(), 0);
|
assert_eq!(output.stderr.len(), 0);
|
||||||
assert!(from_utf8(&output.stdout[0..])?.contains(pattern));
|
assert!(from_utf8(&output.stdout[0..])?.contains(pattern));
|
||||||
@ -161,23 +160,23 @@ fn repair_superblock() -> Result<()> {
|
|||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let before = run_ok_raw(
|
let before = run_ok_raw(
|
||||||
THIN_DUMP,
|
THIN_DUMP,
|
||||||
&[
|
args![
|
||||||
"--transaction-id=5",
|
"--transaction-id=5",
|
||||||
"--data-block-size=128",
|
"--data-block-size=128",
|
||||||
"--nr-data-blocks=4096000",
|
"--nr-data-blocks=4096000",
|
||||||
md.to_str().unwrap(),
|
&md
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
damage_superblock(&md)?;
|
damage_superblock(&md)?;
|
||||||
|
|
||||||
let after = run_ok_raw(
|
let after = run_ok_raw(
|
||||||
THIN_DUMP,
|
THIN_DUMP,
|
||||||
&[
|
args![
|
||||||
"--repair",
|
"--repair",
|
||||||
"--transaction-id=5",
|
"--transaction-id=5",
|
||||||
"--data-block-size=128",
|
"--data-block-size=128",
|
||||||
"--nr-data-blocks=4096000",
|
"--nr-data-blocks=4096000",
|
||||||
md.to_str().unwrap(),
|
&md
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
assert_eq!(after.stderr.len(), 0);
|
assert_eq!(after.stderr.len(), 0);
|
||||||
@ -197,11 +196,11 @@ fn missing_transaction_id() -> Result<()> {
|
|||||||
damage_superblock(&md)?;
|
damage_superblock(&md)?;
|
||||||
let stderr = run_fail(
|
let stderr = run_fail(
|
||||||
THIN_DUMP,
|
THIN_DUMP,
|
||||||
&[
|
args![
|
||||||
"--repair",
|
"--repair",
|
||||||
"--data-block-size=128",
|
"--data-block-size=128",
|
||||||
"--nr-data-blocks=4096000",
|
"--nr-data-blocks=4096000",
|
||||||
md.to_str().unwrap(),
|
&md
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
assert!(stderr.contains("transaction id"));
|
assert!(stderr.contains("transaction id"));
|
||||||
@ -215,11 +214,11 @@ fn missing_data_block_size() -> Result<()> {
|
|||||||
damage_superblock(&md)?;
|
damage_superblock(&md)?;
|
||||||
let stderr = run_fail(
|
let stderr = run_fail(
|
||||||
THIN_DUMP,
|
THIN_DUMP,
|
||||||
&[
|
args![
|
||||||
"--repair",
|
"--repair",
|
||||||
"--transaction-id=5",
|
"--transaction-id=5",
|
||||||
"--nr-data-blocks=4096000",
|
"--nr-data-blocks=4096000",
|
||||||
md.to_str().unwrap(),
|
&md
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
assert!(stderr.contains("data block size"));
|
assert!(stderr.contains("data block size"));
|
||||||
@ -233,11 +232,11 @@ fn missing_nr_data_blocks() -> Result<()> {
|
|||||||
damage_superblock(&md)?;
|
damage_superblock(&md)?;
|
||||||
let stderr = run_fail(
|
let stderr = run_fail(
|
||||||
THIN_DUMP,
|
THIN_DUMP,
|
||||||
&[
|
args![
|
||||||
"--repair",
|
"--repair",
|
||||||
"--transaction-id=5",
|
"--transaction-id=5",
|
||||||
"--data-block-size=128",
|
"--data-block-size=128",
|
||||||
md.to_str().unwrap(),
|
&md
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
assert!(stderr.contains("nr data blocks"));
|
assert!(stderr.contains("nr data blocks"));
|
||||||
|
@ -5,8 +5,10 @@ mod common;
|
|||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
use common::output_option::*;
|
use common::output_option::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -36,8 +38,8 @@ impl<'a> Program<'a> for ThinMetadataPack {
|
|||||||
"thin_metadata_pack"
|
"thin_metadata_pack"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_METADATA_PACK
|
THIN_METADATA_PACK.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
|
@ -3,10 +3,14 @@ use anyhow::Result;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::fixture::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
use common::output_option::*;
|
use common::output_option::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -36,8 +40,8 @@ impl<'a> Program<'a> for ThinMetadataUnpack {
|
|||||||
"thin_metadata_pack"
|
"thin_metadata_pack"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_METADATA_UNPACK
|
THIN_METADATA_UNPACK.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -102,17 +106,14 @@ fn end_to_end() -> Result<()> {
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md_in = mk_valid_md(&mut td)?;
|
let md_in = mk_valid_md(&mut td)?;
|
||||||
let md_out = mk_zeroed_md(&mut td)?;
|
let md_out = mk_zeroed_md(&mut td)?;
|
||||||
run_ok(
|
run_ok(THIN_METADATA_PACK, args!["-i", &md_in, "-o", "meta.pack"])?;
|
||||||
THIN_METADATA_PACK,
|
|
||||||
&["-i", md_in.to_str().unwrap(), "-o", "meta.pack"],
|
|
||||||
)?;
|
|
||||||
run_ok(
|
run_ok(
|
||||||
THIN_METADATA_UNPACK,
|
THIN_METADATA_UNPACK,
|
||||||
&["-i", "meta.pack", "-o", md_out.to_str().unwrap()],
|
args!["-i", "meta.pack", "-o", &md_out],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let dump1 = run_ok(THIN_DUMP, &[md_in.to_str().unwrap()])?;
|
let dump1 = run_ok(THIN_DUMP, args![&md_in])?;
|
||||||
let dump2 = run_ok(THIN_DUMP, &[md_out.to_str().unwrap()])?;
|
let dump2 = run_ok(THIN_DUMP, args![&md_out])?;
|
||||||
assert_eq!(dump1, dump2);
|
assert_eq!(dump1, dump2);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,14 @@ use anyhow::Result;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::fixture::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
use common::output_option::*;
|
use common::output_option::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -29,8 +33,8 @@ impl<'a> Program<'a> for ThinRepair {
|
|||||||
"thin_repair"
|
"thin_repair"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_REPAIR
|
THIN_REPAIR.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -96,9 +100,7 @@ fn dont_repair_xml() -> Result<()> {
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_zeroed_md(&mut td)?;
|
let md = mk_zeroed_md(&mut td)?;
|
||||||
let xml = mk_valid_xml(&mut td)?;
|
let xml = mk_valid_xml(&mut td)?;
|
||||||
let xml_path = xml.to_str().unwrap();
|
run_fail(THIN_REPAIR, args!["-i", &xml, "-o", &md])?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
run_fail(THIN_REPAIR, &["-i", xml_path, "-o", md_path])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,12 +112,9 @@ fn override_thing(flag: &str, val: &str, pattern: &str) -> Result<()> {
|
|||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md1 = mk_valid_md(&mut td)?;
|
let md1 = mk_valid_md(&mut td)?;
|
||||||
let md2 = mk_zeroed_md(&mut td)?;
|
let md2 = mk_zeroed_md(&mut td)?;
|
||||||
let md1_path = md1.to_str().unwrap();
|
let output = run_ok_raw(THIN_REPAIR, args![flag, val, "-i", &md1, "-o", &md2])?;
|
||||||
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);
|
assert_eq!(output.stderr.len(), 0);
|
||||||
let md2_path = md2.to_str().unwrap();
|
let output = run_ok(THIN_DUMP, args![&md2])?;
|
||||||
let output = run_ok(THIN_DUMP, &[md2_path])?;
|
|
||||||
assert!(output.contains(pattern));
|
assert!(output.contains(pattern));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -140,33 +139,31 @@ fn override_nr_data_blocks() -> Result<()> {
|
|||||||
fn superblock_succeeds() -> Result<()> {
|
fn superblock_succeeds() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md1 = mk_valid_md(&mut td)?;
|
let md1 = mk_valid_md(&mut td)?;
|
||||||
let md1_path = md1.to_str().unwrap();
|
|
||||||
let original = run_ok_raw(
|
let original = run_ok_raw(
|
||||||
THIN_DUMP,
|
THIN_DUMP,
|
||||||
&[
|
args![
|
||||||
"--transaction-id=5",
|
"--transaction-id=5",
|
||||||
"--data-block-size=128",
|
"--data-block-size=128",
|
||||||
"--nr-data-blocks=4096000",
|
"--nr-data-blocks=4096000",
|
||||||
md1_path,
|
&md1
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
assert_eq!(original.stderr.len(), 0);
|
assert_eq!(original.stderr.len(), 0);
|
||||||
damage_superblock(&md1)?;
|
damage_superblock(&md1)?;
|
||||||
let md2 = mk_zeroed_md(&mut td)?;
|
let md2 = mk_zeroed_md(&mut td)?;
|
||||||
let md2_path = md2.to_str().unwrap();
|
|
||||||
run_ok(
|
run_ok(
|
||||||
THIN_REPAIR,
|
THIN_REPAIR,
|
||||||
&[
|
args![
|
||||||
"--transaction-id=5",
|
"--transaction-id=5",
|
||||||
"--data-block-size=128",
|
"--data-block-size=128",
|
||||||
"--nr-data-blocks=4096000",
|
"--nr-data-blocks=4096000",
|
||||||
"-i",
|
"-i",
|
||||||
md1_path,
|
&md1,
|
||||||
"-o",
|
"-o",
|
||||||
md2_path,
|
&md2
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
let repaired = run_ok_raw(THIN_DUMP, &[md2_path])?;
|
let repaired = run_ok_raw(THIN_DUMP, args![&md2])?;
|
||||||
assert_eq!(repaired.stderr.len(), 0);
|
assert_eq!(repaired.stderr.len(), 0);
|
||||||
assert_eq!(original.stdout, repaired.stdout);
|
assert_eq!(original.stdout, repaired.stdout);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -181,17 +178,7 @@ fn missing_thing(flag1: &str, flag2: &str, pattern: &str) -> Result<()> {
|
|||||||
let md1 = mk_valid_md(&mut td)?;
|
let md1 = mk_valid_md(&mut td)?;
|
||||||
damage_superblock(&md1)?;
|
damage_superblock(&md1)?;
|
||||||
let md2 = mk_zeroed_md(&mut td)?;
|
let md2 = mk_zeroed_md(&mut td)?;
|
||||||
let stderr = run_fail(
|
let stderr = run_fail(THIN_REPAIR, args![flag1, flag2, "-i", &md1, "-o", &md2])?;
|
||||||
THIN_REPAIR,
|
|
||||||
&[
|
|
||||||
flag1,
|
|
||||||
flag2,
|
|
||||||
"-i",
|
|
||||||
md1.to_str().unwrap(),
|
|
||||||
"-o",
|
|
||||||
md2.to_str().unwrap(),
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
assert!(stderr.contains(pattern));
|
assert!(stderr.contains(pattern));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,14 @@ use anyhow::Result;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::fixture::*;
|
||||||
use common::input_arg::*;
|
use common::input_arg::*;
|
||||||
use common::output_option::*;
|
use common::output_option::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -30,8 +34,8 @@ impl<'a> Program<'a> for ThinRestore {
|
|||||||
"thin_restore"
|
"thin_restore"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_RESTORE
|
THIN_RESTORE.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -98,9 +102,7 @@ fn quiet_flag(flag: &str) -> Result<()> {
|
|||||||
let xml = mk_valid_xml(&mut td)?;
|
let xml = mk_valid_xml(&mut td)?;
|
||||||
let md = mk_zeroed_md(&mut td)?;
|
let md = mk_zeroed_md(&mut td)?;
|
||||||
|
|
||||||
let xml_path = xml.to_str().unwrap();
|
let output = run_ok_raw(THIN_RESTORE, args!["-i", &xml, "-o", &md, flag])?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
let output = run_ok_raw(THIN_RESTORE, &["-i", xml_path, "-o", md_path, flag])?;
|
|
||||||
|
|
||||||
assert_eq!(output.stdout.len(), 0);
|
assert_eq!(output.stdout.len(), 0);
|
||||||
assert_eq!(output.stderr.len(), 0);
|
assert_eq!(output.stderr.len(), 0);
|
||||||
@ -126,11 +128,9 @@ fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> {
|
|||||||
let xml = mk_valid_xml(&mut td)?;
|
let xml = mk_valid_xml(&mut td)?;
|
||||||
let md = mk_zeroed_md(&mut td)?;
|
let md = mk_zeroed_md(&mut td)?;
|
||||||
|
|
||||||
let xml_path = xml.to_str().unwrap();
|
run_ok(THIN_RESTORE, args!["-i", &xml, "-o", &md, flag, value])?;
|
||||||
let md_path = md.to_str().unwrap();
|
|
||||||
run_ok(THIN_RESTORE, &["-i", xml_path, "-o", md_path, flag, value])?;
|
|
||||||
|
|
||||||
let output = run_ok(THIN_DUMP, &[md_path])?;
|
let output = run_ok(THIN_DUMP, args![&md])?;
|
||||||
assert!(output.contains(pattern));
|
assert!(output.contains(pattern));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,11 @@ use anyhow::Result;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::common_args::*;
|
use common::common_args::*;
|
||||||
|
use common::process::*;
|
||||||
|
use common::program::*;
|
||||||
|
use common::target::*;
|
||||||
use common::test_dir::*;
|
use common::test_dir::*;
|
||||||
use common::*;
|
use common::thin::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
@ -26,8 +29,8 @@ impl<'a> Program<'a> for ThinRmap {
|
|||||||
"thin_rmap"
|
"thin_rmap"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path() -> &'a str {
|
fn path() -> &'a std::ffi::OsStr {
|
||||||
THIN_RMAP
|
THIN_RMAP.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() -> &'a str {
|
fn usage() -> &'a str {
|
||||||
@ -55,8 +58,7 @@ test_rejects_bad_option!(ThinRmap);
|
|||||||
fn valid_region_format_should_pass() -> Result<()> {
|
fn valid_region_format_should_pass() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
let md_path = md.to_str().unwrap();
|
run_ok(THIN_RMAP, args!["--region", "23..7890", &md])?;
|
||||||
run_ok(THIN_RMAP, &["--region", "23..7890", md_path])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ fn invalid_regions_should_fail() -> Result<()> {
|
|||||||
for r in &invalid_regions {
|
for r in &invalid_regions {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
run_fail(THIN_RMAP, &[&r.to_string(), md.to_str().unwrap()])?;
|
run_fail(THIN_RMAP, args![r, &md])?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -86,13 +88,7 @@ fn multiple_regions_should_pass() -> Result<()> {
|
|||||||
let md = mk_valid_md(&mut td)?;
|
let md = mk_valid_md(&mut td)?;
|
||||||
run_ok(
|
run_ok(
|
||||||
THIN_RMAP,
|
THIN_RMAP,
|
||||||
&[
|
args!["--region", "1..23", "--region", "45..78", &md],
|
||||||
"--region",
|
|
||||||
"1..23",
|
|
||||||
"--region",
|
|
||||||
"45..78",
|
|
||||||
md.to_str().unwrap(),
|
|
||||||
],
|
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -101,7 +97,7 @@ fn multiple_regions_should_pass() -> Result<()> {
|
|||||||
fn junk_input() -> Result<()> {
|
fn junk_input() -> Result<()> {
|
||||||
let mut td = TestDir::new()?;
|
let mut td = TestDir::new()?;
|
||||||
let xml = mk_valid_xml(&mut td)?;
|
let xml = mk_valid_xml(&mut td)?;
|
||||||
run_fail(THIN_RMAP, &["--region", "0..-1", xml.to_str().unwrap()])?;
|
run_fail(THIN_RMAP, args!["--region", "0..-1", &xml])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user