From d0bdbaa1ed76dfbf3211bb43eee48c49cc4fd448 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Mon, 22 Jun 2026 13:12:27 +0400 Subject: feat: implement FParkan architecture foundation Add the modular fparkan workspace, domain crates, adapters, apps, xtask policy/CI, acceptance evidence, and licensed corpus gates for the macOS-focused roadmap foundation. --- crates/texm/src/tests.rs | 330 ----------------------------------------------- 1 file changed, 330 deletions(-) delete mode 100644 crates/texm/src/tests.rs (limited to 'crates/texm/src/tests.rs') diff --git a/crates/texm/src/tests.rs b/crates/texm/src/tests.rs deleted file mode 100644 index 49a7100..0000000 --- a/crates/texm/src/tests.rs +++ /dev/null @@ -1,330 +0,0 @@ -use super::*; -use common::collect_files_recursive; -use nres::Archive; -use proptest::prelude::*; -use std::fs; -use std::path::{Path, PathBuf}; - -fn nres_test_files() -> Vec { - let root = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("testdata"); - let mut files = Vec::new(); - collect_files_recursive(&root, &mut files); - files.sort(); - files - .into_iter() - .filter(|path| { - fs::read(path) - .map(|bytes| bytes.get(0..4) == Some(b"NRes")) - .unwrap_or(false) - }) - .collect() -} - -fn build_texm_payload( - width: u32, - height: u32, - format_raw: u32, - flags5: u32, - palette: Option<[u8; 1024]>, - mip_levels: &[&[u8]], -) -> Vec { - let mut payload = Vec::new(); - payload.extend_from_slice(&TEXM_MAGIC.to_le_bytes()); - payload.extend_from_slice(&width.to_le_bytes()); - payload.extend_from_slice(&height.to_le_bytes()); - payload.extend_from_slice( - &u32::try_from(mip_levels.len()) - .expect("mip level count overflow in test") - .to_le_bytes(), - ); - payload.extend_from_slice(&0u32.to_le_bytes()); // flags4 - payload.extend_from_slice(&flags5.to_le_bytes()); - payload.extend_from_slice(&0u32.to_le_bytes()); // unk6 - payload.extend_from_slice(&format_raw.to_le_bytes()); - if let Some(palette) = palette { - payload.extend_from_slice(&palette); - } - for level in mip_levels { - payload.extend_from_slice(level); - } - payload -} - -#[test] -fn texm_parse_all_game_textures() { - let archives = nres_test_files(); - if archives.is_empty() { - eprintln!("skipping texm_parse_all_game_textures: no NRes files in testdata"); - return; - } - - let mut texm_total = 0usize; - let mut texm_with_page = 0usize; - for archive_path in archives { - let archive = Archive::open_path(&archive_path) - .unwrap_or_else(|err| panic!("failed to open {}: {err}", archive_path.display())); - - for entry in archive.entries() { - if entry.meta.kind != TEXM_MAGIC { - continue; - } - texm_total += 1; - let payload = archive.read(entry.id).unwrap_or_else(|err| { - panic!( - "failed to read Texm entry '{}' in {}: {err}", - entry.meta.name, - archive_path.display() - ) - }); - let texture = parse_texm(payload.as_slice()).unwrap_or_else(|err| { - panic!( - "failed to parse Texm '{}' in {}: {err}", - entry.meta.name, - archive_path.display() - ) - }); - if !texture.page_rects.is_empty() { - texm_with_page += 1; - } - - assert!( - texture.core_size() <= payload.as_slice().len(), - "core size must be within payload for '{}' in {}", - entry.meta.name, - archive_path.display() - ); - assert_eq!( - usize::try_from(texture.header.mip_count).ok(), - Some(texture.mip_levels.len()), - "mip count mismatch for '{}' in {}", - entry.meta.name, - archive_path.display() - ); - } - } - - assert!(texm_total > 0, "no Texm textures found"); - assert!( - texm_with_page > 0, - "expected at least one Texm texture with Page chunk" - ); -} - -#[test] -fn texm_parse_minimal_argb8888_no_page() { - let payload = build_texm_payload(1, 1, 8888, 0, None, &[&[1, 2, 3, 4]]); - - let parsed = parse_texm(&payload).expect("failed to parse minimal texm"); - assert_eq!(parsed.header.width, 1); - assert_eq!(parsed.header.height, 1); - assert_eq!(parsed.mip_levels.len(), 1); - assert!(parsed.page_rects.is_empty()); -} - -#[test] -fn texm_decode_minimal_argb8888_no_page() { - let payload = build_texm_payload(1, 1, 8888, 0, None, &[&[0x40, 0x11, 0x22, 0x33]]); - let parsed = parse_texm(&payload).expect("failed to parse minimal texm"); - let decoded = decode_mip_rgba8(&parsed, &payload, 0).expect("failed to decode mip"); - assert_eq!(decoded.width, 1); - assert_eq!(decoded.height, 1); - assert_eq!(decoded.rgba8, vec![0x11, 0x22, 0x33, 0x40]); -} - -#[test] -fn texm_decode_rgb565() { - let word = 0xFFE0u16; // r=31 g=63 b=0 - let payload = build_texm_payload(1, 1, 565, 0, None, &[&word.to_le_bytes()]); - let parsed = parse_texm(&payload).expect("failed to parse rgb565 texm"); - let decoded = decode_mip_rgba8(&parsed, &payload, 0).expect("failed to decode rgb565 texm"); - assert_eq!(decoded.rgba8, vec![255, 255, 0, 255]); -} - -#[test] -fn texm_decode_rgb556() { - let word = 0xF800u16; // r=31 g=0 b=0 - let payload = build_texm_payload(1, 1, 556, 0, None, &[&word.to_le_bytes()]); - let parsed = parse_texm(&payload).expect("failed to parse rgb556 texm"); - let decoded = decode_mip_rgba8(&parsed, &payload, 0).expect("failed to decode rgb556 texm"); - assert_eq!(decoded.rgba8, vec![255, 0, 0, 255]); -} - -#[test] -fn texm_decode_argb4444() { - let word = 0xF12Eu16; // a=F r=1 g=2 b=E - let payload = build_texm_payload(1, 1, 4444, 0, None, &[&word.to_le_bytes()]); - let parsed = parse_texm(&payload).expect("failed to parse argb4444 texm"); - let decoded = decode_mip_rgba8(&parsed, &payload, 0).expect("failed to decode argb4444 texm"); - assert_eq!(decoded.rgba8, vec![17, 34, 238, 255]); -} - -#[test] -fn texm_decode_luminance_alpha88() { - let word = 0x7F40u16; // luminance=0x7F alpha=0x40 - let payload = build_texm_payload(1, 1, 88, 0, None, &[&word.to_le_bytes()]); - let parsed = parse_texm(&payload).expect("failed to parse la88 texm"); - let decoded = decode_mip_rgba8(&parsed, &payload, 0).expect("failed to decode la88 texm"); - assert_eq!(decoded.rgba8, vec![0x7F, 0x7F, 0x7F, 0x40]); -} - -#[test] -fn texm_decode_rgb888x() { - let payload = build_texm_payload(1, 1, 888, 0, None, &[&[0x11, 0x22, 0x33, 0x99]]); - let parsed = parse_texm(&payload).expect("failed to parse rgb888 texm"); - let decoded = decode_mip_rgba8(&parsed, &payload, 0).expect("failed to decode rgb888 texm"); - assert_eq!(decoded.rgba8, vec![0x11, 0x22, 0x33, 255]); -} - -#[test] -fn texm_parse_indexed_with_page_chunk() { - let mut palette = [0u8; 1024]; - palette[4..8].copy_from_slice(&[10, 20, 30, 255]); - let mut payload = build_texm_payload(2, 2, 0, 0, Some(palette), &[&[1, 1, 1, 1]]); - payload.extend_from_slice(&PAGE_MAGIC.to_le_bytes()); - payload.extend_from_slice(&1u32.to_le_bytes()); // rect_count - payload.extend_from_slice(&0i16.to_le_bytes()); // x - payload.extend_from_slice(&2i16.to_le_bytes()); // w - payload.extend_from_slice(&0i16.to_le_bytes()); // y - payload.extend_from_slice(&2i16.to_le_bytes()); // h - - let parsed = parse_texm(&payload).expect("failed to parse indexed texm"); - assert!(parsed.palette.is_some()); - assert_eq!(parsed.page_rects.len(), 1); - assert_eq!( - parsed.page_rects[0], - PageRect { - x: 0, - w: 2, - y: 0, - h: 2 - } - ); -} - -#[test] -fn texm_decode_indexed_with_palette_last_entry() { - let mut palette = [0u8; 1024]; - palette[4..8].copy_from_slice(&[10, 20, 30, 255]); // index 1 - palette[8..12].copy_from_slice(&[40, 50, 60, 200]); // index 2 - palette[1020..1024].copy_from_slice(&[1, 2, 3, 4]); // index 255 (last) - let payload = build_texm_payload(3, 1, 0, 0, Some(palette), &[&[1u8, 2u8, 255u8]]); - - let parsed = parse_texm(&payload).expect("failed to parse indexed texm"); - let decoded = decode_mip_rgba8(&parsed, &payload, 0).expect("failed to decode indexed texm"); - assert_eq!(decoded.width, 3); - assert_eq!(decoded.height, 1); - assert_eq!( - decoded.rgba8, - vec![10, 20, 30, 255, 40, 50, 60, 200, 1, 2, 3, 4] - ); -} - -#[test] -fn texm_parse_multi_mip_offsets() { - let mip0 = [0x10u8; 32]; // 4*2*4 - let mip1 = [0x20u8; 8]; // 2*1*4 - let mip2 = [0x30u8; 4]; // 1*1*4 - let payload = build_texm_payload(4, 2, 8888, 0, None, &[&mip0, &mip1, &mip2]); - - let parsed = parse_texm(&payload).expect("failed to parse multi-mip texm"); - assert_eq!(parsed.header.mip_count, 3); - assert_eq!(parsed.mip_levels.len(), 3); - assert_eq!( - parsed.mip_levels, - vec![ - MipLevel { - width: 4, - height: 2, - offset: 32, - size: 32 - }, - MipLevel { - width: 2, - height: 1, - offset: 64, - size: 8 - }, - MipLevel { - width: 1, - height: 1, - offset: 72, - size: 4 - }, - ] - ); -} - -#[test] -fn texm_preserves_flags5_for_mip_skip_metadata() { - let payload = build_texm_payload(1, 1, 8888, 0x0000_00A5, None, &[&[0, 0, 0, 0]]); - let parsed = parse_texm(&payload).expect("failed to parse texm"); - assert_eq!(parsed.header.flags5, 0x0000_00A5); -} - -#[test] -fn texm_errors_for_invalid_header_values() { - let mut bad_magic = build_texm_payload(1, 1, 8888, 0, None, &[&[0, 0, 0, 0]]); - bad_magic[0..4].copy_from_slice(&0u32.to_le_bytes()); - assert!(matches!( - parse_texm(&bad_magic), - Err(Error::InvalidMagic { .. }) - )); - - let zero_dims = build_texm_payload(0, 1, 8888, 0, None, &[&[]]); - assert!(matches!( - parse_texm(&zero_dims), - Err(Error::InvalidDimensions { .. }) - )); - - let mut bad_mips = build_texm_payload(1, 1, 8888, 0, None, &[&[0, 0, 0, 0]]); - bad_mips[12..16].copy_from_slice(&0u32.to_le_bytes()); - assert!(matches!( - parse_texm(&bad_mips), - Err(Error::InvalidMipCount { .. }) - )); - - let bad_format = build_texm_payload(1, 1, 12345, 0, None, &[&[0, 0, 0, 0]]); - assert!(matches!( - parse_texm(&bad_format), - Err(Error::UnknownFormat { .. }) - )); -} - -#[test] -fn texm_errors_for_page_chunk_and_mip_bounds() { - let mut bad_page = build_texm_payload(1, 1, 8888, 0, None, &[&[0, 0, 0, 0]]); - bad_page.extend_from_slice(b"X"); - assert!(matches!( - parse_texm(&bad_page), - Err(Error::InvalidPageSize { .. }) - )); - - let payload = build_texm_payload(1, 1, 8888, 0, None, &[&[1, 2, 3, 4]]); - let parsed = parse_texm(&payload).expect("failed to parse valid texm"); - assert!(matches!( - decode_mip_rgba8(&parsed, &payload, 7), - Err(Error::MipIndexOutOfRange { .. }) - )); - - let truncated = &payload[..payload.len() - 1]; - assert!(matches!( - decode_mip_rgba8(&parsed, truncated, 0), - Err(Error::MipDataOutOfBounds { .. }) - )); -} - -proptest! { - #![proptest_config(ProptestConfig::with_cases(64))] - - #[test] - fn parse_texm_is_panic_free_on_random_bytes(payload in proptest::collection::vec(any::(), 0..4096)) { - if let Ok(texture) = parse_texm(&payload) { - for mip_index in 0..texture.mip_levels.len() { - let _ = decode_mip_rgba8(&texture, &payload, mip_index); - } - } - } -} -- cgit v1.2.3