// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![deny(rust_2018_idioms)] use camino::Utf8Path; use camino_tempfile::{Builder, Utf8TempDir}; use std::{env, fs, path::Path, sync::mpsc::channel, thread}; macro_rules! t { ($e:expr) => { match $e { Ok(n) => n, Err(e) => panic!("error: {}", e), } }; } trait Utf8PathExt { fn exists(&self) -> bool; fn is_dir(&self) -> bool; } impl Utf8PathExt for Utf8Path { fn exists(&self) -> bool { fs::metadata(self).is_ok() } fn is_dir(&self) -> bool { fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) } } fn test_tempdir() { let path = { let p = t!(Builder::new() .prefix("foobar") .tempdir_in(Utf8Path::new("."))); let p = p.path(); assert!(p.as_str().contains("foobar")); p.to_path_buf() }; assert!(!path.exists()); } #[test] fn test_customnamed() { let tmpfile = Builder::new() .prefix("prefix") .suffix("suffix") .rand_bytes(12) .tempdir() .unwrap(); let name = tmpfile.path().file_name().unwrap(); assert!(name.starts_with("prefix")); assert!(name.ends_with("suffix")); assert_eq!(name.len(), 24); } fn test_rm_tempdir() { let (tx, rx) = channel(); let f = move || { let tmp = t!(Utf8TempDir::new()); tx.send(tmp.path().to_path_buf()).unwrap(); panic!("panic to unwind past `tmp`"); }; let _ = thread::spawn(f).join(); let path = rx.recv().unwrap(); assert!(!path.exists()); let tmp = t!(Utf8TempDir::new()); let path = tmp.path().to_path_buf(); let f = move || { let _tmp = tmp; panic!("panic to unwind past `tmp`"); }; let _ = thread::spawn(f).join(); assert!(!path.exists()); let path; { let f = move || t!(Utf8TempDir::new()); let tmp = thread::spawn(f).join().unwrap(); path = tmp.path().to_path_buf(); assert!(path.exists()); } assert!(!path.exists()); let path; { let tmp = t!(Utf8TempDir::new()); path = tmp.into_path(); } assert!(path.exists()); t!(fs::remove_dir_all(&path)); assert!(!path.exists()); } fn test_rm_tempdir_close() { let (tx, rx) = channel(); let f = move || { let tmp = t!(Utf8TempDir::new()); tx.send(tmp.path().to_path_buf()).unwrap(); t!(tmp.close()); panic!("panic when unwinding past `tmp`"); }; let _ = thread::spawn(f).join(); let path = rx.recv().unwrap(); assert!(!path.exists()); let tmp = t!(Utf8TempDir::new()); let path = tmp.path().to_path_buf(); let f = move || { let tmp = tmp; t!(tmp.close()); panic!("panic when unwinding past `tmp`"); }; let _ = thread::spawn(f).join(); assert!(!path.exists()); let path; { let f = move || t!(Utf8TempDir::new()); let tmp = thread::spawn(f).join().unwrap(); path = tmp.path().to_path_buf(); assert!(path.exists()); t!(tmp.close()); } assert!(!path.exists()); let path; { let tmp = t!(Utf8TempDir::new()); path = tmp.into_path(); } assert!(path.exists()); t!(fs::remove_dir_all(&path)); assert!(!path.exists()); } // Ideally these would be in std::os but then core would need // to depend on std fn recursive_mkdir_rel() { let path = Path::new("frob"); let cwd = env::current_dir().unwrap(); println!( "recursive_mkdir_rel: Making: {} in cwd {} [{}]", path.display(), cwd.display(), path.exists() ); t!(fs::create_dir(path)); assert!(path.is_dir()); t!(fs::create_dir_all(path)); assert!(path.is_dir()); } fn recursive_mkdir_dot() { let dot = Path::new("."); t!(fs::create_dir_all(dot)); let dotdot = Path::new(".."); t!(fs::create_dir_all(dotdot)); } fn recursive_mkdir_rel_2() { let path = Path::new("./frob/baz"); let cwd = env::current_dir().unwrap(); println!( "recursive_mkdir_rel_2: Making: {} in cwd {} [{}]", path.display(), cwd.display(), path.exists() ); t!(fs::create_dir_all(path)); assert!(path.is_dir()); assert!(path.parent().unwrap().is_dir()); let path2 = Path::new("quux/blat"); println!( "recursive_mkdir_rel_2: Making: {} in cwd {}", path2.display(), cwd.display() ); t!(fs::create_dir("quux")); t!(fs::create_dir_all(path2)); assert!(path2.is_dir()); assert!(path2.parent().unwrap().is_dir()); } // Ideally this would be in core, but needs TempFile pub fn test_remove_dir_all_ok() { let tmpdir = t!(Utf8TempDir::new()); let tmpdir = tmpdir.path(); let root = tmpdir.join("foo"); println!("making {}", root); t!(fs::create_dir(&root)); t!(fs::create_dir(root.join("foo"))); t!(fs::create_dir(root.join("foo").join("bar"))); t!(fs::create_dir(root.join("foo").join("bar").join("blat"))); t!(fs::remove_dir_all(&root)); assert!(!root.exists()); assert!(!root.join("bar").exists()); assert!(!root.join("bar").join("blat").exists()); } pub fn dont_double_panic() { let r: Result<(), _> = thread::spawn(move || { let tmpdir = Utf8TempDir::new().unwrap(); // Remove the temporary directory so that TempDir sees // an error on drop t!(fs::remove_dir(tmpdir.path())); // Panic. If TempDir panics *again* due to the rmdir // error then the process will abort. panic!(); }) .join(); assert!(r.is_err()); } fn in_tmpdir(f: F) where F: FnOnce(), { let tmpdir = t!(Utf8TempDir::new()); assert!(env::set_current_dir(tmpdir.path()).is_ok()); f(); } pub fn pass_as_asref_path() { let tempdir = t!(Utf8TempDir::new()); takes_asref_path(&tempdir); fn takes_asref_path>(path: T) { let path = path.as_ref(); assert!(path.exists()); } } #[test] fn main() { in_tmpdir(test_tempdir); in_tmpdir(test_rm_tempdir); in_tmpdir(test_rm_tempdir_close); in_tmpdir(recursive_mkdir_rel); in_tmpdir(recursive_mkdir_dot); in_tmpdir(recursive_mkdir_rel_2); in_tmpdir(test_remove_dir_all_ok); in_tmpdir(dont_double_panic); in_tmpdir(pass_as_asref_path); }