use assert2::let_assert; use kxio::fs; type TestResult = Result<(), fs::Error>; mod path { use super::*; mod is_link { use super::*; #[test] fn create_soft_link() -> TestResult { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("content").expect("write"); let link_path = fs.base().join("bar"); let link = fs.path(&link_path); file.soft_link(&link).expect("soft_link"); let exists = link.exists().expect("exists"); assert!(exists); let is_link = link.is_link().expect("is_link"); assert!(is_link); Ok(()) } #[test] fn dir_is_not_a_link() { let fs = fs::temp().expect("temp fs"); let dir_path = fs.base().join("foo"); let dir = fs.dir(&dir_path); dir.create().expect("create"); let is_link = dir.is_link().expect("is link"); assert!(!is_link); } #[test] fn file_is_not_a_link() { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("content").expect("write"); let is_link = file.is_link().expect("is link"); assert!(!is_link); } } mod set_permissions { use super::*; #[test] fn should_set_permissions() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("bar").expect("write"); let md = file.metadata().expect("metadata"); assert!(md.is_file()); let mut perms = md.permissions(); perms.set_readonly(true); let path = fs.path(&path); path.set_permissions(perms).expect("set_permissions"); let md = file.metadata().expect("metadata"); assert!(md.is_file()); assert!(md.permissions().readonly()); Ok(()) } } mod read_link { use super::*; #[test] fn should_read_link() -> TestResult { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("bar").expect("write"); let link_path = fs.base().join("bar"); let link = fs.path(&link_path); file.soft_link(&link).expect("soft_link"); let read_link = link.read_link().expect("read_link"); assert_eq!(read_link.as_pathbuf(), file_path); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.path(&path).read_link() ); Ok(()) } } mod metadata { use super::*; #[test] fn should_return_metadata() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("bar").expect("write"); let md = file.metadata().expect("metadata"); assert!(md.is_file()); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.file(&path).metadata() ); Ok(()) } } mod path_of { use super::*; #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let_assert!( Err(fs::Error::PathTraversal { base, path: _path }) = fs.path_of("..".into()) ); assert_eq!(base, fs.base()); Ok(()) } #[test] fn matches_joins() -> TestResult { let fs = fs::temp().expect("temp fs"); let joined = fs.base().join("foo").join("bar"); let path_of = fs .path_of("foo/bar".into()) .expect("parse foo/bar into path"); assert_eq!(joined, path_of); Ok(()) } } mod as_dir { use super::*; #[test] fn path_is_dir_as_dir_some() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let dir = fs.dir(&path); dir.create().expect("create"); let_assert!(Ok(Some(as_dir)) = fs.path(&path).as_dir()); assert_eq!(dir.as_pathbuf(), as_dir.as_pathbuf()); Ok(()) } #[test] fn path_is_file_as_dir_none() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("contents").expect("create"); let_assert!(Ok(Some(as_file)) = fs.path(&path).as_file()); assert_eq!(file.as_pathbuf(), as_file.as_pathbuf()); assert_eq!(as_file.reader().expect("reader").to_string(), "contents"); Ok(()) } } mod as_file { use super::*; #[test] fn path_is_dir_as_file_none() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let dir = fs.dir(&path); dir.create().expect("create"); let_assert!(Ok(None) = fs.path(&path).as_file()); Ok(()) } #[test] fn path_is_file_as_file_some() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("contents").expect("create"); let_assert!(Ok(None) = fs.path(&path).as_dir()); Ok(()) } } mod is_dir { use super::*; #[test] fn should_be_true_when_is_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); fs.dir(&path).create().expect("create"); let is_dir = fs.path(&path).is_dir().expect("is_dir"); assert!(is_dir); Ok(()) } #[test] fn should_be_false_when_is_a_file() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); fs.file(&path).write("bar").expect("write"); let is_dir = fs.path(&path).is_dir().expect("is_dir"); assert!(!is_dir); Ok(()) } #[test] #[ignore] fn should_be_false_when_is_a_link() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); // let_assert!(Ok(_) = fs.file_write(&path, "bar")); let is_dir = fs.path(&path).is_dir().expect("is_dir"); assert!(!is_dir); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.dir(&path).is_dir() ); Ok(()) } } mod exists { use super::*; #[test] fn should_be_true_when_it_exists() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); fs.file(&path).write("bar").expect("write"); let exists = fs.path(&path).exists().expect("exists"); assert!(exists); Ok(()) } #[test] fn should_be_false_when_it_does_not_exist() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let exists = fs.path(&path).exists().expect("exists"); assert!(!exists); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.path(&path).exists() ); Ok(()) } } mod is_file { use super::*; #[test] fn should_be_true_when_is_a_file() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); fs.file(&path).write("bar").expect("write"); let is_file = fs.path(&path).is_file().expect("is_file"); assert!(is_file); Ok(()) } #[test] fn should_be_false_when_is_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); fs.dir(&path).create().expect("create"); let is_file = fs.path(&path).is_file().expect("is_file"); assert!(!is_file); Ok(()) } #[test] #[ignore] fn should_be_false_when_is_a_link() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); // let_assert!(Ok(_) = fs.file_write(&path, "bar")); let is_file = fs.path(&path).is_file().expect("is_file"); assert!(!is_file); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.file(&path).is_file() ); Ok(()) } } mod rename { use super::*; #[test] fn should_rename_a_file() -> TestResult { let fs = fs::temp().expect("temp fs"); let src_path = fs.base().join("foo"); let src = fs.file(&src_path); src.write("bar").expect("write"); let src_contents = src.reader().expect("reader").to_string(); let dst_path = fs.base().join("bar"); let dst = fs.file(&dst_path); src.rename(&dst).expect("rename"); let dst_contents = dst.reader().expect("reader").to_string(); assert_eq!(src_contents, dst_contents); let src_exists = src.exists().expect("exists"); assert!(!src_exists); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let src_path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.file(&src_path).rename(&fs.file(&src_path)) ); Ok(()) } } mod from_pathbuf { use std::path::PathBuf; use super::*; #[test] fn should_convert_from_pathbuf() { let fs = fs::temp().expect("temp fs"); let src_pathbuf = fs.base().join("foo"); let dst_pathbuf: PathBuf = fs.path(&src_pathbuf).into(); assert_eq!(src_pathbuf, dst_pathbuf); } } } mod file { use super::*; // // test for reading the symlink metadata // #[test] // fn symlink_metadata() -> TestResult { // let fs = fs::temp().expect("temp fs"); // let file_path = fs.base().join("foo"); // let file = fs.file(&file_path); // file.write("bar").expect("write"); // let link_path = fs.base().join("bar"); // let link = fs.path(&link_path); // file.soft_link(&link).expect("soft_link"); // let md = link.symlink_metadata().expect("symlink metadata"); // assert!(md.is_file()); // Ok(()) // } #[test] fn create_hard_link() -> TestResult { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("content").expect("write"); let link_path = fs.base().join("bar"); let link = fs.file(&link_path); file.hard_link(&link).expect("hard_link"); let exists = link.exists().expect("exists"); assert!(exists); Ok(()) } #[test] /// Write to a file, read it, verify it exists, is a file and has the expected contents fn write_read_file_exists() -> TestResult { let fs = fs::temp().expect("temp fs"); let pathbuf = fs.base().join("foo"); let file = fs.file(&pathbuf); file.write("content").expect("write"); let c = file.reader().expect("reader").to_string(); assert_eq!(c, "content"); let path = fs.path(&pathbuf); let exists = path.exists().expect("exists"); assert!(exists); let is_file = path.is_file().expect("is_file"); assert!(is_file); Ok(()) } #[test] fn use_file_as_file() { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("contents").expect("write"); file.remove().expect("remove"); } #[test] fn use_dir_as_file() { let fs = fs::temp().expect("temp fs"); let dir_path = fs.base().join("foo"); let dir = fs.dir(&dir_path); dir.create().expect("create"); let file = fs.file(&dir_path); let_assert!(Err(fs::Error::NotAFile { path }) = file.remove()); assert_eq!(path, dir_path); } #[test] fn use_link_as_file() { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("contents").expect("write"); let link_path = fs.base().join("bar"); let link = fs.path(&link_path); file.soft_link(&link).expect("soft_link"); let path = fs.file(&link_path); let contents = path.reader().expect("reader").to_string(); assert_eq!(contents, "contents"); path.remove().expect("remove"); } mod remove { use super::*; #[test] fn should_remove_a_file() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("bar").expect("write"); file.remove().expect("remove"); let exists = file.exists().expect("exists"); assert!(!exists); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.file(&path).remove() ); Ok(()) } } mod copy { use super::*; #[test] fn should_copy_a_file() -> TestResult { let fs = fs::temp().expect("temp fs"); let src_path = fs.base().join("foo"); let src = fs.file(&src_path); src.write("bar").expect("write"); let dst_path = fs.base().join("bar"); let dst = fs.file(&dst_path); src.copy(&dst).expect("copy"); let src_contents = src.reader().expect("reader").to_string(); let dst_contents = dst.reader().expect("reader").to_string(); assert_eq!(src_contents, dst_contents); Ok(()) } } mod symlink_metadata { use super::*; #[test] fn should_return_metadata_for_a_file() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("bar").expect("write"); let md = file.symlink_metadata().expect("metadata"); assert!(md.is_file()); Ok(()) } #[test] fn should_return_metadata_for_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let dir = fs.dir(&path); dir.create().expect("create"); let md = dir.symlink_metadata().expect("metadata"); assert!(md.is_dir()); Ok(()) } #[test] fn should_return_metadata_for_a_symlink() -> TestResult { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("bar").expect("write"); let link_path = fs.base().join("bar"); let link = fs.path(&link_path); file.soft_link(&link).expect("soft_link"); let md = link.symlink_metadata().expect("metadata"); assert!(md.is_symlink()); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.file(&path).symlink_metadata() ); Ok(()) } } mod reader { use super::*; mod to_string { use super::*; use std::time::SystemTime; #[test] fn read_file_to_string() { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); let line3 = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("duration") .as_millis() .to_string(); let contents = format!("line 1\nline 2\n{line3}"); file.write(&contents).expect("write"); let reader = file.reader().expect("reader"); let string = reader.as_str(); assert_eq!(string, contents); } #[test] fn read_file_lines() { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); let line3 = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("duration") .as_millis() .to_string(); let contents = format!("line 1\nline 2\n{line3}"); file.write(&contents).expect("write"); let reader = file.reader().expect("reader"); let lines = reader.lines().collect::>(); assert_eq!(lines, vec!["line 1", "line 2", &line3]); } } mod lines { use super::*; #[test] fn read_file_lines() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("line 1\nline 2").expect("write"); let reader = file.reader().expect("reader"); let lines = reader.lines().collect::>(); assert_eq!(lines, vec!["line 1", "line 2"]); Ok(()) } } mod bytes { use super::*; #[test] fn should_return_bytes() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let file = fs.file(&path); file.write("bar").expect("write"); let reader = file.reader().expect("reader"); let bytes = reader.bytes(); assert_eq!(bytes.len(), 3); assert_eq!(bytes[0], b'b'); assert_eq!(bytes[1], b'a'); assert_eq!(bytes[2], b'r'); Ok(()) } } } } mod dir { use super::*; mod create { use super::*; #[test] fn should_create_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let pathbuf = fs.base().join("subdir"); fs.dir(&pathbuf).create().expect("create"); let exists = fs.path(&pathbuf).exists().expect("exitss"); assert!(exists); let is_dir = fs.path(&pathbuf).is_dir().expect("is dir"); assert!(is_dir); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.dir(&path).create() ); Ok(()) } } mod create_all { use super::*; #[test] fn should_create_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let pathbuf = fs.base().join("subdir").join("child"); fs.dir(&pathbuf).create_all().expect("create_all"); let path = fs.path(&pathbuf); let exists = path.exists().expect("exists"); assert!(exists, "path exists"); let is_dir = path.is_dir().expect("is_dir"); assert!(is_dir, "path is a directory"); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.dir(&path).create_all() ); Ok(()) } } mod read { use crate::fs::DirItem; use super::*; #[test] fn should_return_dir_items() -> TestResult { let fs = fs::temp().expect("temp fs"); let file1 = fs.base().join("file-1"); let dir = fs.base().join("dir"); let file2 = dir.join("file-2"); fs.file(&file1).write("file-1").expect("write: file-1"); fs.dir(&dir).create().expect("create dir"); fs.file(&file2).write("file-2").expect("write: file-2"); let items = fs .dir(fs.base()) .read() .expect("dir.read") .filter_map(|i| i.ok()) .collect::>(); assert_eq!(items.len(), 2); assert!(items.contains(&DirItem::File(file1))); assert!(items.contains(&DirItem::Dir(dir))); Ok(()) } #[test] fn should_fail_on_not_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("file"); fs.file(&path).write("contents").expect("write"); let_assert!(Err(fs::Error::NotADirectory { path: _path }) = fs.dir(&path).read()); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.dir(&path).read() ); Ok(()) } } mod remove { use super::*; #[test] fn should_remove_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); fs.dir(&path).create().expect("create"); fs.dir(&path).remove().expect("remove"); let exists = fs.path(&path).exists().expect("exists"); assert!(!exists); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.dir(&path).remove() ); Ok(()) } } mod remove_all { use super::*; #[test] fn should_remove_a_dir() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("foo"); let dir = fs.dir(&path); dir.create().expect("create dir"); let sub_path = path.join("sub"); let sub = fs.dir(&sub_path); sub.create().expect("create sub"); let file = sub_path.join("file"); fs.file(&file).write("contents").expect("write"); dir.remove_all().expect("remove"); let exists = dir.exists().expect("exists"); assert!(!exists); Ok(()) } #[test] fn should_fail_on_path_traversal() -> TestResult { let fs = fs::temp().expect("temp fs"); let path = fs.base().join("..").join("foo"); let_assert!( Err(fs::Error::PathTraversal { base: _base, path: _path }) = fs.dir(&path).remove_all() ); Ok(()) } } } mod canonicalize { use std::path::Path; use super::*; #[test] fn should_resolve_symlinks() -> TestResult { let fs = fs::temp().expect("temp fs"); let file_path = fs.base().join("foo"); let file = fs.file(&file_path); file.write("bar").expect("write"); let link_path = fs.base().join("link"); let link = fs.path(&link_path); file.soft_link(&link).expect("create"); let canonical = link.canonicalize().expect("canonicalize"); let canonical = if canonical.starts_with("/private") { // INFO: macos puts all temp files under /private Path::new("/").join(canonical.strip_prefix("/private").unwrap()) } else { canonical }; assert_eq!(canonical, file_path); Ok(()) } }