1 //! This file contains tests for the `stracciatella::vfs` module. 2 3 // Vfs tests. 4 mod vfs { 5 #[test] read()6 fn read() { 7 let (temp, dir, dir_fs) = create_temp_dir(); 8 create_data_slf(&dir); // data.slf 9 10 let mut vfs = Vfs::new(); 11 add_slf(&mut vfs, &dir_fs, "data.slf"); 12 let data = read_file_data(&vfs, "foo.txt"); 13 assert_eq!(&data, b"data.slf"); 14 15 temp.close().expect("close temp dir"); 16 } 17 18 #[test] seek()19 fn seek() { 20 let (temp, dir, dir_fs) = create_temp_dir(); 21 create_data_slf(&dir); // data.slf 22 23 let mut vfs = Vfs::new(); 24 add_slf(&mut vfs, &dir_fs, "data.slf"); 25 let mut file = vfs.open(&Nfc::caseless_path("foo.txt")).unwrap(); 26 27 assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 8); 28 assert_eq!(file.seek(SeekFrom::Current(4)).unwrap(), 12); 29 assert_eq!(file.seek(SeekFrom::Current(-8)).unwrap(), 4); 30 assert!(file.seek(SeekFrom::Current(-5)).is_err()); 31 assert_eq!(file.seek(SeekFrom::Start(0)).unwrap(), 0); 32 33 temp.close().expect("close temp dir"); 34 } 35 36 #[test] order()37 fn order() { 38 let (temp, dir, dir_fs) = create_temp_dir(); 39 create_foo_slf(&dir); // foo.slf 40 create_foobar_slf(&dir); // foobar.slf 41 create_file(&dir.join("foo/bar/baz.txt")); // baz.txt 42 43 let mut vfs = Vfs::new(); 44 vfs.add_dir(&dir).expect("dir"); 45 add_slf(&mut vfs, &dir_fs, "foo.slf"); 46 add_slf(&mut vfs, &dir_fs, "foobar.slf"); 47 48 let data = read_file_data(&vfs, "foo/bar/baz.txt"); 49 assert_eq!(&data, b"baz.txt"); 50 51 temp.close().expect("close temp dir"); 52 } 53 54 #[test] paths()55 fn paths() { 56 let (temp, dir, dir_fs) = create_temp_dir(); 57 create_foo_slf(&dir); // foo.slf 58 59 let mut vfs = Vfs::new(); 60 add_slf(&mut vfs, &dir_fs, "foo.slf"); 61 // case insensitive 62 let data = read_file_data(&vfs, "FOO/bar.txt"); 63 assert_eq!(&data, b"foo.slf"); 64 let data = read_file_data(&vfs, "foo/BAR.TXT"); 65 assert_eq!(&data, b"foo.slf"); 66 let data = read_file_data(&vfs, "foo/BAR/baz.txt"); 67 assert_eq!(&data, b"foo.slf"); 68 let data = read_file_data(&vfs, "foo/bar/BAZ.TXT"); 69 assert_eq!(&data, b"foo.slf"); 70 let data = read_file_data(&vfs, "foo\\bar/ὈΔΥΣΣΕΎΣ.baz"); 71 assert_eq!(&data, b"foo.slf"); 72 // separators 73 let data = read_file_data(&vfs, "foo/bar/baz.txt"); 74 assert_eq!(&data, b"foo.slf"); 75 let data = read_file_data(&vfs, "foo/bar\\baz.txt"); 76 assert_eq!(&data, b"foo.slf"); 77 let data = read_file_data(&vfs, "foo\\bar\\baz.txt"); 78 assert_eq!(&data, b"foo.slf"); 79 let data = read_file_data(&vfs, "foo\\bar/baz.txt"); 80 assert_eq!(&data, b"foo.slf"); 81 let data = read_file_data(&vfs, "foo\\bar/ὀδυσσεύς.baz"); 82 assert_eq!(&data, b"foo.slf"); 83 84 temp.close().expect("close temp dir"); 85 } 86 87 #[test] read_dir()88 fn read_dir() { 89 let (temp, dir, dir_fs) = create_temp_dir(); 90 create_foo_slf(&dir); // foo.slf 91 create_foobar_slf(&dir); // foobar.slf 92 create_file(&dir.join("foo/foo1.txt")); 93 create_file(&dir.join("foo/Foo2.txt")); 94 // Non-ascii paths 95 create_file(&dir.join("foo/ὈΔΥΣΣΕΎΣ.txt")); 96 create_file(&dir.join("foo/ХЦЧ")); 97 create_file(&dir.join("foo/农历新年.txt")); 98 99 let mut vfs = Vfs::new(); 100 vfs.add_dir(&dir).expect("dir"); 101 add_slf(&mut vfs, &dir_fs, "foo.slf"); 102 add_slf(&mut vfs, &dir_fs, "foobar.slf"); 103 104 let root_paths = ["foo", "foo.slf", "foobar.slf"]; 105 let foo_paths = [ 106 "农历新年.txt", 107 "хцч", 108 "ὀδυσσεύς.txt", 109 "foo2.txt", 110 "foo1.txt", 111 "bar.txt", 112 "bar", 113 ]; 114 let foo_bar_paths = ["baz.txt", "ὀδυσσεύς.baz"]; 115 // case insensitive 116 assert_vfs_read_dir(&vfs, "foo", &foo_paths); 117 assert_vfs_read_dir(&vfs, "FOO", &foo_paths); 118 assert_vfs_read_dir(&vfs, "foo/bar", &foo_bar_paths); 119 assert_vfs_read_dir(&vfs, "FOO/Bar", &foo_bar_paths); 120 121 // trailing path separators should not matter 122 assert_vfs_read_dir(&vfs, "foo/", &foo_paths); 123 assert_vfs_read_dir(&vfs, "FOO/", &foo_paths); 124 assert_vfs_read_dir(&vfs, "foo/bar/", &foo_bar_paths); 125 assert_vfs_read_dir(&vfs, "FOO/Bar/", &foo_bar_paths); 126 127 // the kind of path separator should not matter 128 assert_vfs_read_dir(&vfs, "foo\\bar", &foo_bar_paths); 129 assert_vfs_read_dir(&vfs, "FOO\\Bar", &foo_bar_paths); 130 assert_vfs_read_dir(&vfs, "foo/bar\\", &foo_bar_paths); 131 assert_vfs_read_dir(&vfs, "FOO/Bar\\", &foo_bar_paths); 132 133 // it should be possible to list root paths 134 assert_vfs_read_dir(&vfs, "", &root_paths); 135 assert_vfs_read_dir(&vfs, "/", &root_paths); 136 137 let mut vfs = Vfs::new(); 138 add_slf(&mut vfs, &dir_fs, "foobar.slf"); 139 140 // it should be possible to list root paths in vfs just consisting of slf 141 let root_paths = ["foo"]; 142 assert_vfs_read_dir(&vfs, "", &root_paths); 143 assert_vfs_read_dir(&vfs, "/", &root_paths); 144 145 // it should be possible to list paths that only exist in an slf prefix 146 let foo_paths = ["bar"]; 147 assert_vfs_read_dir(&vfs, "foo", &foo_paths); 148 assert_vfs_read_dir(&vfs, "foo/", &foo_paths); 149 150 temp.close().expect("close temp dir"); 151 } 152 153 // end of vfs tests 154 //------------------ 155 156 use std::collections::HashSet; 157 use std::io::{Read, Seek, SeekFrom, Write}; 158 use std::iter::FromIterator; 159 use std::path::{Path, PathBuf}; 160 use std::rc::Rc; 161 162 use stracciatella::file_formats::slf::{SlfEntry, SlfEntryState, SlfHeader}; 163 use stracciatella::fs; 164 use stracciatella::fs::{OpenOptions, TempDir}; 165 use stracciatella::unicode::Nfc; 166 use stracciatella::vfs::dir::DirFs; 167 use stracciatella::vfs::{Vfs, VfsLayer}; 168 169 fn read_file_data(vfs: &Vfs, path: &str) -> Vec<u8> { 170 let mut file = vfs.open(&Nfc::caseless_path(path)).expect("open"); 171 let mut data = Vec::new(); 172 file.read_to_end(&mut data).expect("Vec<u8>"); 173 data 174 } 175 176 fn assert_vfs_read_dir(vfs: &Vfs, path: &str, expected: &[&str]) { 177 let result = vfs.read_dir(&Nfc::caseless_path(path)).expect("read_dir"); 178 let expected = HashSet::from_iter(expected.iter().map(|s| Nfc::caseless_path(s))); 179 assert_eq!(result, expected); 180 } 181 182 /// The inner file data is the name. 183 fn create_slf(dir: &Path, name: &str, library_path: &str, entry_paths: &[&str]) -> PathBuf { 184 let header = SlfHeader { 185 library_name: name.to_owned(), 186 library_path: library_path.to_owned(), 187 num_entries: entry_paths.len() as i32, 188 ok_entries: entry_paths.len() as i32, 189 sort: 0xFFFF, 190 version: 0x200, 191 contains_subdirectories: if library_path.is_empty() { 0 } else { 1 }, 192 }; 193 let path = dir.join(&name); 194 let mut file = OpenOptions::new() 195 .write(true) 196 .create_new(true) 197 .open(&path) 198 .expect("open new file for writing"); 199 header.to_output(&mut file).expect("write header"); 200 let mut entries = entry_paths 201 .iter() 202 .map(|&entry_path| { 203 let offset = file.seek(SeekFrom::Current(0)).expect("seek to entry data"); 204 let data = name.as_bytes(); 205 file.write_all(&data).expect("write entry data"); 206 SlfEntry { 207 file_path: entry_path.to_owned(), 208 offset: offset as u32, 209 length: data.len() as u32, 210 state: SlfEntryState::Ok, 211 file_time: 0, 212 } 213 }) 214 .collect::<Vec<SlfEntry>>(); 215 entries.sort_by(|a, b| a.file_path.cmp(&b.file_path)); 216 header 217 .entries_to_output(&mut file, &entries) 218 .expect("write entries"); 219 file.sync_all().expect("sync_all"); 220 path 221 } 222 223 /// The file data is the same as the filename. 224 fn create_file(path: &Path) { 225 let dir = path.parent().expect("parent path"); 226 let name = path 227 .file_name() 228 .expect("file name") 229 .to_str() 230 .expect("file name string"); 231 if !dir.exists() { 232 fs::create_dir_all(&dir).expect("create_dir_all"); 233 } 234 fs::write(&dir.join(&name), name.as_bytes()).expect("write"); 235 } 236 237 /// The inner file data is "foobar.slf". 238 fn create_foobar_slf(dir: &Path) { 239 create_slf(&dir, "foobar.slf", r"foo\bar\", &["baz.txt"]); 240 } 241 242 /// The inner file data is "foo.slf". 243 fn create_foo_slf(dir: &Path) { 244 create_slf( 245 &dir, 246 "foo.slf", 247 "foo\\", 248 &["bar.txt", "bar\\baz.txt", "bar\\ὈΔΥΣΣΕΎΣ.baz"], 249 ); 250 } 251 252 /// The inner file data "data.slf". 253 fn create_data_slf(dir: &Path) { 254 create_slf(&dir, "data.slf", "", &["foo.txt"]); 255 } 256 257 /// Add an slf to vfs 258 fn add_slf(vfs: &mut Vfs, dir_fs: &Rc<dyn VfsLayer>, name: &str) { 259 vfs.add_slf(dir_fs.open(&name.into()).expect("DirFs::open")) 260 .expect("add_slf"); 261 } 262 263 /// The temporary dir and it's contents are removed when TempDir is closed or goes out of scope. 264 fn create_temp_dir() -> (TempDir, PathBuf, Rc<dyn VfsLayer>) { 265 let temp = TempDir::new().expect("TempDir"); 266 let dir = temp.path().to_owned(); 267 let dir_fs = DirFs::new(&dir).expect("DirFs"); 268 (temp, dir, dir_fs) 269 } 270 } 271