temp-dir 0.1.11

Simple temporary directory with cleanup
Documentation
use crate::{TempDir, COUNTER};
use core::sync::atomic::Ordering;
use safe_lock::SafeLock;
use std::io::ErrorKind;
use std::path::Path;

// The error tests require all tests to run single-threaded.
static LOCK: SafeLock = SafeLock::new();

fn make_non_writable(path: &Path) {
    assert!(std::process::Command::new("chmod")
        .arg("-w")
        .arg(path)
        .status()
        .unwrap()
        .success());
}

fn make_writable(path: &Path) {
    assert!(std::process::Command::new("chmod")
        .arg("u+w")
        .arg(path)
        .status()
        .unwrap()
        .success());
}

fn should_skip_cleanup_test() -> bool {
    // On Gitlab's shared CI runners, the cleanup always succeeds and the
    // test fails.  So we skip these tests when it's running on Gitlab CI.
    if std::env::current_dir().unwrap().starts_with("/builds/") {
        println!("Running on Gitlab CI.  Skipping test.");
        return true;
    }
    false
}

#[test]
fn new() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    println!("{:?}", temp_dir);
    println!("{:?}", TempDir::new().unwrap());
    let metadata = std::fs::metadata(temp_dir.path()).unwrap();
    assert!(metadata.is_dir());
    let temp_dir2 = TempDir::new().unwrap();
    assert_ne!(temp_dir.path(), temp_dir2.path());
}

#[test]
fn new_error() {
    let _guard = LOCK.lock();
    let previous_counter_value = COUNTER.load(Ordering::SeqCst);
    let temp_dir = TempDir::new().unwrap();
    let dir_path = temp_dir.path().to_path_buf();
    COUNTER.store(previous_counter_value, Ordering::SeqCst);
    let e = TempDir::new().unwrap_err();
    assert_eq!(std::io::ErrorKind::AlreadyExists, e.kind());
    assert!(
        e.to_string()
            .starts_with(&format!("error creating directory {:?}: ", dir_path)),
        "unexpected error {:?}",
        e
    );
}

#[test]
fn with_prefix() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::with_prefix("prefix1").unwrap();
    let name = temp_dir.path().file_name().unwrap();
    assert!(
        name.to_str().unwrap().starts_with("prefix1"),
        "{:?}",
        temp_dir
    );
    let metadata = std::fs::metadata(temp_dir.path()).unwrap();
    assert!(metadata.is_dir());
    let temp_dir2 = TempDir::new().unwrap();
    assert_ne!(temp_dir.path(), temp_dir2.path());
}

#[test]
fn with_prefix_error() {
    let _guard = LOCK.lock();
    let previous_counter_value = COUNTER.load(Ordering::SeqCst);
    let temp_dir = TempDir::with_prefix("prefix1").unwrap();
    COUNTER.store(previous_counter_value, Ordering::SeqCst);
    let e = TempDir::with_prefix("prefix1").unwrap_err();
    assert_eq!(std::io::ErrorKind::AlreadyExists, e.kind());
    assert!(
        e.to_string()
            .starts_with(&format!("error creating directory {:?}: ", temp_dir.path())),
        "unexpected error {:?}",
        e
    );
}

#[test]
fn child() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    let file1_path = temp_dir.child("file1");
    assert!(
        file1_path.ends_with("file1"),
        "{:?}",
        file1_path.to_string_lossy()
    );
    assert!(
        file1_path.starts_with(temp_dir.path()),
        "{:?}",
        file1_path.to_string_lossy()
    );
    std::fs::write(&file1_path, b"abc").unwrap();
}

#[test]
fn cleanup() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    std::fs::write(&temp_dir.child("file1"), b"abc").unwrap();
    let dir_path = temp_dir.path().to_path_buf();
    std::fs::metadata(&dir_path).unwrap();
    temp_dir.cleanup().unwrap();
    assert_eq!(
        ErrorKind::NotFound,
        std::fs::metadata(&dir_path).unwrap_err().kind()
    );
}

