diff --git a/crates/nu-command/tests/commands/utouch.rs b/crates/nu-command/tests/commands/utouch.rs index 8665837149..79650c5503 100644 --- a/crates/nu-command/tests/commands/utouch.rs +++ b/crates/nu-command/tests/commands/utouch.rs @@ -1,7 +1,11 @@ use chrono::{DateTime, Local}; -use nu_test_support::fs::Stub; +use nu_test_support::fs::{files_exist_at, Stub}; use nu_test_support::nu; use nu_test_support::playground::Playground; +use std::path::Path; + +// Use 1 instead of 0 because 0 has a special meaning in Windows +const TIME_ONE: filetime::FileTime = filetime::FileTime::from_unix_time(1, 0); #[test] fn creates_a_file_when_it_doesnt_exist() { @@ -36,21 +40,29 @@ fn creates_two_files() { fn change_modified_time_of_file_to_today() { Playground::setup("change_time_test_9", |dirs, sandbox| { sandbox.with_files(&[Stub::EmptyFile("file.txt")]); + let path = dirs.test().join("file.txt"); + + // Set file.txt's times to the past before the test to make sure `utouch` actually changes the mtime to today + filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); nu!( cwd: dirs.test(), "utouch -m file.txt" ); - let path = dirs.test().join("file.txt"); + let metadata = path.metadata().unwrap(); // Check only the date since the time may not match exactly - let date = Local::now().date_naive(); - let actual_date_time: DateTime = - DateTime::from(path.metadata().unwrap().modified().unwrap()); - let actual_date = actual_date_time.date_naive(); + let today = Local::now().date_naive(); + let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); - assert_eq!(date, actual_date); + assert_eq!(today, mtime_day); + + // Check that atime remains unchanged + assert_eq!( + TIME_ONE, + filetime::FileTime::from_system_time(metadata.accessed().unwrap()) + ); }) } @@ -58,21 +70,29 @@ fn change_modified_time_of_file_to_today() { fn change_access_time_of_file_to_today() { Playground::setup("change_time_test_18", |dirs, sandbox| { sandbox.with_files(&[Stub::EmptyFile("file.txt")]); + let path = dirs.test().join("file.txt"); + + // Set file.txt's times to the past before the test to make sure `utouch` actually changes the atime to today + filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); nu!( cwd: dirs.test(), "utouch -a file.txt" ); - let path = dirs.test().join("file.txt"); + let metadata = path.metadata().unwrap(); // Check only the date since the time may not match exactly - let date = Local::now().date_naive(); - let actual_date_time: DateTime = - DateTime::from(path.metadata().unwrap().accessed().unwrap()); - let actual_date = actual_date_time.date_naive(); + let today = Local::now().date_naive(); + let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); - assert_eq!(date, actual_date); + assert_eq!(today, atime_day); + + // Check that mtime remains unchanged + assert_eq!( + TIME_ONE, + filetime::FileTime::from_system_time(metadata.modified().unwrap()) + ); }) } @@ -80,30 +100,31 @@ fn change_access_time_of_file_to_today() { fn change_modified_and_access_time_of_file_to_today() { Playground::setup("change_time_test_27", |dirs, sandbox| { sandbox.with_files(&[Stub::EmptyFile("file.txt")]); + let path = dirs.test().join("file.txt"); + + filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); nu!( cwd: dirs.test(), "utouch -a -m file.txt" ); - let metadata = dirs.test().join("file.txt").metadata().unwrap(); + let metadata = path.metadata().unwrap(); // Check only the date since the time may not match exactly - let date = Local::now().date_naive(); - let adate_time: DateTime = DateTime::from(metadata.accessed().unwrap()); - let adate = adate_time.date_naive(); - let mdate_time: DateTime = DateTime::from(metadata.modified().unwrap()); - let mdate = mdate_time.date_naive(); + let today = Local::now().date_naive(); + let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); + let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); - assert_eq!(date, adate); - assert_eq!(date, mdate); + assert_eq!(today, mtime_day); + assert_eq!(today, atime_day); }) } #[test] fn not_create_file_if_it_not_exists() { Playground::setup("change_time_test_28", |dirs, _sandbox| { - nu!( + let outcome = nu!( cwd: dirs.test(), "utouch -c file.txt" ); @@ -112,17 +133,39 @@ fn not_create_file_if_it_not_exists() { assert!(!path.exists()); - nu!( - cwd: dirs.test(), - "utouch -c file.txt" - ); - - let path = dirs.test().join("file.txt"); - - assert!(!path.exists()); + // If --no-create is improperly handled `utouch` may error when trying to change the times of a nonexistent file + assert!(outcome.status.success()) }) } +#[test] +fn change_file_times_if_exists_with_no_create() { + Playground::setup( + "change_file_times_if_exists_with_no_create", + |dirs, sandbox| { + sandbox.with_files(&[Stub::EmptyFile("file.txt")]); + let path = dirs.test().join("file.txt"); + + filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); + + nu!( + cwd: dirs.test(), + "utouch -c file.txt" + ); + + let metadata = path.metadata().unwrap(); + + // Check only the date since the time may not match exactly + let today = Local::now().date_naive(); + let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); + let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); + + assert_eq!(today, mtime_day); + assert_eq!(today, atime_day); + }, + ) +} + #[test] fn creates_file_three_dots() { Playground::setup("create_test_1", |dirs, _sandbox| { @@ -163,14 +206,325 @@ fn creates_file_four_dots_quotation_marks() { } #[test] -fn creates_with_date() { - Playground::setup("create_test_1", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "utouch 'file....'" +fn change_file_times_to_reference_file() { + Playground::setup("change_dir_times_to_reference_dir", |dirs, sandbox| { + sandbox.with_files(&[ + Stub::EmptyFile("reference_file"), + Stub::EmptyFile("target_file"), + ]); + + let reference = dirs.test().join("reference_file"); + let target = dirs.test().join("target_file"); + + // Change the times for reference + filetime::set_file_times( + &reference, + filetime::FileTime::from_unix_time(1337, 0), + TIME_ONE, + ) + .unwrap(); + + // target should have today's date since it was just created, but reference should be different + assert_ne!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_ne!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() ); - let path = dirs.test().join("file...."); + nu!( + cwd: dirs.test(), + "utouch -r reference_file target_file" + ); + + assert_eq!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_eq!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() + ); + }) +} + +#[test] +fn change_file_mtime_to_reference() { + Playground::setup("change_file_mtime_to_reference", |dirs, sandbox| { + sandbox.with_files(&[ + Stub::EmptyFile("reference_file"), + Stub::EmptyFile("target_file"), + ]); + + let reference = dirs.test().join("reference_file"); + let target = dirs.test().join("target_file"); + + // Change the times for reference + filetime::set_file_times( + &reference, + TIME_ONE, + filetime::FileTime::from_unix_time(1337, 0), + ) + .unwrap(); + + // target should have today's date since it was just created, but reference should be different + assert_ne!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_ne!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() + ); + + // Save target's current atime to make sure it is preserved + let target_original_atime = target.metadata().unwrap().accessed().unwrap(); + + nu!( + cwd: dirs.test(), + "utouch -mr reference_file target_file" + ); + + assert_eq!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() + ); + assert_ne!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_eq!( + target_original_atime, + target.metadata().unwrap().accessed().unwrap() + ); + }) +} + +#[test] +fn change_modified_time_of_dir_to_today() { + Playground::setup("change_dir_mtime", |dirs, sandbox| { + sandbox.mkdir("test_dir"); + let path = dirs.test().join("test_dir"); + + filetime::set_file_mtime(&path, TIME_ONE).unwrap(); + + nu!( + cwd: dirs.test(), + "utouch -m test_dir" + ); + + // Check only the date since the time may not match exactly + let today = Local::now().date_naive(); + let mtime_day = + DateTime::::from(path.metadata().unwrap().modified().unwrap()).date_naive(); + + assert_eq!(today, mtime_day); + }) +} + +#[test] +fn change_access_time_of_dir_to_today() { + Playground::setup("change_dir_atime", |dirs, sandbox| { + sandbox.mkdir("test_dir"); + let path = dirs.test().join("test_dir"); + + filetime::set_file_atime(&path, TIME_ONE).unwrap(); + + nu!( + cwd: dirs.test(), + "utouch -a test_dir" + ); + + // Check only the date since the time may not match exactly + let today = Local::now().date_naive(); + let atime_day = + DateTime::::from(path.metadata().unwrap().accessed().unwrap()).date_naive(); + + assert_eq!(today, atime_day); + }) +} + +#[test] +fn change_modified_and_access_time_of_dir_to_today() { + Playground::setup("change_dir_times", |dirs, sandbox| { + sandbox.mkdir("test_dir"); + let path = dirs.test().join("test_dir"); + + filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); + + nu!( + cwd: dirs.test(), + "utouch -a -m test_dir" + ); + + let metadata = path.metadata().unwrap(); + + // Check only the date since the time may not match exactly + let today = Local::now().date_naive(); + let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); + let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); + + assert_eq!(today, mtime_day); + assert_eq!(today, atime_day); + }) +} + +#[test] +fn change_dir_three_dots_times() { + Playground::setup("change_dir_three_dots_times", |dirs, sandbox| { + sandbox.mkdir("test_dir..."); + let path = dirs.test().join("test_dir..."); + + filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); + + nu!( + cwd: dirs.test(), + "utouch test_dir..." + ); + + let metadata = path.metadata().unwrap(); + + // Check only the date since the time may not match exactly + let today = Local::now().date_naive(); + let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); + let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); + + assert_eq!(today, mtime_day); + assert_eq!(today, atime_day); + }) +} + +#[test] +fn change_dir_times_to_reference_dir() { + Playground::setup("change_dir_times_to_reference_dir", |dirs, sandbox| { + sandbox.mkdir("reference_dir"); + sandbox.mkdir("target_dir"); + + let reference = dirs.test().join("reference_dir"); + let target = dirs.test().join("target_dir"); + + // Change the times for reference + filetime::set_file_times( + &reference, + filetime::FileTime::from_unix_time(1337, 0), + TIME_ONE, + ) + .unwrap(); + + // target should have today's date since it was just created, but reference should be different + assert_ne!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_ne!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() + ); + + nu!( + cwd: dirs.test(), + "utouch -r reference_dir target_dir" + ); + + assert_eq!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_eq!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() + ); + }) +} + +#[test] +fn change_dir_atime_to_reference() { + Playground::setup("change_dir_atime_to_reference", |dirs, sandbox| { + sandbox.mkdir("reference_dir"); + sandbox.mkdir("target_dir"); + + let reference = dirs.test().join("reference_dir"); + let target = dirs.test().join("target_dir"); + + // Change the times for reference + filetime::set_file_times( + &reference, + filetime::FileTime::from_unix_time(1337, 0), + TIME_ONE, + ) + .unwrap(); + + // target should have today's date since it was just created, but reference should be different + assert_ne!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_ne!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() + ); + + // Save target's current mtime to make sure it is preserved + let target_original_mtime = target.metadata().unwrap().modified().unwrap(); + + nu!( + cwd: dirs.test(), + "utouch -ar reference_dir target_dir" + ); + + assert_eq!( + reference.metadata().unwrap().accessed().unwrap(), + target.metadata().unwrap().accessed().unwrap() + ); + assert_ne!( + reference.metadata().unwrap().modified().unwrap(), + target.metadata().unwrap().modified().unwrap() + ); + assert_eq!( + target_original_mtime, + target.metadata().unwrap().modified().unwrap() + ); + }) +} + +#[test] +fn create_a_file_with_tilde() { + Playground::setup("utouch with tilde", |dirs, _| { + let actual = nu!(cwd: dirs.test(), "utouch '~tilde'"); + assert!(actual.err.is_empty()); + assert!(files_exist_at(vec![Path::new("~tilde")], dirs.test())); + + // pass variable + let actual = nu!(cwd: dirs.test(), "let f = '~tilde2'; utouch $f"); + assert!(actual.err.is_empty()); + assert!(files_exist_at(vec![Path::new("~tilde2")], dirs.test())); + }) +} + +#[test] +fn respects_cwd() { + Playground::setup("utouch_respects_cwd", |dirs, _sandbox| { + nu!( + cwd: dirs.test(), + "mkdir 'dir'; cd 'dir'; utouch 'i_will_be_created.txt'" + ); + + let path = dirs.test().join("dir/i_will_be_created.txt"); + assert!(path.exists()); + }) +} + +#[test] +fn reference_respects_cwd() { + Playground::setup("utouch_reference_respects_cwd", |dirs, _sandbox| { + nu!( + cwd: dirs.test(), + "mkdir 'dir'; cd 'dir'; utouch 'ref.txt'; utouch --reference 'ref.txt' 'foo.txt'" + ); + + let path = dirs.test().join("dir/foo.txt"); assert!(path.exists()); }) }