#![cfg(feature = "write")]
use hadris_fat::file::ShortFileName;
#[test]
fn test_short_filename_from_long_name_simple() {
let sfn = ShortFileName::from_long_name("test.txt", 0).unwrap();
assert_eq!(sfn.as_str(), "TEST .TXT");
}
#[test]
fn test_short_filename_from_long_name_with_suffix() {
let sfn = ShortFileName::from_long_name("test.txt", 1).unwrap();
assert!(sfn.as_str().contains("~1"));
}
#[test]
fn test_short_filename_from_long_name_no_extension() {
let sfn = ShortFileName::from_long_name("README", 0).unwrap();
assert!(sfn.as_str().starts_with("README"));
}
#[test]
fn test_short_filename_from_long_name_long_base() {
let sfn = ShortFileName::from_long_name("verylongfilename.txt", 1).unwrap();
assert!(sfn.as_str().starts_with("VERYLO~1"));
}
#[test]
fn test_short_filename_from_long_name_lowercase() {
let sfn = ShortFileName::from_long_name("lowercase.dat", 0).unwrap();
assert_eq!(sfn.as_str(), "LOWERCAS.DAT");
}
#[test]
fn test_short_filename_from_long_name_special_chars() {
let sfn = ShortFileName::from_long_name("my file.txt", 0).unwrap();
assert!(!sfn.as_str().contains(' ') || sfn.as_str().chars().filter(|c| *c == ' ').count() <= 3);
}
#[cfg(feature = "std")]
mod datetime_tests {
use hadris_fat::FatDateTime;
#[test]
fn test_fat_datetime_now() {
let dt = FatDateTime::now();
assert!(dt.date > 0);
}
#[test]
fn test_fat_datetime_new() {
let dt = FatDateTime::new(2024, 6, 15, 14, 30, 45);
let (date, time, _) = dt.to_raw();
let year = ((date >> 9) & 0x7F) + 1980;
let month = (date >> 5) & 0x0F;
let day = date & 0x1F;
assert_eq!(year, 2024);
assert_eq!(month, 6);
assert_eq!(day, 15);
let hour = (time >> 11) & 0x1F;
let minute = (time >> 5) & 0x3F;
let second = (time & 0x1F) * 2;
assert_eq!(hour, 14);
assert_eq!(minute, 30);
assert!(second == 44 || second == 46);
}
}
#[cfg(feature = "std")]
mod fat32_image {
use std::io::Cursor;
const SECTOR_SIZE: usize = 512;
const SECTORS_PER_CLUSTER: u8 = 1;
const CLUSTER_SIZE: usize = SECTOR_SIZE * SECTORS_PER_CLUSTER as usize;
const RESERVED_SECTORS: u16 = 32;
const FAT_COUNT: u8 = 2;
const SECTORS_PER_FAT: u32 = 128;
const ROOT_CLUSTER: u32 = 2;
const FSINFO_LEAD_SIG: u32 = 0x41615252;
const FSINFO_STRUC_SIG: u32 = 0x61417272;
const FSINFO_TRAIL_SIG: u32 = 0xAA550000;
pub fn create_fat32_image() -> Cursor<Vec<u8>> {
let data_start_sector = RESERVED_SECTORS as u32 + FAT_COUNT as u32 * SECTORS_PER_FAT;
let total_data_clusters: u32 = 256; let total_sectors = data_start_sector + total_data_clusters;
let total_size = total_sectors as usize * SECTOR_SIZE;
let mut image = vec![0u8; total_size];
write_boot_sector(&mut image);
write_fsinfo_sector(&mut image, total_data_clusters - 1);
let fat_start = RESERVED_SECTORS as usize * SECTOR_SIZE;
write_fat_table(&mut image, fat_start);
let fat2_start = fat_start + SECTORS_PER_FAT as usize * SECTOR_SIZE;
write_fat_table(&mut image, fat2_start);
Cursor::new(image)
}
fn write_boot_sector(image: &mut [u8]) {
image[0] = 0xEB;
image[1] = 0x58;
image[2] = 0x90;
image[3..11].copy_from_slice(b"HADRISFT");
image[11..13].copy_from_slice(&(SECTOR_SIZE as u16).to_le_bytes());
image[13] = SECTORS_PER_CLUSTER;
image[14..16].copy_from_slice(&RESERVED_SECTORS.to_le_bytes());
image[16] = FAT_COUNT;
image[17..19].copy_from_slice(&0u16.to_le_bytes());
image[19..21].copy_from_slice(&0u16.to_le_bytes());
image[21] = 0xF8;
image[22..24].copy_from_slice(&0u16.to_le_bytes());
image[24..26].copy_from_slice(&63u16.to_le_bytes());
image[26..28].copy_from_slice(&255u16.to_le_bytes());
image[28..32].copy_from_slice(&0u32.to_le_bytes());
let total_sectors = RESERVED_SECTORS as u32 + FAT_COUNT as u32 * SECTORS_PER_FAT + 256;
image[32..36].copy_from_slice(&total_sectors.to_le_bytes());
image[36..40].copy_from_slice(&SECTORS_PER_FAT.to_le_bytes());
image[40..42].copy_from_slice(&0u16.to_le_bytes());
image[42..44].copy_from_slice(&0u16.to_le_bytes());
image[44..48].copy_from_slice(&ROOT_CLUSTER.to_le_bytes());
image[48..50].copy_from_slice(&1u16.to_le_bytes());
image[50..52].copy_from_slice(&6u16.to_le_bytes());
image[64] = 0x80;
image[65] = 0;
image[66] = 0x29;
image[67..71].copy_from_slice(&0x12345678u32.to_le_bytes());
image[71..82].copy_from_slice(b"TEST ");
image[82..90].copy_from_slice(b"FAT32 ");
image[510] = 0x55;
image[511] = 0xAA;
}
fn write_fsinfo_sector(image: &mut [u8], free_clusters: u32) {
let offset = SECTOR_SIZE;
image[offset..offset + 4].copy_from_slice(&FSINFO_LEAD_SIG.to_le_bytes());
image[offset + 484..offset + 488].copy_from_slice(&FSINFO_STRUC_SIG.to_le_bytes());
image[offset + 488..offset + 492].copy_from_slice(&free_clusters.to_le_bytes());
image[offset + 492..offset + 496].copy_from_slice(&3u32.to_le_bytes());
image[offset + 508..offset + 512].copy_from_slice(&FSINFO_TRAIL_SIG.to_le_bytes());
}
fn write_fat_table(image: &mut [u8], fat_start: usize) {
image[fat_start..fat_start + 4].copy_from_slice(&0x0FFFFFF8u32.to_le_bytes());
image[fat_start + 4..fat_start + 8].copy_from_slice(&0x0FFFFFFFu32.to_le_bytes());
image[fat_start + 8..fat_start + 12].copy_from_slice(&0x0FFFFFF8u32.to_le_bytes());
}
pub const fn cluster_size() -> usize {
CLUSTER_SIZE
}
}
#[cfg(feature = "std")]
mod fat16_image {
use std::io::Cursor;
const SECTOR_SIZE: usize = 512;
const SECTORS_PER_CLUSTER: u8 = 1;
const CLUSTER_SIZE: usize = SECTOR_SIZE * SECTORS_PER_CLUSTER as usize;
const RESERVED_SECTORS: u16 = 1;
const FAT_COUNT: u8 = 2;
const SECTORS_PER_FAT: u16 = 32;
const ROOT_ENTRY_COUNT: u16 = 512;
pub fn create_fat16_image() -> Cursor<Vec<u8>> {
let root_dir_sectors = (ROOT_ENTRY_COUNT as usize * 32 + SECTOR_SIZE - 1) / SECTOR_SIZE;
let data_start_sector = RESERVED_SECTORS as usize
+ FAT_COUNT as usize * SECTORS_PER_FAT as usize
+ root_dir_sectors;
let total_data_clusters: usize = 8192; let total_sectors = data_start_sector + total_data_clusters;
let total_size = total_sectors * SECTOR_SIZE;
let mut image = vec![0u8; total_size];
write_boot_sector(&mut image, total_sectors as u32);
let fat_start = RESERVED_SECTORS as usize * SECTOR_SIZE;
write_fat_table(&mut image, fat_start);
let fat2_start = fat_start + SECTORS_PER_FAT as usize * SECTOR_SIZE;
write_fat_table(&mut image, fat2_start);
Cursor::new(image)
}
fn write_boot_sector(image: &mut [u8], total_sectors: u32) {
image[0] = 0xEB;
image[1] = 0x3C;
image[2] = 0x90;
image[3..11].copy_from_slice(b"HADRISFT");
image[11..13].copy_from_slice(&(SECTOR_SIZE as u16).to_le_bytes());
image[13] = SECTORS_PER_CLUSTER;
image[14..16].copy_from_slice(&RESERVED_SECTORS.to_le_bytes());
image[16] = FAT_COUNT;
image[17..19].copy_from_slice(&ROOT_ENTRY_COUNT.to_le_bytes());
if total_sectors <= 65535 {
image[19..21].copy_from_slice(&(total_sectors as u16).to_le_bytes());
} else {
image[19..21].copy_from_slice(&0u16.to_le_bytes());
}
image[21] = 0xF8;
image[22..24].copy_from_slice(&SECTORS_PER_FAT.to_le_bytes());
image[24..26].copy_from_slice(&63u16.to_le_bytes());
image[26..28].copy_from_slice(&255u16.to_le_bytes());
image[28..32].copy_from_slice(&0u32.to_le_bytes());
if total_sectors > 65535 {
image[32..36].copy_from_slice(&total_sectors.to_le_bytes());
} else {
image[32..36].copy_from_slice(&0u32.to_le_bytes());
}
image[36] = 0x80;
image[37] = 0;
image[38] = 0x29;
image[39..43].copy_from_slice(&0x12345678u32.to_le_bytes());
image[43..54].copy_from_slice(b"TEST ");
image[54..62].copy_from_slice(b"FAT16 ");
image[510] = 0x55;
image[511] = 0xAA;
}
fn write_fat_table(image: &mut [u8], fat_start: usize) {
image[fat_start..fat_start + 2].copy_from_slice(&0xFFF8u16.to_le_bytes());
image[fat_start + 2..fat_start + 4].copy_from_slice(&0xFFFFu16.to_le_bytes());
}
pub const fn cluster_size() -> usize {
CLUSTER_SIZE
}
}
#[cfg(feature = "std")]
mod fat12_image {
use std::io::Cursor;
const SECTOR_SIZE: usize = 512;
const SECTORS_PER_CLUSTER: u8 = 1;
const CLUSTER_SIZE: usize = SECTOR_SIZE * SECTORS_PER_CLUSTER as usize;
const RESERVED_SECTORS: u16 = 1;
const FAT_COUNT: u8 = 2;
const SECTORS_PER_FAT: u16 = 9;
const ROOT_ENTRY_COUNT: u16 = 224;
pub fn create_fat12_image() -> Cursor<Vec<u8>> {
let root_dir_sectors = (ROOT_ENTRY_COUNT as usize * 32 + SECTOR_SIZE - 1) / SECTOR_SIZE;
let _data_start_sector = RESERVED_SECTORS as usize
+ FAT_COUNT as usize * SECTORS_PER_FAT as usize
+ root_dir_sectors;
let total_sectors: usize = 2880;
let total_size = total_sectors * SECTOR_SIZE;
let mut image = vec![0u8; total_size];
write_boot_sector(&mut image, total_sectors as u16);
let fat_start = RESERVED_SECTORS as usize * SECTOR_SIZE;
write_fat_table(&mut image, fat_start);
let fat2_start = fat_start + SECTORS_PER_FAT as usize * SECTOR_SIZE;
write_fat_table(&mut image, fat2_start);
Cursor::new(image)
}
fn write_boot_sector(image: &mut [u8], total_sectors: u16) {
image[0] = 0xEB;
image[1] = 0x3C;
image[2] = 0x90;
image[3..11].copy_from_slice(b"HADRISFT");
image[11..13].copy_from_slice(&(SECTOR_SIZE as u16).to_le_bytes());
image[13] = SECTORS_PER_CLUSTER;
image[14..16].copy_from_slice(&RESERVED_SECTORS.to_le_bytes());
image[16] = FAT_COUNT;
image[17..19].copy_from_slice(&ROOT_ENTRY_COUNT.to_le_bytes());
image[19..21].copy_from_slice(&total_sectors.to_le_bytes());
image[21] = 0xF0;
image[22..24].copy_from_slice(&SECTORS_PER_FAT.to_le_bytes());
image[24..26].copy_from_slice(&18u16.to_le_bytes());
image[26..28].copy_from_slice(&2u16.to_le_bytes());
image[28..32].copy_from_slice(&0u32.to_le_bytes());
image[32..36].copy_from_slice(&0u32.to_le_bytes());
image[36] = 0x00;
image[37] = 0;
image[38] = 0x29;
image[39..43].copy_from_slice(&0x12345678u32.to_le_bytes());
image[43..54].copy_from_slice(b"TEST ");
image[54..62].copy_from_slice(b"FAT12 ");
image[510] = 0x55;
image[511] = 0xAA;
}
fn write_fat_table(image: &mut [u8], fat_start: usize) {
image[fat_start] = 0xF0; image[fat_start + 1] = 0xFF; image[fat_start + 2] = 0xFF;
}
pub const fn cluster_size() -> usize {
CLUSTER_SIZE
}
}
#[cfg(feature = "std")]
mod integration_tests {
use super::fat32_image::{cluster_size, create_fat32_image};
use hadris_fat::{FatError, FatFs, FatFsWriteExt};
#[test]
fn test_open_fat32_image() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let entries: Vec<_> = root.entries().collect();
assert!(entries.is_empty(), "Root directory should be empty");
}
#[test]
fn test_create_file() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "TEST.TXT")
.expect("Failed to create file");
assert!(entry.is_file());
assert_eq!(entry.size(), 0);
let root = fs.root_dir();
let found = root.find("TEST.TXT").expect("Find failed");
assert!(found.is_some(), "File should be found in directory");
}
#[test]
fn test_create_file_already_exists() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
fs.create_file(&root, "TEST.TXT")
.expect("Failed to create file");
let result = fs.create_file(&root, "TEST.TXT");
match result {
Err(FatError::AlreadyExists) => {}
_ => panic!("Expected AlreadyExists error"),
}
}
#[test]
fn test_write_file_content() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "HELLO.TXT")
.expect("Failed to create file");
let content = b"Hello, FAT32 World!";
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(content).expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("HELLO.TXT")
.expect("Find failed")
.expect("File not found");
assert_eq!(entry.size(), content.len());
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let mut buf = vec![0u8; content.len()];
let mut total = 0;
while total < buf.len() {
let n = reader.read(&mut buf[total..]).expect("Read failed");
if n == 0 {
break;
}
total += n;
}
assert_eq!(&buf[..total], content);
}
#[test]
fn test_write_file_multiple_clusters() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "BIG.DAT")
.expect("Failed to create file");
let cluster_sz = cluster_size();
let content: Vec<u8> = (0..cluster_sz * 3).map(|i| (i % 256) as u8).collect();
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(&content).expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("BIG.DAT")
.expect("Find failed")
.expect("File not found");
assert_eq!(entry.size(), content.len());
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let read_content = reader.read_to_vec().expect("Read failed");
assert_eq!(read_content, content);
}
#[test]
fn test_create_directory() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let subdir = fs
.create_dir(&root, "SUBDIR")
.expect("Failed to create directory");
let entries: Vec<_> = subdir.entries().collect();
let names: Vec<_> = entries
.iter()
.filter_map(|e| e.as_ref().ok())
.map(|e| e.name().trim_end_matches(' ').to_string())
.collect();
assert!(
names.iter().any(|n| n.starts_with('.')),
"Directory should have . entry: {:?}",
names
);
assert!(
names.iter().any(|n| n.starts_with("..")),
"Directory should have .. entry: {:?}",
names
);
}
#[test]
fn test_create_file_in_subdirectory() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let subdir = fs
.create_dir(&root, "MYDIR")
.expect("Failed to create directory");
let entry = fs
.create_file(&subdir, "FILE.TXT")
.expect("Failed to create file");
assert!(entry.is_file());
let found = subdir.find("FILE.TXT").expect("Find failed");
assert!(found.is_some());
let root = fs.root_dir();
let found_in_root = root.find("FILE.TXT").expect("Find failed");
assert!(found_in_root.is_none());
}
#[test]
fn test_delete_file() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "DELETE.ME")
.expect("Failed to create file");
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(b"temporary data").expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("DELETE.ME")
.expect("Find failed")
.expect("File not found");
fs.delete(&entry).expect("Failed to delete");
let root = fs.root_dir();
let found = root.find("DELETE.ME").expect("Find failed");
assert!(found.is_none(), "File should be deleted");
}
#[test]
fn test_delete_empty_directory() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let _subdir = fs
.create_dir(&root, "EMPTYDIR")
.expect("Failed to create directory");
let root = fs.root_dir();
let entry = root
.find("EMPTYDIR")
.expect("Find failed")
.expect("Dir not found");
fs.delete(&entry).expect("Failed to delete empty directory");
let root = fs.root_dir();
let found = root.find("EMPTYDIR").expect("Find failed");
assert!(found.is_none(), "Directory should be deleted");
}
#[test]
fn test_delete_non_empty_directory_fails() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let subdir = fs
.create_dir(&root, "HASFILE")
.expect("Failed to create directory");
fs.create_file(&subdir, "INSIDE.TXT")
.expect("Failed to create file");
let root = fs.root_dir();
let entry = root
.find("HASFILE")
.expect("Find failed")
.expect("Dir not found");
let result = fs.delete(&entry);
match result {
Err(FatError::DirectoryNotEmpty) => {}
_ => panic!("Expected DirectoryNotEmpty error"),
}
}
#[test]
fn test_create_multiple_files() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
for i in 0..10 {
let name = format!("FILE{}.TXT", i);
fs.create_file(&root, &name).expect("Failed to create file");
}
let root = fs.root_dir();
let entries: Vec<_> = root.entries().collect();
assert_eq!(entries.len(), 10, "Should have 10 files");
}
#[test]
fn test_case_insensitive_find() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
fs.create_file(&root, "MYFILE.TXT")
.expect("Failed to create file");
let root = fs.root_dir();
assert!(root.find("MYFILE.TXT").expect("Find failed").is_some());
assert!(root.find("myfile.txt").expect("Find failed").is_some());
assert!(root.find("MyFile.Txt").expect("Find failed").is_some());
}
#[test]
fn test_write_then_append() {
let image = create_fat32_image();
let fs = FatFs::open(image).expect("Failed to open FAT32 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "APPEND.TXT")
.expect("Failed to create file");
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(b"First part. ").expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("APPEND.TXT")
.expect("Find failed")
.expect("File not found");
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let content = reader.read_to_vec().expect("Read failed");
assert_eq!(&content, b"First part. ");
}
}
#[cfg(feature = "std")]
mod fat16_integration_tests {
use super::fat16_image::{cluster_size, create_fat16_image};
use hadris_fat::{FatError, FatFs, FatFsWriteExt, FatType};
#[test]
fn test_open_fat16_image() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
assert!(
matches!(fs.fat_type(), FatType::Fat16),
"Expected FAT16, got {:?}",
fs.fat_type()
);
let root = fs.root_dir();
let entries: Vec<_> = root.entries().collect();
assert!(entries.is_empty(), "Root directory should be empty");
}
#[test]
fn test_fat16_create_file() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "TEST.TXT")
.expect("Failed to create file");
assert!(entry.is_file());
assert_eq!(entry.size(), 0);
let root = fs.root_dir();
let found = root.find("TEST.TXT").expect("Find failed");
assert!(found.is_some(), "File should be found in directory");
}
#[test]
fn test_fat16_write_file_content() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "HELLO.TXT")
.expect("Failed to create file");
let content = b"Hello, FAT16 World!";
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(content).expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("HELLO.TXT")
.expect("Find failed")
.expect("File not found");
assert_eq!(entry.size(), content.len());
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let mut buf = vec![0u8; content.len()];
let mut total = 0;
while total < buf.len() {
let n = reader.read(&mut buf[total..]).expect("Read failed");
if n == 0 {
break;
}
total += n;
}
assert_eq!(&buf[..total], content);
}
#[test]
fn test_fat16_write_file_multiple_clusters() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "BIG.DAT")
.expect("Failed to create file");
let cluster_sz = cluster_size();
let content: Vec<u8> = (0..cluster_sz * 3).map(|i| (i % 256) as u8).collect();
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(&content).expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("BIG.DAT")
.expect("Find failed")
.expect("File not found");
assert_eq!(entry.size(), content.len());
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let read_content = reader.read_to_vec().expect("Read failed");
assert_eq!(read_content, content);
}
#[test]
fn test_fat16_create_directory() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
let subdir = fs
.create_dir(&root, "SUBDIR")
.expect("Failed to create directory");
let entries: Vec<_> = subdir.entries().collect();
let names: Vec<_> = entries
.iter()
.filter_map(|e| e.as_ref().ok())
.map(|e| e.name().trim_end_matches(' ').to_string())
.collect();
assert!(
names.iter().any(|n| n.starts_with('.')),
"Directory should have . entry: {:?}",
names
);
assert!(
names.iter().any(|n| n.starts_with("..")),
"Directory should have .. entry: {:?}",
names
);
}
#[test]
fn test_fat16_create_file_in_subdirectory() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
let subdir = fs
.create_dir(&root, "MYDIR")
.expect("Failed to create directory");
let entry = fs
.create_file(&subdir, "FILE.TXT")
.expect("Failed to create file");
assert!(entry.is_file());
let found = subdir.find("FILE.TXT").expect("Find failed");
assert!(found.is_some());
let root = fs.root_dir();
let found_in_root = root.find("FILE.TXT").expect("Find failed");
assert!(found_in_root.is_none());
}
#[test]
fn test_fat16_delete_file() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "DELETE.ME")
.expect("Failed to create file");
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(b"temporary data").expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("DELETE.ME")
.expect("Find failed")
.expect("File not found");
fs.delete(&entry).expect("Failed to delete");
let root = fs.root_dir();
let found = root.find("DELETE.ME").expect("Find failed");
assert!(found.is_none(), "File should be deleted");
}
#[test]
fn test_fat16_create_multiple_files() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
for i in 0..10 {
let name = format!("FILE{}.TXT", i);
fs.create_file(&root, &name).expect("Failed to create file");
}
let root = fs.root_dir();
let entries: Vec<_> = root.entries().collect();
assert_eq!(entries.len(), 10, "Should have 10 files");
}
#[test]
fn test_fat16_file_already_exists() {
let image = create_fat16_image();
let fs = FatFs::open(image).expect("Failed to open FAT16 image");
let root = fs.root_dir();
fs.create_file(&root, "TEST.TXT")
.expect("Failed to create file");
let result = fs.create_file(&root, "TEST.TXT");
match result {
Err(FatError::AlreadyExists) => {}
_ => panic!("Expected AlreadyExists error"),
}
}
}
#[cfg(feature = "std")]
mod fat12_integration_tests {
use super::fat12_image::{cluster_size, create_fat12_image};
use hadris_fat::{FatError, FatFs, FatFsWriteExt, FatType};
#[test]
fn test_open_fat12_image() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
assert!(
matches!(fs.fat_type(), FatType::Fat12),
"Expected FAT12, got {:?}",
fs.fat_type()
);
let root = fs.root_dir();
let entries: Vec<_> = root.entries().collect();
assert!(entries.is_empty(), "Root directory should be empty");
}
#[test]
fn test_fat12_create_file() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "TEST.TXT")
.expect("Failed to create file");
assert!(entry.is_file());
assert_eq!(entry.size(), 0);
let root = fs.root_dir();
let found = root.find("TEST.TXT").expect("Find failed");
assert!(found.is_some(), "File should be found in directory");
}
#[test]
fn test_fat12_write_file_content() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "HELLO.TXT")
.expect("Failed to create file");
let content = b"Hello, FAT12 World!";
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(content).expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("HELLO.TXT")
.expect("Find failed")
.expect("File not found");
assert_eq!(entry.size(), content.len());
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let mut buf = vec![0u8; content.len()];
let mut total = 0;
while total < buf.len() {
let n = reader.read(&mut buf[total..]).expect("Read failed");
if n == 0 {
break;
}
total += n;
}
assert_eq!(&buf[..total], content);
}
#[test]
fn test_fat12_write_file_multiple_clusters() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "BIG.DAT")
.expect("Failed to create file");
let cluster_sz = cluster_size();
let content: Vec<u8> = (0..cluster_sz * 3).map(|i| (i % 256) as u8).collect();
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(&content).expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("BIG.DAT")
.expect("Find failed")
.expect("File not found");
assert_eq!(entry.size(), content.len());
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let read_content = reader.read_to_vec().expect("Read failed");
assert_eq!(read_content, content);
}
#[test]
fn test_fat12_create_directory() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
let subdir = fs
.create_dir(&root, "SUBDIR")
.expect("Failed to create directory");
let entries: Vec<_> = subdir.entries().collect();
let names: Vec<_> = entries
.iter()
.filter_map(|e| e.as_ref().ok())
.map(|e| e.name().trim_end_matches(' ').to_string())
.collect();
assert!(
names.iter().any(|n| n.starts_with('.')),
"Directory should have . entry: {:?}",
names
);
assert!(
names.iter().any(|n| n.starts_with("..")),
"Directory should have .. entry: {:?}",
names
);
}
#[test]
fn test_fat12_create_file_in_subdirectory() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
let subdir = fs
.create_dir(&root, "MYDIR")
.expect("Failed to create directory");
let entry = fs
.create_file(&subdir, "FILE.TXT")
.expect("Failed to create file");
assert!(entry.is_file());
let found = subdir.find("FILE.TXT").expect("Find failed");
assert!(found.is_some());
let root = fs.root_dir();
let found_in_root = root.find("FILE.TXT").expect("Find failed");
assert!(found_in_root.is_none());
}
#[test]
fn test_fat12_delete_file() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
let entry = fs
.create_file(&root, "DELETE.ME")
.expect("Failed to create file");
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(b"temporary data").expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("DELETE.ME")
.expect("Find failed")
.expect("File not found");
fs.delete(&entry).expect("Failed to delete");
let root = fs.root_dir();
let found = root.find("DELETE.ME").expect("Find failed");
assert!(found.is_none(), "File should be deleted");
}
#[test]
fn test_fat12_create_multiple_files() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
for i in 0..10 {
let name = format!("FILE{}.TXT", i);
fs.create_file(&root, &name).expect("Failed to create file");
}
let root = fs.root_dir();
let entries: Vec<_> = root.entries().collect();
assert_eq!(entries.len(), 10, "Should have 10 files");
}
#[test]
fn test_fat12_file_already_exists() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
fs.create_file(&root, "TEST.TXT")
.expect("Failed to create file");
let result = fs.create_file(&root, "TEST.TXT");
match result {
Err(FatError::AlreadyExists) => {}
_ => panic!("Expected AlreadyExists error"),
}
}
#[test]
fn test_fat12_cluster_chain() {
let image = create_fat12_image();
let fs = FatFs::open(image).expect("Failed to open FAT12 image");
let root = fs.root_dir();
let cluster_sz = cluster_size();
let content: Vec<u8> = (0..cluster_sz * 5).map(|i| (i % 256) as u8).collect();
let entry = fs
.create_file(&root, "CHAIN.DAT")
.expect("Failed to create file");
{
let mut writer = fs.write_file(&entry).expect("Failed to get writer");
writer.write(&content).expect("Failed to write");
writer.finish().expect("Failed to finish");
}
let root = fs.root_dir();
let entry = root
.find("CHAIN.DAT")
.expect("Find failed")
.expect("File not found");
assert_eq!(entry.size(), content.len());
use hadris_fat::FatFsReadExt;
let mut reader = fs.read_file(&entry).expect("Failed to get reader");
let read_content = reader.read_to_vec().expect("Read failed");
assert_eq!(read_content, content, "FAT12 cluster chain read mismatch");
}
}