[thin_shrink] more test cases

This commit is contained in:
Joe Thornber 2020-07-01 07:52:53 +01:00
parent 4d7f9c7ddc
commit 470d0b7532
2 changed files with 223 additions and 12 deletions

View File

@ -1,4 +1,4 @@
use anyhow::Result; use anyhow::{anyhow, Result};
use fixedbitset::FixedBitSet; use fixedbitset::FixedBitSet;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
@ -459,8 +459,7 @@ pub fn shrink(
eprintln!("{} free blocks.", free_blocks); eprintln!("{} free blocks.", free_blocks);
if free_blocks < pass1.nr_high_blocks { if free_blocks < pass1.nr_high_blocks {
// FIXME: return error return Err(anyhow!("Insufficient space"));
panic!("Insufficient space");
} }
let remaps = build_remaps(above, free); let remaps = build_remaps(above, free);

View File

@ -1,6 +1,6 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use rand::Rng; use rand::prelude::*;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use std::io::{Cursor, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -32,7 +32,7 @@ struct ThinWriteRef<'a, W: Write + Seek> {
impl ThinBlock { impl ThinBlock {
fn read_ref<R: Read + Seek>(&self, r: &mut R) -> Result<ThinReadRef> { fn read_ref<R: Read + Seek>(&self, r: &mut R) -> Result<ThinReadRef> {
let mut rr = ThinReadRef { let mut rr = ThinReadRef {
data: vec![0; self.block_size], data: vec![0; self.block_size * 512],
}; };
let byte = self.data_block * (self.block_size as u64) * 512; let byte = self.data_block * (self.block_size as u64) * 512;
r.seek(SeekFrom::Start(byte))?; r.seek(SeekFrom::Start(byte))?;
@ -44,7 +44,7 @@ impl ThinBlock {
ThinWriteRef { ThinWriteRef {
file: w, file: w,
block_byte: self.data_block * (self.block_size as u64) * 512, block_byte: self.data_block * (self.block_size as u64) * 512,
data: vec![0; self.block_size], data: vec![0; self.block_size * 512],
} }
} }
@ -218,10 +218,7 @@ impl<'a, W: Write + Seek> ThinVisitor for Stamper<'a, W> {
fn thin_block(&mut self, b: &ThinBlock) -> Result<()> { fn thin_block(&mut self, b: &ThinBlock) -> Result<()> {
let mut wr = b.zero_ref(self.data_file); let mut wr = b.zero_ref(self.data_file);
let mut gen = Generator::new(); let mut gen = Generator::new();
gen.fill_buffer( gen.fill_buffer(self.seed ^ (b.thin_id as u64) ^ b.thin_block, &mut wr.data)?;
self.seed ^ (b.thin_id as u64) ^ b.thin_block,
&mut wr.data[0..],
)?;
Ok(()) Ok(())
} }
} }
@ -243,7 +240,7 @@ impl<'a, R: Read + Seek> ThinVisitor for Verifier<'a, R> {
fn thin_block(&mut self, b: &ThinBlock) -> Result<()> { fn thin_block(&mut self, b: &ThinBlock) -> Result<()> {
let rr = b.read_ref(self.data_file)?; let rr = b.read_ref(self.data_file)?;
let mut gen = Generator::new(); let mut gen = Generator::new();
if !gen.verify_buffer(self.seed ^ (b.thin_id as u64) ^ b.thin_block, &rr.data[0..])? { if !gen.verify_buffer(self.seed ^ (b.thin_id as u64) ^ b.thin_block, &rr.data)? {
return Err(anyhow!("data verify failed for {:?}", b)); return Err(anyhow!("data verify failed for {:?}", b));
} }
Ok(()) Ok(())
@ -322,7 +319,8 @@ fn test_shrink(scenario: &mut dyn Scenario) -> Result<()> {
let seed = rng.gen::<u64>(); let seed = rng.gen::<u64>();
stamp(&xml_before, &data_path, seed)?; stamp(&xml_before, &data_path, seed)?;
verify(&xml_before, &data_path, seed)?;
let new_nr_blocks = scenario.get_new_nr_blocks(); let new_nr_blocks = scenario.get_new_nr_blocks();
thinp::shrink::toplevel::shrink(&xml_before, &xml_after, &data_path, new_nr_blocks, true)?; thinp::shrink::toplevel::shrink(&xml_before, &xml_after, &data_path, new_nr_blocks, true)?;
@ -435,4 +433,218 @@ fn shrink_single_partial_move() -> Result<()> {
test_shrink(&mut s) test_shrink(&mut s)
} }
#[test]
fn shrink_single_total_move() -> Result<()> {
let mut s = SingleThinS::new(2048, 1024, 1024 + 2048, 1280);
test_shrink(&mut s)
}
#[test]
fn shrink_insufficient_space() -> Result<()> {
let mut s = SingleThinS::new(0, 2048, 3000, 1280);
match test_shrink(&mut s) {
Ok(_) => Err(anyhow!("Shrink unexpectedly succeeded")),
Err(_) => Ok(()),
}
}
//------------------------------------
struct FragmentedS {
nr_thins: u32,
thin_size: u64,
old_nr_data_blocks: u64,
new_nr_data_blocks: u64,
}
impl FragmentedS {
fn new(nr_thins: u32, thin_size: u64) -> Self {
let old_size = (nr_thins as u64) * thin_size;
FragmentedS {
nr_thins,
thin_size,
old_nr_data_blocks: (nr_thins as u64) * thin_size,
new_nr_data_blocks: old_size * 3 / 4,
}
}
}
#[derive(Clone)]
struct ThinRun {
thin_id: u32,
thin_begin: u64,
len: u64,
}
#[derive(Clone, Debug, Copy)]
struct MappedRun {
thin_id: u32,
thin_begin: u64,
data_begin: u64,
len: u64,
}
fn mk_runs(thin_id: u32, total_len: u64, run_len: std::ops::Range<u64>) -> Vec<ThinRun> {
let mut runs = Vec::new();
let mut b = 0u64;
while b < total_len {
let len = u64::min(
total_len - b,
thread_rng().gen_range(run_len.start, run_len.end),
);
runs.push(ThinRun {
thin_id: thin_id,
thin_begin: b,
len,
});
b += len;
}
runs
}
impl Scenario for FragmentedS {
fn generate_xml(&mut self, v: &mut dyn xml::MetadataVisitor) -> Result<()> {
// Allocate each thin fully, in runs between 1 and 16.
let mut runs = Vec::new();
for thin in 0..self.nr_thins {
runs.append(&mut mk_runs(thin, self.thin_size, 1..17));
}
// Shuffle
runs.shuffle(&mut rand::thread_rng());
// map across the data
let mut maps = Vec::new();
let mut b = 0;
for r in &runs {
maps.push(MappedRun {
thin_id: r.thin_id,
thin_begin: r.thin_begin,
data_begin: b,
len: r.len,
});
b += r.len;
}
// drop half the mappings, which leaves us free runs
let mut dropped = Vec::new();
for i in 0..maps.len() {
if i % 2 == 0 {
dropped.push(maps[i].clone());
}
}
// Unshuffle. This isn't strictly necc. but makes the xml
// more readable.
use std::cmp::Ordering;
maps.sort_by(|&l, &r| match l.thin_id.cmp(&r.thin_id) {
Ordering::Equal => l.thin_begin.cmp(&r.thin_begin),
o => o,
});
// write the xml
v.superblock_b(&common_sb(self.old_nr_data_blocks))?;
for thin in 0..self.nr_thins {
v.device_b(&xml::Device {
dev_id: thin,
mapped_blocks: self.thin_size,
transaction: 0,
creation_time: 0,
snap_time: 0,
})?;
for m in &dropped {
if m.thin_id != thin {
continue;
}
v.map(&xml::Map {
thin_begin: m.thin_begin,
data_begin: m.data_begin,
time: 0,
len: m.len,
})?;
}
v.device_e()?;
}
v.superblock_e()?;
Ok(())
}
fn get_new_nr_blocks(&self) -> u64 {
self.new_nr_data_blocks
}
}
#[test]
fn shrink_fragmented_thin_1() -> Result<()> {
let mut s = FragmentedS::new(1, 2048);
test_shrink(&mut s)
}
#[test]
fn shrink_fragmented_thin_2() -> Result<()> {
let mut s = FragmentedS::new(2, 2048);
test_shrink(&mut s)
}
#[test]
fn shrink_fragmented_thin_8() -> Result<()> {
let mut s = FragmentedS::new(2, 2048);
test_shrink(&mut s)
}
#[test]
fn shrink_fragmented_thin_64() -> Result<()> {
let mut s = FragmentedS::new(2, 2048);
test_shrink(&mut s)
}
//------------------------------------
/*
// Snapshots share mappings, not neccessarily the entire ranges.
struct SnapS {
len: u64,
nr_snaps: u32,
// Snaps will differ from the origin by this percentage
percent_change: usize,
old_nr_data_blocks: u64,
new_nr_data_blocks: u64,
}
impl SnapS {
fn new(len: u64, nr_snaps: u32, percent_change: usize) -> Self {
let delta = len * (nr_snaps as u64) * (percent_change as u64) / 100;
let old_nr_data_blocks = len + 3 * delta;
let new_nr_data_blocks = len + 2 * delta;
SnapS {
len,
nr_snaps,
percent_change,
old_nr_data_blocks,
new_nr_data_blocks,
}
}
}
impl Scenario for SnapS {
fn generate_xml(&mut self, v: &mut dyn xml::MetadataVisitor) -> Result<()> {
let origin = mk_runs(0, self.len, 8..64);
}
fn get_new_nr_blocks(&self) -> u64 {
self.new_nr_data_blocks
}
}
#[test]
fn shrink_identical_snap() -> Result<()> {
let mut s = SnapS::new(1024, 1, 0);
test_shrink(&mut s)
}
*/
//------------------------------------ //------------------------------------