[functional-tests] port some of the cache_check tests to Rust
This commit is contained in:
parent
4a0582bb5d
commit
fa4ea3e2d9
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -53,6 +53,12 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@ -638,6 +644,7 @@ name = "thinp"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"base64",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap",
|
"clap",
|
||||||
"crc32c",
|
"crc32c",
|
||||||
|
@ -7,6 +7,7 @@ license = "GPL3"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
base64 = "0.12"
|
||||||
byteorder = "1.3"
|
byteorder = "1.3"
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
crc32c = "0.4"
|
crc32c = "0.4"
|
||||||
|
@ -49,105 +49,6 @@
|
|||||||
;; to run.
|
;; to run.
|
||||||
(define (register-cache-tests) #t)
|
(define (register-cache-tests) #t)
|
||||||
|
|
||||||
;;;-----------------------------------------------------------
|
|
||||||
;;; cache_check scenarios
|
|
||||||
;;;-----------------------------------------------------------
|
|
||||||
|
|
||||||
(define-scenario (cache-check v)
|
|
||||||
"cache_check -V"
|
|
||||||
(run-ok-rcv (stdout _) (cache-check "-V")
|
|
||||||
(assert-equal tools-version stdout)))
|
|
||||||
|
|
||||||
(define-scenario (cache-check version)
|
|
||||||
"cache_check --version"
|
|
||||||
(run-ok-rcv (stdout _) (cache-check "--version")
|
|
||||||
(assert-equal tools-version stdout)))
|
|
||||||
|
|
||||||
(define-scenario (cache-check h)
|
|
||||||
"cache_check -h"
|
|
||||||
(run-ok-rcv (stdout _) (cache-check "-h")
|
|
||||||
(assert-equal cache-check-help stdout)))
|
|
||||||
|
|
||||||
(define-scenario (cache-check help)
|
|
||||||
"cache_check --help"
|
|
||||||
(run-ok-rcv (stdout _) (cache-check "--help")
|
|
||||||
(assert-equal cache-check-help stdout)))
|
|
||||||
|
|
||||||
(define-scenario (cache-check must-specify-metadata)
|
|
||||||
"Metadata file must be specified"
|
|
||||||
(run-fail-rcv (_ stderr) (cache-check)
|
|
||||||
(assert-equal
|
|
||||||
(string-append "No input file provided.\n"
|
|
||||||
cache-check-help)
|
|
||||||
stderr)))
|
|
||||||
|
|
||||||
(define-scenario (cache-check no-such-metadata)
|
|
||||||
"Metadata file doesn't exist."
|
|
||||||
(let ((bad-path "/arbitrary/filename"))
|
|
||||||
(run-fail-rcv (_ stderr) (cache-check bad-path)
|
|
||||||
(assert-starts-with
|
|
||||||
(string-append bad-path ": No such file or directory")
|
|
||||||
stderr))))
|
|
||||||
|
|
||||||
(define-scenario (cache-check metadata-file-cannot-be-a-directory)
|
|
||||||
"Metadata file must not be a directory"
|
|
||||||
(let ((bad-path "/tmp"))
|
|
||||||
(run-fail-rcv (_ stderr) (cache-check bad-path)
|
|
||||||
(assert-starts-with
|
|
||||||
(string-append bad-path ": Not a block device or regular file")
|
|
||||||
stderr))))
|
|
||||||
|
|
||||||
(define-scenario (cache-check unreadable-metadata)
|
|
||||||
"Metadata file exists, but is unreadable."
|
|
||||||
(with-valid-metadata (md)
|
|
||||||
(run-ok "chmod" "-r" md)
|
|
||||||
(run-fail-rcv (_ stderr) (cache-check md)
|
|
||||||
(assert-starts-with "syscall 'open' failed: Permission denied" stderr))))
|
|
||||||
|
|
||||||
(define-scenario (cache-check fails-with-corrupt-metadata)
|
|
||||||
"Fail with corrupt superblock"
|
|
||||||
(with-corrupt-metadata (md)
|
|
||||||
(run-fail (cache-check md))))
|
|
||||||
|
|
||||||
(define-scenario (cache-check failing-q)
|
|
||||||
"Fail quietly with -q"
|
|
||||||
(with-corrupt-metadata (md)
|
|
||||||
(run-fail-rcv (stdout stderr) (cache-check "-q" md)
|
|
||||||
(assert-eof stdout)
|
|
||||||
(assert-eof stderr))))
|
|
||||||
|
|
||||||
(define-scenario (cache-check failing-quiet)
|
|
||||||
"Fail quietly with --quiet"
|
|
||||||
(with-corrupt-metadata (md)
|
|
||||||
(run-fail-rcv (stdout stderr) (cache-check "--quiet" md)
|
|
||||||
(assert-eof stdout)
|
|
||||||
(assert-eof stderr))))
|
|
||||||
|
|
||||||
(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))))
|
|
||||||
|
|
||||||
;;;-----------------------------------------------------------
|
;;;-----------------------------------------------------------
|
||||||
;;; cache_restore scenarios
|
;;; cache_restore scenarios
|
||||||
;;;-----------------------------------------------------------
|
;;;-----------------------------------------------------------
|
||||||
|
1
src/cache/mod.rs
vendored
Normal file
1
src/cache/mod.rs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod xml;
|
175
src/cache/xml.rs
vendored
Normal file
175
src/cache/xml.rs
vendored
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use base64::encode;
|
||||||
|
use std::{borrow::Cow, fmt::Display, io::Write};
|
||||||
|
|
||||||
|
use quick_xml::events::attributes::Attribute;
|
||||||
|
use quick_xml::events::{BytesEnd, BytesStart, Event};
|
||||||
|
use quick_xml::Writer;
|
||||||
|
|
||||||
|
//---------------------------------------
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Superblock {
|
||||||
|
pub uuid: String,
|
||||||
|
pub block_size: u64,
|
||||||
|
pub nr_cache_blocks: u64,
|
||||||
|
pub policy: String,
|
||||||
|
pub hint_width: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Map {
|
||||||
|
pub cblock: u64,
|
||||||
|
pub oblock: u64,
|
||||||
|
pub dirty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Hint {
|
||||||
|
pub cblock: u64,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Discard {
|
||||||
|
pub begin: u64,
|
||||||
|
pub end: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Visit {
|
||||||
|
Continue,
|
||||||
|
Stop,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MetadataVisitor {
|
||||||
|
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit>;
|
||||||
|
fn superblock_e(&mut self) -> Result<Visit>;
|
||||||
|
|
||||||
|
fn mappings_b(&mut self) -> Result<Visit>;
|
||||||
|
fn mappings_e(&mut self) -> Result<Visit>;
|
||||||
|
fn mapping(&mut self, m: &Map) -> Result<Visit>;
|
||||||
|
|
||||||
|
fn hints_b(&mut self) -> Result<Visit>;
|
||||||
|
fn hints_e(&mut self) -> Result<Visit>;
|
||||||
|
fn hint(&mut self, h: &Hint) -> Result<Visit>;
|
||||||
|
|
||||||
|
fn discards_b(&mut self) -> Result<Visit>;
|
||||||
|
fn discards_e(&mut self) -> Result<Visit>;
|
||||||
|
fn discard(&mut self, d: &Discard) -> Result<Visit>;
|
||||||
|
|
||||||
|
fn eof(&mut self) -> Result<Visit>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct XmlWriter<W: Write> {
|
||||||
|
w: Writer<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> XmlWriter<W> {
|
||||||
|
pub fn new(w: W) -> XmlWriter<W> {
|
||||||
|
XmlWriter {
|
||||||
|
w: Writer::new_with_indent(w, 0x20, 2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_attr_<'a, T: Display>(n: T) -> Cow<'a, [u8]> {
|
||||||
|
let str = format!("{}", n);
|
||||||
|
Cow::Owned(str.into_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_attr<T: Display>(key: &[u8], value: T) -> Attribute {
|
||||||
|
Attribute {
|
||||||
|
key,
|
||||||
|
value: mk_attr_(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> MetadataVisitor for XmlWriter<W> {
|
||||||
|
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit> {
|
||||||
|
let tag = b"superblock";
|
||||||
|
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||||
|
elem.push_attribute(mk_attr(b"uuid", sb.uuid.clone()));
|
||||||
|
elem.push_attribute(mk_attr(b"block_size", sb.block_size));
|
||||||
|
elem.push_attribute(mk_attr(b"nr_cache_blocks", sb.nr_cache_blocks));
|
||||||
|
elem.push_attribute(mk_attr(b"policy", sb.policy.clone()));
|
||||||
|
elem.push_attribute(mk_attr(b"hint_width", sb.hint_width));
|
||||||
|
|
||||||
|
self.w.write_event(Event::Start(elem))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn superblock_e(&mut self) -> Result<Visit> {
|
||||||
|
self.w.write_event(Event::End(BytesEnd::borrowed(b"superblock")))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mappings_b(&mut self) -> Result<Visit> {
|
||||||
|
let tag = b"mappings";
|
||||||
|
let elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||||
|
self.w.write_event(Event::Start(elem))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mappings_e(&mut self) -> Result<Visit> {
|
||||||
|
self.w.write_event(Event::End(BytesEnd::borrowed(b"mappings")))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mapping(&mut self, m: &Map) -> Result<Visit> {
|
||||||
|
let tag = b"map";
|
||||||
|
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||||
|
elem.push_attribute(mk_attr(b"cache_block", m.cblock));
|
||||||
|
elem.push_attribute(mk_attr(b"origin_block", m.oblock));
|
||||||
|
elem.push_attribute(mk_attr(b"dirty", m.dirty));
|
||||||
|
self.w.write_event(Event::Empty(elem))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hints_b(&mut self) -> Result<Visit> {
|
||||||
|
let tag = b"hints";
|
||||||
|
let elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||||
|
self.w.write_event(Event::Start(elem))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hints_e(&mut self) -> Result<Visit> {
|
||||||
|
self.w.write_event(Event::End(BytesEnd::borrowed(b"hints")))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hint(&mut self, h: &Hint) -> Result<Visit> {
|
||||||
|
let tag = b"hint";
|
||||||
|
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||||
|
elem.push_attribute(mk_attr(b"cache_block", h.cblock));
|
||||||
|
elem.push_attribute(mk_attr(b"data", encode(&h.data[0..])));
|
||||||
|
self.w.write_event(Event::Empty(elem))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discards_b(&mut self) -> Result<Visit> {
|
||||||
|
let tag = b"discards";
|
||||||
|
let elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||||
|
self.w.write_event(Event::Start(elem))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discards_e(&mut self) -> Result<Visit> {
|
||||||
|
self.w.write_event(Event::End(BytesEnd::borrowed(b"discards")))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard(&mut self, d: &Discard) -> Result<Visit> {
|
||||||
|
let tag = b"discard";
|
||||||
|
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||||
|
elem.push_attribute(mk_attr(b"dbegin", d.begin));
|
||||||
|
elem.push_attribute(mk_attr(b"dend", d.end));
|
||||||
|
self.w.write_event(Event::Empty(elem))?;
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eof(&mut self) -> Result<Visit> {
|
||||||
|
Ok(Visit::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ extern crate quickcheck;
|
|||||||
extern crate quickcheck_macros;
|
extern crate quickcheck_macros;
|
||||||
|
|
||||||
pub mod block_manager;
|
pub mod block_manager;
|
||||||
|
pub mod cache;
|
||||||
pub mod checksum;
|
pub mod checksum;
|
||||||
pub mod file_utils;
|
pub mod file_utils;
|
||||||
pub mod pack;
|
pub mod pack;
|
||||||
|
@ -212,7 +212,6 @@ pub fn check(dev: &Path) -> Result<()> {
|
|||||||
println!("found {} devices", visitor.devs.len());
|
println!("found {} devices", visitor.devs.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// mapping top level
|
// mapping top level
|
||||||
let mut roots = HashMap::new();
|
let mut roots = HashMap::new();
|
||||||
{
|
{
|
||||||
@ -242,7 +241,6 @@ pub fn check(dev: &Path) -> Result<()> {
|
|||||||
|
|
||||||
pool.join();
|
pool.join();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// data space map
|
// data space map
|
||||||
{
|
{
|
||||||
@ -276,7 +274,7 @@ pub fn check(dev: &Path) -> Result<()> {
|
|||||||
return Err(anyhow!("Index entry points to block ({}) that isn't a bitmap", b.loc));
|
return Err(anyhow!("Index entry points to block ({}) that isn't a bitmap", b.loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bitmap = unpack::<Bitmap>(b.get_data())?;
|
let _bitmap = unpack::<Bitmap>(b.get_data())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::io::{Read, Write};
|
||||||
use std::path::{PathBuf};
|
use std::path::{PathBuf};
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
use tempfile::{tempdir, TempDir};
|
|
||||||
use thinp::file_utils;
|
use thinp::file_utils;
|
||||||
|
|
||||||
pub mod xml_generator;
|
pub mod thin_xml_generator;
|
||||||
use crate::common::xml_generator::{write_xml, SingleThinS};
|
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
|
// Returns stderr, a non zero status must be returned
|
||||||
pub fn run_fail(command: Expression) -> Result<String> {
|
pub fn run_fail(command: Expression) -> Result<String> {
|
||||||
let output = command.stderr_capture().unchecked().run()?;
|
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;
|
mod common;
|
||||||
|
|
||||||
use 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;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use common::test_dir::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use std::str::from_utf8;
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use common::test_dir::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ use thinp::version::TOOLS_VERSION;
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use common::test_dir::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ use thinp::version::TOOLS_VERSION;
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use common::test_dir::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ use thinp::version::TOOLS_VERSION;
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use common::test_dir::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use thinp::version::TOOLS_VERSION;
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use common::test_dir::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ use thinp::version::TOOLS_VERSION;
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use common::test_dir::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ use thinp::file_utils;
|
|||||||
use thinp::thin::xml::{self, Visit};
|
use thinp::thin::xml::{self, Visit};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::test_dir::*;
|
||||||
use common::xml_generator::{
|
use common::thin_xml_generator::{
|
||||||
write_xml, EmptyPoolS, FragmentedS, SingleThinS, SnapS, XmlGen
|
write_xml, EmptyPoolS, FragmentedS, SingleThinS, SnapS, XmlGen
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user