sd.rs
1 //! `describe("SD Card")` 2 //! 3 //! Tests the SD card filesystem through the `firmware::filesystems::sd` 4 //! infrastructure. Mirrors the C++ test suite in `hardware/storage.cpp`. 5 6 #![no_std] 7 #![no_main] 8 9 extern crate alloc; 10 11 #[path = "common/mod.rs"] 12 mod common; 13 14 use defmt::info; 15 use firmware::filesystems::sd; 16 17 use common::Device; 18 19 esp_bootloader_esp_idf::esp_app_desc!(); 20 21 #[cfg(test)] 22 #[embedded_test::setup] 23 fn setup() { 24 rtt_target::rtt_init_defmt!(); 25 } 26 27 #[cfg(test)] 28 #[embedded_test::tests(default_timeout = 15, executor = esp_rtos::embassy::Executor::new())] 29 mod tests { 30 use super::*; 31 32 #[init] 33 fn init() -> Device { 34 info!("=== SD Card — describe block ==="); 35 common::setup::boot_device() 36 } 37 38 /// `it("user observes that the SD card reports its size")` 39 #[test] 40 async fn user_observes_sd_card_reports_its_size(_device: Device) -> Result<(), &'static str> { 41 let snapshot = sd::snapshot(); 42 info!("SD card size={=u32} MiB", snapshot.sd_card_size_mb); 43 defmt::assert!(snapshot.sd_card_size_mb > 0, "SD card size is 0"); 44 Ok(()) 45 } 46 47 /// `it("user writes a file, reads it back, and deletes it")` 48 #[test] 49 async fn user_writes_reads_and_deletes_a_file(_device: Device) -> Result<(), &'static str> { 50 let name = "RT_TEST.BIN"; 51 let payload = b"sd-card round-trip test payload \xde\xad\xbe\xef\nline two\n"; 52 53 sd::write_file_at("", name, payload).map_err(|_| "write failed")?; 54 55 let readback = sd::read_file_at::<256>("", name).map_err(|_| "read failed")?; 56 defmt::assert_eq!(readback.as_slice(), payload.as_slice(), "content mismatch"); 57 58 sd::delete_file(name).map_err(|_| "delete failed")?; 59 60 let after_delete = sd::file_size(name); 61 defmt::assert!(after_delete.is_err(), "file still exists after delete"); 62 63 info!("write/read/delete roundtrip verified"); 64 Ok(()) 65 } 66 67 /// `it("user appends to a file without truncating")` 68 #[test] 69 async fn user_appends_to_a_file(_device: Device) -> Result<(), &'static str> { 70 let name = "APPEND.TMP"; 71 let _ = sd::delete_file(name); 72 73 sd::write_file_at("", name, b"hello").map_err(|_| "initial write failed")?; 74 75 sd::write_file_chunk(name, 5, b" world").map_err(|_| "append write failed")?; 76 77 let readback = sd::read_file_at::<256>("", name).map_err(|_| "read failed")?; 78 defmt::assert_eq!( 79 readback.as_slice(), 80 b"hello world", 81 "append content mismatch" 82 ); 83 84 sd::delete_file(name).map_err(|_| "cleanup delete failed")?; 85 86 info!("append mode verified"); 87 Ok(()) 88 } 89 90 /// `it("user lists a directory and finds created files")` 91 #[test] 92 async fn user_lists_directory_and_finds_files(_device: Device) -> Result<(), &'static str> { 93 sd::touch_at("", "LST_A.TMP").map_err(|_| "touch a failed")?; 94 sd::touch_at("", "LST_B.TMP").map_err(|_| "touch b failed")?; 95 96 let entries = sd::list_directory_at("/").map_err(|_| "list failed")?; 97 98 let found_a = entries.iter().any(|e| e.name.as_str() == "LST_A.TMP"); 99 let found_b = entries.iter().any(|e| e.name.as_str() == "LST_B.TMP"); 100 101 defmt::assert!(found_a, "LST_A.TMP not found in listing"); 102 defmt::assert!(found_b, "LST_B.TMP not found in listing"); 103 104 info!("directory listing found {=usize} entries", entries.len()); 105 106 sd::delete_file("LST_A.TMP").map_err(|_| "cleanup a failed")?; 107 sd::delete_file("LST_B.TMP").map_err(|_| "cleanup b failed")?; 108 109 Ok(()) 110 } 111 112 /// `it("user creates a directory and verifies it exists")` 113 #[test] 114 async fn user_creates_a_directory(_device: Device) -> Result<(), &'static str> { 115 let dir_name = "TSTDIR"; 116 if !sd::directory_exists(dir_name) { 117 sd::create_directory(dir_name).map_err(|_| "mkdir failed")?; 118 } 119 120 defmt::assert!( 121 sd::directory_exists(dir_name), 122 "directory not found after create" 123 ); 124 125 sd::delete_at("/", dir_name).map_err(|_| "rmdir failed")?; 126 127 info!("directory create/delete verified"); 128 Ok(()) 129 } 130 131 /// `it("user copies a file and reads the copy")` 132 #[test] 133 async fn user_copies_a_file(_device: Device) -> Result<(), &'static str> { 134 let payload = b"copy test data"; 135 sd::write_file_at("", "CP_SRC.TMP", payload).map_err(|_| "write src failed")?; 136 137 sd::copy_file("CP_SRC.TMP", "CP_DST.TMP").map_err(|_| "copy failed")?; 138 139 let readback = sd::read_file_at::<256>("", "CP_DST.TMP").map_err(|_| "read dst failed")?; 140 defmt::assert_eq!( 141 readback.as_slice(), 142 payload.as_slice(), 143 "copy content mismatch" 144 ); 145 146 let _ = sd::delete_file("CP_SRC.TMP"); 147 let _ = sd::delete_file("CP_DST.TMP"); 148 149 info!("file copy verified"); 150 Ok(()) 151 } 152 }