use super::*;
pub struct Defer<F: FnMut()>(pub F);
impl<F: FnMut()> Drop for Defer<F> {
fn drop(&mut self) {
(self.0)()
}
}
macro_rules! defer {
($($body:tt)*) => {
let __deferred = Defer(|| { $($body)* });
};
}
macro_rules! temp_file {
($file_name:expr) => {
defer! {
let _ = dbg!(std::fs::remove_file($file_name));
}
};
}
const ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
#[test]
fn test_corrupt1() {
if cfg!(miri) {
return;
}
let ref key = Key::default();
temp_file!("corrupt1b");
FileEditor::create_empty("corrupt1b", key).unwrap();
{
let mut edit = FileEditor::open("corrupt1b", key).unwrap();
edit.create_file(b"example", ALPHABET, key).unwrap();
edit.finish(key).unwrap();
}
let example_text = {
let reader = FileReader::open("corrupt1b", key).unwrap();
reader.read(b"example", key).unwrap()
};
assert_eq!(example_text, ALPHABET);
}
#[test]
fn test_drop_without_finish_discards_changes() {
if cfg!(miri) {
return;
}
let ref key = [2, 3];
temp_file!("drop_without_finish.paks");
FileEditor::create_empty("drop_without_finish.paks", key).unwrap();
{
let mut edit = FileEditor::open("drop_without_finish.paks", key).unwrap();
edit.create_file(b"pending.txt", ALPHABET, key).unwrap();
}
let reader = FileReader::open("drop_without_finish.paks", key).unwrap();
assert!(matches!(reader.read(b"pending.txt", key), Err(err) if err.kind() == io::ErrorKind::NotFound));
assert!(reader.as_ref().is_empty());
assert_eq!(reader.high_mark(), Header::BLOCKS_LEN as u32);
}
#[test]
fn test_nested_roundtrip_persists_after_finish() {
if cfg!(miri) {
return;
}
let ref key = [9, 10];
let nested = b"profiles/player1/settings.json";
temp_file!("nested_roundtrip.paks");
{
let mut edit = FileEditor::create_new("nested_roundtrip.paks", key).unwrap();
edit.create_file(nested, br#"{"volume":7,"difficulty":"hard"}"#, key).unwrap();
edit.finish(key).unwrap();
}
let reader = FileReader::open("nested_roundtrip.paks", key).unwrap();
assert_eq!(reader.read(nested, key).unwrap(), br#"{"volume":7,"difficulty":"hard"}"#);
let tree = reader.display_children(Some("profiles"), &dir::TreeArt::ASCII).unwrap().to_string();
assert_eq!(tree, "profiles/\n`- player1/\n ` settings.json\n");
let desc = reader.find_file(nested).expect("nested file should exist");
assert_eq!(reader.high_mark(), desc.section.offset + desc.section.size);
assert!(reader.info().directory.offset >= reader.high_mark());
}
#[test]
fn test_file_reader_and_editor_read_variants_agree() {
if cfg!(miri) {
return;
}
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 = [11, 12];
let ref file_key = [13, 14];
let path = b"logs/current.txt";
let expected = TEXT_CONTENT.as_bytes();
temp_file!("read_variants.paks");
{
let mut edit = FileEditor::create_new("read_variants.paks", archive_key).unwrap();
edit.create_file(path, expected, file_key).unwrap();
edit.finish(archive_key).unwrap();
}
let reader = FileReader::open("read_variants.paks", archive_key).unwrap();
let editor = FileEditor::open("read_variants.paks", archive_key).unwrap();
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 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,
);
}
#[test]
fn test_read_only_surfaces_write_errors() {
if cfg!(miri) {
return;
}
let ref archive_key = [15, 16];
let ref file_key = [17, 18];
temp_file!("read_only_errors.paks");
{
let mut edit = FileEditor::create_new("read_only_errors.paks", archive_key).unwrap();
edit.create_file(b"logs/current.txt", b"read only errors", file_key).unwrap();
edit.finish(archive_key).unwrap();
}
let mut create_edit = FileEditor::read_only("read_only_errors.paks", archive_key).unwrap();
assert!(create_edit.create_file(b"logs/new.txt", b"new", file_key).is_err());
let mut finish_edit = FileEditor::read_only("read_only_errors.paks", archive_key).unwrap();
finish_edit.create_dir(b"logs/archive");
assert!(finish_edit.finish(archive_key).is_err());
}
#[test]
fn test_file_edit_file_zero_data_and_reencrypt() {
if cfg!(miri) {
return;
}
let ref archive_key = [21, 22];
let ref old_file_key = [23, 24];
let ref new_file_key = [25, 26];
temp_file!("reencrypt_zero.paks");
{
let mut edit = FileEditor::create_new("reencrypt_zero.paks", archive_key).unwrap();
{
let mut file = edit.edit_file(b"generated/zero.bin");
file.set_content(7, 19);
file.allocate_data().zero_data(old_file_key).unwrap();
file.reencrypt_data(old_file_key, new_file_key).unwrap();
}
edit.finish(archive_key).unwrap();
}
let reader = FileReader::open("reencrypt_zero.paks", archive_key).unwrap();
assert_eq!(reader.read(b"generated/zero.bin", new_file_key).unwrap(), vec![0; 19]);
assert!(reader.read(b"generated/zero.bin", old_file_key).is_err());
}