[functional-tests] port some of the cache_check tests to Rust
This commit is contained in:
129
tests/cache_check.rs
Normal file
129
tests/cache_check.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use anyhow::Result;
|
||||
use thinp::file_utils;
|
||||
use thinp::version::TOOLS_VERSION;
|
||||
use duct::cmd;
|
||||
|
||||
mod common;
|
||||
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
use common::cache_xml_generator::{write_xml, XmlGen};
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn accepts_v() -> Result<()> {
|
||||
let stdout = cache_check!("-V").read()?;
|
||||
assert_eq!(stdout, TOOLS_VERSION);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_version() -> Result<()> {
|
||||
let stdout = cache_check!("--version").read()?;
|
||||
assert_eq!(stdout, TOOLS_VERSION);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const USAGE: &str = "Usage: cache_check [options] {device|file}\nOptions:\n {-q|--quiet}\n {-h|--help}\n {-V|--version}\n {--clear-needs-check-flag}\n {--super-block-only}\n {--skip-mappings}\n {--skip-hints}\n {--skip-discards}";
|
||||
|
||||
#[test]
|
||||
fn accepts_h() -> Result<()> {
|
||||
let stdout = cache_check!("-h").read()?;
|
||||
assert_eq!(stdout, USAGE);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_help() -> Result<()> {
|
||||
let stdout = cache_check!("--help").read()?;
|
||||
assert_eq!(stdout, USAGE);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_metadata() -> Result<()> {
|
||||
let stderr = run_fail(cache_check!())?;
|
||||
assert!(stderr.contains("No input file provided"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_such_metadata() -> Result<()> {
|
||||
let stderr = run_fail(cache_check!("/arbitrary/filename"))?;
|
||||
assert!(stderr.contains("No such file or directory"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata_cannot_be_a_directory() -> Result<()> {
|
||||
let stderr = run_fail(cache_check!("/tmp"))?;
|
||||
assert!(stderr.contains("Not a block device or regular file"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreadable_metadata() -> Result<()> {
|
||||
let mut td = TestDir::new()?;
|
||||
let md = mk_valid_md(&mut td)?;
|
||||
cmd!("chmod", "-r", &md).run()?;
|
||||
let stderr = run_fail(cache_check!(&md))?;
|
||||
assert!(stderr.contains("syscall 'open' failed: Permission denied"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrupt_metadata() -> Result<()> {
|
||||
let mut td = TestDir::new()?;
|
||||
let md = mk_zeroed_md(&mut td)?;
|
||||
run_fail(cache_check!(&md))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_q() -> Result<()> {
|
||||
let mut td = TestDir::new()?;
|
||||
let md = mk_zeroed_md(&mut td)?;
|
||||
let output = cache_check!("-q", &md).unchecked().run()?;
|
||||
assert!(!output.status.success());
|
||||
assert_eq!(output.stdout.len(), 0);
|
||||
assert_eq!(output.stderr.len(), 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_quiet() -> Result<()> {
|
||||
let mut td = TestDir::new()?;
|
||||
let md = mk_zeroed_md(&mut td)?;
|
||||
let output = cache_check!("--quiet", &md).unchecked().run()?;
|
||||
assert!(!output.status.success());
|
||||
assert_eq!(output.stdout.len(), 0);
|
||||
assert_eq!(output.stderr.len(), 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// (define-scenario (cache-check valid-metadata-passes)
|
||||
// "A valid metadata area passes"
|
||||
// (with-valid-metadata (md)
|
||||
// (run-ok (cache-check md))))
|
||||
//
|
||||
// (define-scenario (cache-check bad-metadata-version)
|
||||
// "Invalid metadata version fails"
|
||||
// (with-cache-xml (xml)
|
||||
// (with-empty-metadata (md)
|
||||
// (cache-restore "-i" xml "-o" md "--debug-override-metadata-version" "12345")
|
||||
// (run-fail (cache-check md)))))
|
||||
//
|
||||
// (define-scenario (cache-check tiny-metadata)
|
||||
// "Prints helpful message in case tiny metadata given"
|
||||
// (with-temp-file-sized ((md "cache.bin" 1024))
|
||||
// (run-fail-rcv (_ stderr) (cache-check md)
|
||||
// (assert-starts-with "Metadata device/file too small. Is this binary metadata?" stderr))))
|
||||
//
|
||||
// (define-scenario (cache-check spot-accidental-xml-data)
|
||||
// "Prints helpful message if XML metadata given"
|
||||
// (with-cache-xml (xml)
|
||||
// (system (fmt #f "man bash >> " xml))
|
||||
// (run-fail-rcv (_ stderr) (cache-check xml)
|
||||
// (assert-matches ".*This looks like XML. cache_check only checks the binary metadata format." stderr))))
|
||||
//
|
||||
94
tests/common/cache_xml_generator.rs
Normal file
94
tests/common/cache_xml_generator.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use anyhow::{Result};
|
||||
use rand::prelude::*;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::Path;
|
||||
use thinp::cache::xml;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
pub trait XmlGen {
|
||||
fn generate_xml(&mut self, v: &mut dyn xml::MetadataVisitor) -> Result<()>;
|
||||
}
|
||||
|
||||
pub fn write_xml(path: &Path, g: &mut dyn XmlGen) -> Result<()> {
|
||||
let xml_out = OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(path)?;
|
||||
let mut w = xml::XmlWriter::new(xml_out);
|
||||
|
||||
g.generate_xml(&mut w)
|
||||
}
|
||||
|
||||
pub struct CacheGen {
|
||||
block_size: u64,
|
||||
nr_cache_blocks: u64,
|
||||
nr_origin_blocks: u64,
|
||||
percent_resident: u8,
|
||||
percent_dirty: u8,
|
||||
}
|
||||
|
||||
impl CacheGen {
|
||||
pub fn new(
|
||||
block_size: u64,
|
||||
nr_cache_blocks: u64,
|
||||
nr_origin_blocks: u64,
|
||||
percent_resident: u8,
|
||||
percent_dirty: u8,
|
||||
) -> Self {
|
||||
CacheGen {
|
||||
block_size,
|
||||
nr_cache_blocks,
|
||||
nr_origin_blocks,
|
||||
percent_resident,
|
||||
percent_dirty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XmlGen for CacheGen {
|
||||
fn generate_xml(&mut self, v: &mut dyn xml::MetadataVisitor) -> Result<()> {
|
||||
v.superblock_b(&xml::Superblock {
|
||||
uuid: "".to_string(),
|
||||
block_size: self.block_size,
|
||||
nr_cache_blocks: self.nr_cache_blocks,
|
||||
policy: "smq".to_string(),
|
||||
hint_width: 4,
|
||||
})?;
|
||||
|
||||
let mut cblocks = Vec::new();
|
||||
for n in 0..self.nr_cache_blocks {
|
||||
cblocks.push(n);
|
||||
}
|
||||
cblocks.shuffle(&mut rand::thread_rng());
|
||||
|
||||
v.mappings_b()?;
|
||||
{
|
||||
let nr_resident = (self.nr_cache_blocks * 100 as u64) / (self.percent_resident as u64);
|
||||
let mut used = HashSet::new();
|
||||
for n in 0..nr_resident {
|
||||
let mut oblock = 0u64;
|
||||
while used.contains(&oblock) {
|
||||
oblock = rand::thread_rng().gen();
|
||||
}
|
||||
|
||||
used.insert(oblock);
|
||||
// FIXME: dirty should vary
|
||||
v.mapping(&xml::Map {
|
||||
cblock: cblocks[n as usize],
|
||||
oblock,
|
||||
dirty: false,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
v.mappings_e()?;
|
||||
|
||||
v.superblock_e()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
@@ -6,11 +6,14 @@ use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{PathBuf};
|
||||
use std::str::from_utf8;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
use thinp::file_utils;
|
||||
|
||||
pub mod xml_generator;
|
||||
use crate::common::xml_generator::{write_xml, SingleThinS};
|
||||
pub mod thin_xml_generator;
|
||||
pub mod cache_xml_generator;
|
||||
pub mod test_dir;
|
||||
|
||||
use crate::common::thin_xml_generator::{write_xml, SingleThinS};
|
||||
use test_dir::TestDir;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
@@ -103,28 +106,19 @@ macro_rules! thin_metadata_unpack {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cache_check {
|
||||
( $( $arg: expr ),* ) => {
|
||||
{
|
||||
use std::ffi::OsString;
|
||||
let args: &[OsString] = &[$( Into::<OsString>::into($arg) ),*];
|
||||
duct::cmd("bin/cache_check", args).stdout_capture().stderr_capture()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
pub struct TestDir {
|
||||
dir: TempDir,
|
||||
file_count: usize,
|
||||
}
|
||||
|
||||
impl TestDir {
|
||||
pub fn new() -> Result<TestDir> {
|
||||
let dir = tempdir()?;
|
||||
Ok(TestDir { dir, file_count: 0 })
|
||||
}
|
||||
|
||||
pub fn mk_path(&mut self, file: &str) -> PathBuf {
|
||||
let mut p = PathBuf::new();
|
||||
p.push(&self.dir);
|
||||
p.push(PathBuf::from(format!("{:02}_{}", self.file_count, file)));
|
||||
self.file_count += 1;
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
// Returns stderr, a non zero status must be returned
|
||||
pub fn run_fail(command: Expression) -> Result<String> {
|
||||
let output = command.stderr_capture().unchecked().run()?;
|
||||
|
||||
27
tests/common/test_dir.rs
Normal file
27
tests/common/test_dir.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use anyhow::Result;
|
||||
use std::path::{PathBuf};
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
pub struct TestDir {
|
||||
dir: TempDir,
|
||||
file_count: usize,
|
||||
}
|
||||
|
||||
impl TestDir {
|
||||
pub fn new() -> Result<TestDir> {
|
||||
let dir = tempdir()?;
|
||||
Ok(TestDir { dir, file_count: 0 })
|
||||
}
|
||||
|
||||
pub fn mk_path(&mut self, file: &str) -> PathBuf {
|
||||
let mut p = PathBuf::new();
|
||||
p.push(&self.dir);
|
||||
p.push(PathBuf::from(format!("{:02}_{}", self.file_count, file)));
|
||||
self.file_count += 1;
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
@@ -5,7 +5,8 @@ use thinp::version::TOOLS_VERSION;
|
||||
mod common;
|
||||
|
||||
use common::*;
|
||||
use common::xml_generator::{write_xml, FragmentedS};
|
||||
use common::test_dir::*;
|
||||
use common::thin_xml_generator::{write_xml, FragmentedS};
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use thinp::version::TOOLS_VERSION;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::str::from_utf8;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use thinp::version::TOOLS_VERSION;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use thinp::version::TOOLS_VERSION;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use thinp::version::TOOLS_VERSION;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use thinp::version::TOOLS_VERSION;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use thinp::version::TOOLS_VERSION;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::test_dir::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ use thinp::file_utils;
|
||||
use thinp::thin::xml::{self, Visit};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use common::xml_generator::{
|
||||
use common::test_dir::*;
|
||||
use common::thin_xml_generator::{
|
||||
write_xml, EmptyPoolS, FragmentedS, SingleThinS, SnapS, XmlGen
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user