From 084a26bf8565a6f52b029c921b2cb7b89ca55aef Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 31 Jul 2020 12:12:40 +0100 Subject: [PATCH] [functional-tests] Recode thin_restore tests in Rust. Now part of 'cargo test' --- functional-tests/thin-functional-tests.scm | 98 -------------- tests/common/mod.rs | 98 ++++++++++++++ tests/thin_check.rs | 49 +------ tests/thin_restore.rs | 144 +++++++++++++++++++++ 4 files changed, 245 insertions(+), 144 deletions(-) create mode 100644 tests/thin_restore.rs diff --git a/functional-tests/thin-functional-tests.scm b/functional-tests/thin-functional-tests.scm index 5202d94..4887ff7 100644 --- a/functional-tests/thin-functional-tests.scm +++ b/functional-tests/thin-functional-tests.scm @@ -67,104 +67,6 @@ ;; to run. (define (register-thin-tests) #t) - ;;;----------------------------------------------------------- - ;;; thin_restore scenarios - ;;;----------------------------------------------------------- - - (define-scenario (thin-restore print-version-v) - "print help (-V)" - (run-ok-rcv (stdout _) (thin-restore "-V") - (assert-equal tools-version stdout))) - - (define-scenario (thin-restore print-version-long) - "print help (--version)" - (run-ok-rcv (stdout _) (thin-restore "--version") - (assert-equal tools-version stdout))) - - (define-scenario (thin-restore h) - "print help (-h)" - (run-ok-rcv (stdout _) (thin-restore "-h") - (assert-equal thin-restore-help stdout))) - - (define-scenario (thin-restore help) - "print help (-h)" - (run-ok-rcv (stdout _) (thin-restore "--help") - (assert-equal thin-restore-help stdout))) - - (define-scenario (thin-restore no-input-file) - "forget to specify an input file" - (with-empty-metadata (md) - (run-fail-rcv (_ stderr) (thin-restore "-o" md) - (assert-starts-with "No input file provided." stderr)))) - - (define-scenario (thin-restore missing-input-file) - "the input file can't be found" - (with-empty-metadata (md) - (run-fail-rcv (_ stderr) (thin-restore "-i no-such-file -o" md) - (assert-superblock-all-zeroes md) - (assert-starts-with "Couldn't stat file" stderr)))) - - (define-scenario (thin-restore garbage-input-file) - "the input file is just zeroes" - (with-empty-metadata (md) - (with-temp-file-sized ((xml "thin.xml" 4096)) - (run-fail-rcv (_ stderr) (thin-restore "-i " xml "-o" md) - (assert-superblock-all-zeroes md))))) - - (define-scenario (thin-restore missing-output-file) - "the output file can't be found" - (with-thin-xml (xml) - (run-fail-rcv (_ stderr) (thin-restore "-i " xml) - (assert-starts-with "No output file provided." stderr)))) - - (define-scenario (thin-restore tiny-output-file) - "Fails if the output file is too small." - (with-temp-file-sized ((md "thin.bin" 4096)) - (with-thin-xml (xml) - (run-fail-rcv (_ stderr) (thin-restore "-i" xml "-o" md) - (assert-starts-with thin-restore-outfile-too-small-text stderr))))) - - (define-scenario (thin-restore q) - "thin_restore accepts -q" - (with-empty-metadata (md) - (with-thin-xml (xml) - (run-ok-rcv (stdout _) (thin-restore "-i" xml "-o" md "-q") - (assert-eof stdout))))) - - (define-scenario (thin-restore quiet) - "thin_restore accepts --quiet" - (with-empty-metadata (md) - (with-thin-xml (xml) - (run-ok-rcv (stdout _) (thin-restore "-i" xml "-o" md "--quiet") - (assert-eof stdout))))) - - (define-scenario (thin-restore override transaction-id) - "thin_restore obeys the --transaction-id override" - (with-empty-metadata (md) - (with-thin-xml (xml) - (run-ok-rcv (stdout stderr) (thin-restore "--transaction-id 2345" "-i" xml "-o" md) - (assert-eof stderr)) - (run-ok-rcv (stdout stderr) (thin-dump md) - (assert-matches ".*transaction=\"2345\"" stdout))))) - - (define-scenario (thin-restore override data-block-size) - "thin_restore obeys the --data-block-size override" - (with-empty-metadata (md) - (with-thin-xml (xml) - (run-ok-rcv (stdout stderr) (thin-restore "--data-block-size 8192" "-i" xml "-o" md) - (assert-eof stderr)) - (run-ok-rcv (stdout stderr) (thin-dump md) - (assert-matches ".*data_block_size=\"8192\"" stdout))))) - - (define-scenario (thin-restore override nr-data-blocks) - "thin_restore obeys the --nr-data-blocks override" - (with-empty-metadata (md) - (with-thin-xml (xml) - (run-ok-rcv (stdout stderr) (thin-restore "--nr-data-blocks 234500" "-i" xml "-o" md) - (assert-eof stderr)) - (run-ok-rcv (stdout stderr) (thin-dump md) - (assert-matches ".*nr_data_blocks=\"234500\"" stdout))))) - ;;;----------------------------------------------------------- ;;; thin_dump scenarios ;;;----------------------------------------------------------- diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 325d697..b9fe1f8 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,6 +1,16 @@ +use anyhow::Result; +use duct::{cmd, Expression}; +use std::fs::OpenOptions; use std::path::{Path, PathBuf}; +use std::str::from_utf8; +use tempfile::{tempdir, TempDir}; +use thinp::file_utils; +use std::io::{Read}; pub mod xml_generator; +use crate::common::xml_generator::{write_xml, FragmentedS, SingleThinS}; + +//------------------------------------------ pub fn mk_path(dir: &Path, file: &str) -> PathBuf { let mut p = PathBuf::new(); @@ -9,3 +19,91 @@ pub fn mk_path(dir: &Path, file: &str) -> PathBuf { p } +// FIXME: write a macro to generate these commands +#[macro_export] +macro_rules! thin_check { + ( $( $arg: expr ),* ) => { + { + use std::ffi::OsString; + let args: &[OsString] = &[$( Into::::into($arg) ),*]; + duct::cmd("bin/thin_check", args).stdout_capture().stderr_capture() + } + }; +} + +#[macro_export] +macro_rules! thin_restore { + ( $( $arg: expr ),* ) => { + { + use std::ffi::OsString; + let args: &[OsString] = &[$( Into::::into($arg) ),*]; + duct::cmd("bin/thin_restore", args).stdout_capture().stderr_capture() + } + }; +} + +#[macro_export] +macro_rules! thin_dump { + ( $( $arg: expr ),* ) => { + { + use std::ffi::OsString; + let args: &[OsString] = &[$( Into::::into($arg) ),*]; + duct::cmd("bin/thin_dump", args).stdout_capture().stderr_capture() + } + }; +} + + +// Returns stderr, a non zero status must be returned +pub fn run_fail(command: Expression) -> Result { + let output = command.stderr_capture().unchecked().run()?; + assert!(!output.status.success()); + Ok(from_utf8(&output.stderr[0..]).unwrap().to_string()) +} + +pub fn mk_valid_xml(dir: &TempDir) -> Result { + let xml = mk_path(dir.path(), "meta.xml"); + let mut gen = SingleThinS::new(0, 1024, 2048, 2048); + write_xml(&xml, &mut gen)?; + Ok(xml) +} + +pub fn mk_valid_md(dir: &TempDir) -> Result { + let xml = mk_path(dir.path(), "meta.xml"); + let md = mk_path(dir.path(), "meta.bin"); + + let mut gen = SingleThinS::new(0, 1024, 2048, 2048); + write_xml(&xml, &mut gen)?; + + let _file = file_utils::create_sized_file(&md, 4096 * 4096); + cmd!("bin/thin_restore", "-i", xml, "-o", &md).run()?; + Ok(md) +} + +pub fn mk_zeroed_md(dir: &TempDir) -> Result { + let md = mk_path(dir.path(), "meta.bin"); + let _file = file_utils::create_sized_file(&md, 4096 * 4096); + Ok(md) +} + +pub fn accepts_flag(flag: &str) -> Result<()> { + let dir = tempdir()?; + let md = mk_valid_md(&dir)?; + thin_check!(flag, &md).run()?; + Ok(()) +} + +pub fn superblock_all_zeroes(path: &PathBuf) -> Result { + let mut input = OpenOptions::new().read(true).write(false).open(path)?; + let mut buf = vec![0; 4096]; + input.read_exact(&mut buf[0..])?; + for b in buf { + if b != 0 { + return Ok(false); + } + } + + Ok(true) +} + +//------------------------------------------ diff --git a/tests/thin_check.rs b/tests/thin_check.rs index 385050d..cf59d0f 100644 --- a/tests/thin_check.rs +++ b/tests/thin_check.rs @@ -8,55 +8,11 @@ use thinp::version::TOOLS_VERSION; mod common; -use common::mk_path; +use common::*; use common::xml_generator::{write_xml, FragmentedS, SingleThinS}; //------------------------------------------ -macro_rules! thin_check { - ( $( $arg: expr ),* ) => { - { - use std::ffi::OsString; - let args: &[OsString] = &[$( Into::::into($arg) ),*]; - duct::cmd("bin/thin_check", args).stdout_capture().stderr_capture() - } - }; -} - -// Returns stderr, a non zero status must be returned -fn run_fail(command: Expression) -> Result { - let output = command.stderr_capture().unchecked().run()?; - assert!(!output.status.success()); - Ok(from_utf8(&output.stderr[0..]).unwrap().to_string()) -} - -fn mk_valid_md(dir: &TempDir) -> Result { - let xml = mk_path(dir.path(), "meta.xml"); - let md = mk_path(dir.path(), "meta.bin"); - - let mut gen = SingleThinS::new(0, 1024, 2048, 2048); - write_xml(&xml, &mut gen)?; - - let _file = file_utils::create_sized_file(&md, 4096 * 4096); - cmd!("bin/thin_restore", "-i", xml, "-o", &md).run()?; - Ok(md) -} - -fn mk_corrupt_md(dir: &TempDir) -> Result { - let md = mk_path(dir.path(), "meta.bin"); - let _file = file_utils::create_sized_file(&md, 4096 * 4096); - Ok(md) -} - -fn accepts_flag(flag: &str) -> Result<()> { - let dir = tempdir()?; - let md = mk_valid_md(&dir)?; - thin_check!(flag, &md).run()?; - Ok(()) -} - -//------------------------------------------ - #[test] fn accepts_v() -> Result<()> { let stdout = thin_check!("-V").read()?; @@ -120,6 +76,7 @@ fn accepts_quiet() -> Result<()> { let md = mk_valid_md(&dir)?; let output = thin_check!("--quiet", &md).run()?; + assert!(output.status.success()); assert_eq!(output.stdout.len(), 0); assert_eq!(output.stderr.len(), 0); Ok(()) @@ -128,7 +85,7 @@ fn accepts_quiet() -> Result<()> { #[test] fn detects_corrupt_superblock_with_superblock_only() -> Result<()> { let dir = tempdir()?; - let md = mk_corrupt_md(&dir)?; + let md = mk_zeroed_md(&dir)?; let output = thin_check!("--super-block-only", &md).unchecked().run()?; assert!(!output.status.success()); Ok(()) diff --git a/tests/thin_restore.rs b/tests/thin_restore.rs new file mode 100644 index 0000000..96396bc --- /dev/null +++ b/tests/thin_restore.rs @@ -0,0 +1,144 @@ +use anyhow::Result; +use duct::{cmd, Expression}; +use std::path::{Path, PathBuf}; +use std::str::from_utf8; +use tempfile::{tempdir, TempDir}; +use thinp::file_utils; +use thinp::version::TOOLS_VERSION; + +mod common; + +use common::xml_generator::{write_xml, FragmentedS, SingleThinS}; +use common::*; + +//------------------------------------------ + +#[test] +fn accepts_v() -> Result<()> { + let stdout = thin_restore!("-V").read()?; + assert_eq!(stdout, TOOLS_VERSION); + Ok(()) +} + +#[test] +fn accepts_version() -> Result<()> { + let stdout = thin_restore!("--version").read()?; + assert_eq!(stdout, TOOLS_VERSION); + Ok(()) +} + +const USAGE: &'static str = "Usage: thin_restore [options]\nOptions:\n {-h|--help}\n {-i|--input} \n {-o|--output} \n {--transaction-id} \n {--data-block-size} \n {--nr-data-blocks} \n {-q|--quiet}\n {-V|--version}"; + +#[test] +fn accepts_h() -> Result<()> { + let stdout = thin_restore!("-h").read()?; + assert_eq!(stdout, USAGE); + Ok(()) +} + +#[test] +fn accepts_help() -> Result<()> { + let stdout = thin_restore!("--help").read()?; + assert_eq!(stdout, USAGE); + Ok(()) +} + +#[test] +fn no_input_file() -> Result<()> { + let dir = tempdir()?; + let md = mk_zeroed_md(&dir)?; + let stderr = run_fail(thin_restore!("-o", &md))?; + assert!(stderr.contains("No input file provided.")); + Ok(()) +} + +#[test] +fn missing_input_file() -> Result<()> { + let dir = tempdir()?; + let md = mk_zeroed_md(&dir)?; + let stderr = run_fail(thin_restore!("-i", "no-such-file", "-o", &md))?; + assert!(superblock_all_zeroes(&md)?); + assert!(stderr.contains("Couldn't stat file")); + Ok(()) +} + +#[test] +fn garbage_input_file() -> Result<()> { + let dir = tempdir()?; + let xml = mk_zeroed_md(&dir)?; + let md = mk_zeroed_md(&dir)?; + let _stderr = run_fail(thin_restore!("-i", &xml, "-o", &md))?; + assert!(superblock_all_zeroes(&md)?); + Ok(()) +} + +#[test] +fn no_output_file() -> Result<()> { + let dir = tempdir()?; + let xml = mk_valid_xml(&dir)?; + let stderr = run_fail(thin_restore!("-i", &xml))?; + assert!(stderr.contains("No output file provided.")); + Ok(()) +} + +#[test] +fn tiny_output_file() -> Result<()> { + let dir = tempdir()?; + let xml = mk_valid_xml(&dir)?; + let md = mk_path(dir.path(), "meta.bin"); + let _file = file_utils::create_sized_file(&md, 4096); + let stderr = run_fail(thin_restore!("-i", &xml, "-o", &md))?; + eprintln!("{}", stderr); + assert!(stderr.contains("Output file too small")); + Ok(()) +} + +fn quiet_flag(flag: &str) -> Result<()> { + let dir = tempdir()?; + let xml = mk_valid_xml(&dir)?; + let md = mk_zeroed_md(&dir)?; + + let output = thin_restore!("-i", &xml, "-o", &md, flag).run()?; + + assert!(output.status.success()); + assert_eq!(output.stdout.len(), 0); + assert_eq!(output.stderr.len(), 0); + Ok(()) +} + +#[test] +fn accepts_q() -> Result<()> { + quiet_flag("-q") +} + +#[test] +fn accepts_quiet() -> Result<()> { + quiet_flag("--quiet") +} + +fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> { + let dir = tempdir()?; + let xml = mk_valid_xml(&dir)?; + let md = mk_zeroed_md(&dir)?; + + thin_restore!("-i", &xml, "-o", &md, flag, value).run()?; + + let output = thin_dump!(&md).run()?; + assert!(from_utf8(&output.stdout)?.contains(pattern)); + Ok(()) +} + +#[test] +fn override_transaction_id() -> Result<()> { + override_something("--transaction-id", "2345", "transaction=\"2345\"") +} + +#[test] +fn override_data_block_size() -> Result<()> { + override_something("--data-block-size", "8192", "data_block_size=\"8192\"") +} + +#[test] +fn override_nr_data_blocks() -> Result<()> { + override_something("--nr-data-blocks", "234500", "nr_data_blocks=\"234500\"") +}