#[test]
fn cleanup_already_deleted() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    std::fs::remove_dir_all(temp_dir.path()).unwrap();
    temp_dir.cleanup().unwrap();
}

#[cfg(unix)]
#[test]
fn cleanup_error() {
    if should_skip_cleanup_test() {
        return;
    }
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    let dir_path = temp_dir.path().to_path_buf();
    let file1_path = temp_dir.child("file1");
    std::fs::write(&file1_path, b"abc").unwrap();
    make_non_writable(&dir_path);
    let result = temp_dir.cleanup();
    std::fs::metadata(&dir_path).unwrap();
    std::fs::metadata(&file1_path).unwrap();
    make_writable(&dir_path);
    std::fs::remove_dir_all(&dir_path).unwrap();
    let e = result.unwrap_err();
    assert_eq!(std::io::ErrorKind::PermissionDenied, e.kind());
    assert!(
        e.to_string().starts_with(&format!(
            "error removing directory and contents {:?}: ",
            dir_path
        )),
        "unexpected error {:?}",
        e
    );
}

#[test]
fn test_drop() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    let dir_path = temp_dir.path().to_path_buf();
    let file1_path = temp_dir.child("file1");
    std::fs::write(&file1_path, b"abc").unwrap();
    TempDir::new().unwrap();
    std::fs::metadata(&dir_path).unwrap();
    std::fs::metadata(&file1_path).unwrap();
    drop(temp_dir);
    assert_eq!(
        ErrorKind::NotFound,
        std::fs::metadata(&dir_path).unwrap_err().kind()
    );
    assert_eq!(
        ErrorKind::NotFound,
        std::fs::metadata(&file1_path).unwrap_err().kind()
    );
}

#[test]
fn drop_already_deleted() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    std::fs::remove_dir(temp_dir.path()).unwrap();
}

#[cfg(unix)]
#[test]
fn drop_error_ignored() {
    if should_skip_cleanup_test() {
        return;
    }
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    let dir_path = temp_dir.path().to_path_buf();
    let file1_path = temp_dir.child("file1");
    std::fs::write(&file1_path, b"abc").unwrap();
    make_non_writable(&dir_path);
    drop(temp_dir);
    std::fs::metadata(&dir_path).unwrap();
    std::fs::metadata(&file1_path).unwrap();
    make_writable(&dir_path);
    std::fs::remove_dir_all(&dir_path).unwrap();
}

#[cfg(unix)]
#[test]
fn drop_error_panic() {
    if should_skip_cleanup_test() {
        return;
    }
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap().panic_on_cleanup_error();
    let dir_path = temp_dir.path().to_path_buf();
    let file1_path = temp_dir.child("file1");
    std::fs::write(&file1_path, b"abc").unwrap();
    make_non_writable(&dir_path);
    let result = std::panic::catch_unwind(move || drop(temp_dir));
    std::fs::metadata(&dir_path).unwrap();
    std::fs::metadata(&file1_path).unwrap();
    make_writable(&dir_path);
    std::fs::remove_dir_all(&dir_path).unwrap();
    let msg = result.unwrap_err().downcast::<String>().unwrap();
    assert!(
        msg.contains("error removing directory and contents ",),
        "unexpected panic message {:?}",
        msg
    );
}

#[test]
fn leak() {
    let _guard = LOCK.lock();
    let temp_dir = TempDir::new().unwrap();
    let dir_path = temp_dir.path().to_path_buf();
    let file1_path = temp_dir.child("file1");
    std::fs::write(&file1_path, b"abc").unwrap();
    temp_dir.leak();
    std::fs::metadata(&dir_path).unwrap();
    std::fs::metadata(&file1_path).unwrap();
    std::fs::remove_dir_all(&dir_path).unwrap();
}