use std::fs::File;
use std::io::Write;
use std::path::Path;
use bale::{ArchiveWrite, ArchiveWriter, BaleEocd, Eocd, Zip64Eocd, Zip64EocdLocator};
use zerocopy::IntoBytes;
const VALID_FIXTURES_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/valid");
const INVALID_FIXTURES_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/invalid");
#[test]
#[ignore]
fn generate_all_fixtures() {
generate_empty_bale();
generate_single_file_bale();
generate_multi_file_bale();
generate_align_16k_bale();
generate_path_2048_bale();
generate_orphaned_data_bale();
generate_unsorted_cd_bale();
generate_duplicate_paths_bale();
generate_bad_crc_bale();
}
fn generate_empty_bale() {
let path = Path::new(VALID_FIXTURES_DIR).join("empty.bale");
let mut file = File::create(&path).expect("failed to create empty.bale");
let zip64_eocd = Zip64Eocd::new(0, 0, 0);
file.write_all(zip64_eocd.as_bytes())
.expect("failed to write ZIP64 EOCD");
let zip64_locator = Zip64EocdLocator::new(0);
file.write_all(zip64_locator.as_bytes())
.expect("failed to write ZIP64 EOCD Locator");
let eocd = Eocd::new_with_comment(0, 0, 0, BaleEocd::SIZE as u16);
file.write_all(eocd.as_bytes())
.expect("failed to write EOCD");
let bale_eocd = BaleEocd::new();
file.write_all(bale_eocd.as_bytes())
.expect("failed to write BaleEocd");
}
fn generate_single_file_bale() {
let fixtures_dir = Path::new(VALID_FIXTURES_DIR);
let archive_path = fixtures_dir.join("single_file.bale");
let content_path = fixtures_dir.join("hello.txt");
let mut src = File::create(&content_path).expect("failed to create hello.txt");
src.write_all(b"Hello, World!")
.expect("failed to write content");
drop(src);
let _ = std::fs::remove_file(&archive_path);
let mut writer = ArchiveWriter::create(&archive_path).expect("failed to create archive");
writer
.add_file(&content_path, "hello.txt")
.expect("failed to add file");
writer.sync().expect("failed to sync archive");
std::fs::remove_file(&content_path).expect("failed to remove hello.txt");
}
fn generate_multi_file_bale() {
let archive_path = Path::new(VALID_FIXTURES_DIR).join("multi_file.bale");
let _ = std::fs::remove_file(&archive_path);
let mut writer = ArchiveWriter::create(&archive_path).expect("failed to create archive");
writer
.add_entry("docs/", b"", 0o040755)
.expect("failed to add docs/");
writer
.add_entry("src/", b"", 0o040755)
.expect("failed to add src/");
writer
.add_entry("src/bin/", b"", 0o040700)
.expect("failed to add src/bin/");
writer
.add_entry("README.md", b"# Project\n\nA sample project.\n", 0o100644)
.expect("failed to add README.md");
writer
.add_entry("docs/guide.txt", b"User guide content here.\n", 0o100644)
.expect("failed to add docs/guide.txt");
writer
.add_entry(
"src/main.rs",
b"fn main() { println!(\"Hello\"); }\n",
0o100644,
)
.expect("failed to add src/main.rs");
writer
.add_entry("build.sh", b"#!/bin/bash\ncargo build\n", 0o100755)
.expect("failed to add build.sh");
writer
.add_entry("src/bin/tool", b"ELF binary placeholder", 0o100755)
.expect("failed to add src/bin/tool");
writer
.add_entry("LICENSE", b"MIT License\n", 0o100444)
.expect("failed to add LICENSE");
writer.sync().expect("failed to sync archive");
}
fn generate_align_16k_bale() {
let fixtures_dir = Path::new(VALID_FIXTURES_DIR);
let archive_path = fixtures_dir.join("align_16k.bale");
let content_path = fixtures_dir.join("align_test.txt");
let mut src = File::create(&content_path).expect("failed to create source file");
src.write_all(b"16KB alignment test")
.expect("failed to write content");
drop(src);
let _ = std::fs::remove_file(&archive_path);
let mut writer = ArchiveWriter::create_with_options(&archive_path, 16384, 256)
.expect("failed to create archive");
writer
.add_file(&content_path, "align_test.txt")
.expect("failed to add file");
writer.sync().expect("failed to sync archive");
std::fs::remove_file(&content_path).expect("failed to remove source file");
}
fn generate_path_2048_bale() {
let fixtures_dir = Path::new(VALID_FIXTURES_DIR);
let archive_path = fixtures_dir.join("path_2048.bale");
let content_path = fixtures_dir.join("path_test.txt");
let mut src = File::create(&content_path).expect("failed to create source file");
src.write_all(b"2048-byte path size test")
.expect("failed to write content");
drop(src);
let _ = std::fs::remove_file(&archive_path);
let mut writer = ArchiveWriter::create_with_options(&archive_path, 4096, 2048)
.expect("failed to create archive");
writer
.add_file(&content_path, "path_test.txt")
.expect("failed to add file");
writer.sync().expect("failed to sync archive");
std::fs::remove_file(&content_path).expect("failed to remove source file");
}
fn generate_orphaned_data_bale() {
let archive_path = Path::new(VALID_FIXTURES_DIR).join("orphaned_data.bale");
let _ = std::fs::remove_file(&archive_path);
let mut writer = ArchiveWriter::create(&archive_path).expect("failed to create archive");
writer
.add_entry("first.txt", b"This will be deleted", 0o644)
.expect("failed to add first entry");
writer
.add_entry("second.txt", b"This stays", 0o644)
.expect("failed to add second entry");
writer.delete("first.txt");
writer.sync().expect("failed to sync archive");
}
fn generate_unsorted_cd_bale() {
let archive_path = Path::new(INVALID_FIXTURES_DIR).join("unsorted_cd.bale");
let _ = std::fs::remove_file(&archive_path);
let mut writer = ArchiveWriter::create(&archive_path).expect("failed to create archive");
writer
.add_entry("c.txt", b"third", 0o644)
.expect("failed to add c.txt");
writer
.add_entry("b.txt", b"second", 0o644)
.expect("failed to add b.txt");
writer
.add_entry("a.txt", b"first", 0o644)
.expect("failed to add a.txt");
writer.sync().expect("failed to sync archive");
}
fn generate_duplicate_paths_bale() {
let archive_path = Path::new(INVALID_FIXTURES_DIR).join("duplicate_paths.bale");
let _ = std::fs::remove_file(&archive_path);
let mut writer = ArchiveWriter::create(&archive_path).expect("failed to create archive");
writer
.add_entry("file.txt", b"version 1", 0o644)
.expect("failed to add first file.txt");
writer
.add_entry("file.txt", b"version 2", 0o644)
.expect("failed to add second file.txt");
writer
.add_entry("file.txt", b"version 3", 0o644)
.expect("failed to add third file.txt");
writer.sync().expect("failed to sync archive");
}
fn generate_bad_crc_bale() {
use std::io::{Read, Seek, SeekFrom};
let archive_path = Path::new(INVALID_FIXTURES_DIR).join("bad_crc.bale");
let _ = std::fs::remove_file(&archive_path);
{
let mut writer = ArchiveWriter::create(&archive_path).expect("failed to create archive");
writer
.add_entry("test.txt", b"test content", 0o644)
.expect("failed to add entry");
writer.sync().expect("failed to sync archive");
}
let mut file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&archive_path)
.expect("failed to open archive");
let file_len = file.metadata().expect("failed to get metadata").len();
let trailer_size = 256u64; let cd_header_size = 46u64;
let path_size = 256u64;
let cd_entry_size = cd_header_size + path_size;
let cd_offset = file_len - trailer_size - cd_entry_size;
let crc_offset = cd_offset + 16;
file.seek(SeekFrom::Start(crc_offset))
.expect("failed to seek");
let mut crc_bytes = [0u8; 4];
file.read_exact(&mut crc_bytes).expect("failed to read CRC");
crc_bytes[0] ^= 0xFF;
file.seek(SeekFrom::Start(crc_offset))
.expect("failed to seek");
file.write_all(&crc_bytes)
.expect("failed to write corrupted CRC");
}