use super::*;
const EXAMPLE: &[u8] = include_str!("../../tests/data/example.txt").as_bytes();
#[test]
fn test_simple() {
let ref key = [1, 2];
let (blocks, _) = MemoryEditor::new().finish(key);
let mut edit = MemoryEditor::from_blocks(blocks, key).expect("failed to edit");
edit.create_file(b"example", EXAMPLE, key);
let (blocks, _) = edit.finish(key);
let reader = MemoryReader::from_blocks(blocks, key).expect("failed to read");
let dir = &*reader;
let listing = dir::DirFmt::new(".", dir.as_ref(), &dir::TreeArt::ASCII).to_string();
assert_eq!(dbg!(listing), "./\n` example\n");
let desc = reader.find_file(b"example").expect("example file not found");
let example = reader.read_data(desc, key).expect("failed to read example");
assert_eq!(example, EXAMPLE);
}
#[test]
fn test_bundled_archive() {
let ref key = [7, 11];
let mut edit = MemoryEditor::new();
edit.create_file(b"nested/example.txt", EXAMPLE, key);
let (blocks, _) = edit.finish(key);
let reader = BundleReader::open(&blocks, *key).expect("failed to open embedded archive");
let desc = reader.find_file(b"nested/example.txt").expect("missing embedded file");
let data = reader.read_data(&desc, key).expect("failed to read embedded file");
assert_eq!(data, EXAMPLE);
}
#[test]
fn test_bundle_rejects_tampered_directory() {
let ref key = [7, 11];
let mut edit = MemoryEditor::new();
edit.create_file(b"nested/example.txt", EXAMPLE, key);
let (mut blocks, _) = edit.finish(key);
blocks.last_mut().unwrap()[0] ^= 1;
assert!(matches!(BundleReader::open(&blocks, *key), Err(ErrorKind::InvalidData)));
}
#[test]
fn test_gc_reclaims_removed_file_space() {
let ref key = [5, 8];
let mut edit = MemoryEditor::new();
edit.create_file(b"keep.txt", EXAMPLE, key);
edit.create_file(b"remove.txt", &[0xAB; 96], key);
let high_mark_before_remove = edit.high_mark();
let removed = edit.remove(b"remove.txt").expect("file should be removable");
assert!(removed.is_file());
edit.gc();
assert!(edit.high_mark() < high_mark_before_remove);
let keep = edit.read(b"keep.txt", key).expect("remaining file should still read");
assert_eq!(keep, EXAMPLE);
let keep_desc = edit.find_file(b"keep.txt").expect("remaining file should still exist");
assert_eq!(keep_desc.section.offset, Header::BLOCKS_LEN as u32);
assert_eq!(edit.high_mark(), keep_desc.section.offset + keep_desc.section.size);
let (blocks, _) = edit.finish(key);
let reader = MemoryReader::from_blocks(blocks.clone(), key).expect("compacted archive should reopen");
let reopened = MemoryEditor::from_blocks(blocks, key).expect("compacted archive should reopen for editing");
assert_eq!(reader.read(b"keep.txt", key).unwrap(), EXAMPLE);
assert!(matches!(reader.read(b"remove.txt", key), Err(ErrorKind::NotFound)));
assert!(reader.find_file(b"keep.txt").is_some());
assert!(reader.find_file(b"remove.txt").is_none());
let mut log = String::new();
let reopened_keep = reopened.find_file(b"keep.txt").expect("remaining file should still exist after reopen");
assert_eq!(reopened.high_mark(), reopened_keep.section.offset + reopened_keep.section.size);
assert!(reopened.fsck(reopened.high_mark(), &mut log), "fsck failed: {log}");
}
#[test]
fn test_read_data_into_reads_requested_window() {
let ref key = [21, 34];
let payload = b"header:payload:footer";
let mut edit = MemoryEditor::new();
edit.create_file(b"logs/current.txt", payload, key);
let (blocks, _) = edit.finish(key);
let reader = MemoryReader::from_blocks(blocks, key).expect("failed to open archive");
let desc = reader.find_file(b"logs/current.txt").expect("file should exist");
let mut slice = [0u8; 7];
reader.read_data_into(desc, key, 7, &mut slice).expect("partial read should succeed");
assert_eq!(&slice, b"payload");
assert!(matches!(reader.read(b"logs/missing.txt", key), Err(ErrorKind::NotFound)));
}
#[test]
fn test_memory_and_bundle_read_variants_agree() {
const TEXT_CONTENT: &str = "header:payload:footer";
const READ_WINDOW_OFFSET: usize = 7;
const READ_WINDOW: &[u8] = b"payload";
#[track_caller]
fn assert_read_variants(
read: Vec<u8>,
text: String,
section: Vec<Block>,
data: Vec<u8>,
window: &[u8],
expected: &[u8],
) {
assert_eq!(read, expected);
assert_eq!(text, std::str::from_utf8(expected).unwrap());
assert_eq!(data, expected);
assert_eq!(window, &expected[READ_WINDOW_OFFSET..READ_WINDOW_OFFSET + READ_WINDOW.len()]);
assert_eq!(&dataview::bytes(section.as_slice())[..expected.len()], expected);
}
let ref archive_key = [31, 32];
let ref file_key = [33, 34];
let path = b"logs/current.txt";
let expected = TEXT_CONTENT.as_bytes();
let mut builder = MemoryEditor::new();
builder.create_file(path, expected, file_key);
let (blocks, _) = builder.finish(archive_key);
let editor = MemoryEditor::from_blocks(blocks.clone(), archive_key).expect("failed to reopen archive for editing");
let reader = MemoryReader::from_blocks(blocks.clone(), archive_key).expect("failed to reopen archive for reading");
let bundle = BundleReader::open(&blocks, *archive_key).expect("failed to open bundled archive");
let editor_desc = *editor.find_file(path).expect("editor descriptor missing");
let mut editor_window = [0u8; READ_WINDOW.len()];
editor.read_data_into(&editor_desc, file_key, READ_WINDOW_OFFSET, &mut editor_window).unwrap();
assert_read_variants(
editor.read(path, file_key).unwrap(),
editor.read_to_string(path, file_key).unwrap(),
editor.read_section(&editor_desc.section, file_key).unwrap(),
editor.read_data(&editor_desc, file_key).unwrap(),
&editor_window,
expected,
);
let reader_desc = *reader.find_file(path).expect("reader descriptor missing");
let mut reader_window = [0u8; READ_WINDOW.len()];
reader.read_data_into(&reader_desc, file_key, READ_WINDOW_OFFSET, &mut reader_window).unwrap();
assert_read_variants(
reader.read(path, file_key).unwrap(),
reader.read_to_string(path, file_key).unwrap(),
reader.read_section(&reader_desc.section, file_key).unwrap(),
reader.read_data(&reader_desc, file_key).unwrap(),
&reader_window,
expected,
);
let bundle_desc = bundle.find_file(path).expect("bundle descriptor missing");
let mut bundle_window = [0u8; READ_WINDOW.len()];
bundle.read_data_into(&bundle_desc, file_key, READ_WINDOW_OFFSET, &mut bundle_window).unwrap();
assert_read_variants(
bundle.read(path, file_key).unwrap(),
bundle.read_to_string(path, file_key).unwrap(),
bundle.read_section(&bundle_desc.section, file_key).unwrap(),
bundle.read_data(&bundle_desc, file_key).unwrap(),
&bundle_window,
expected,
);
}
#[test]
fn test_memory_edit_file_zero_data_and_reencrypt() {
let ref archive_key = [41, 42];
let ref old_file_key = [43, 44];
let ref new_file_key = [45, 46];
let mut edit = MemoryEditor::new();
{
let mut file = edit.edit_file(b"generated/zero.bin");
file.set_content(7, 19);
file.allocate_data().zero_data(old_file_key);
file.reencrypt_data(old_file_key, new_file_key);
}
let (blocks, _) = edit.finish(archive_key);
let reader = MemoryReader::from_blocks(blocks, archive_key).expect("failed to reopen archive");
assert_eq!(reader.read(b"generated/zero.bin", new_file_key).unwrap(), vec![0; 19]);
assert!(matches!(reader.read(b"generated/zero.bin", old_file_key), Err(ErrorKind::InvalidData)));
}