diff --git a/lib/src/backend.rs b/lib/src/backend.rs index 1ac02d558..be88f1c94 100644 --- a/lib/src/backend.rs +++ b/lib/src/backend.rs @@ -233,10 +233,15 @@ pub struct Timestamp { impl Timestamp { pub fn now() -> Self { - let now = chrono::offset::Local::now(); + Self::from_datetime(chrono::offset::Local::now()) + } + + pub fn from_datetime>( + datetime: chrono::DateTime, + ) -> Self { Self { - timestamp: MillisSinceEpoch(now.timestamp_millis() as u64), - tz_offset: now.offset().local_minus_utc() / 60, + timestamp: MillisSinceEpoch(datetime.timestamp_millis() as u64), + tz_offset: datetime.offset().local_minus_utc() / 60, } } } diff --git a/lib/src/commit_builder.rs b/lib/src/commit_builder.rs index 5859ec41a..def0b893a 100644 --- a/lib/src/commit_builder.rs +++ b/lib/src/commit_builder.rs @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::env; use std::sync::Arc; +use chrono::DateTime; use uuid::Uuid; use crate::backend; @@ -35,8 +37,13 @@ pub fn new_change_id() -> ChangeId { } pub fn signature(settings: &UserSettings) -> Signature { - // TODO: check if it's slow to get the timezone etc for every signature - let timestamp = Timestamp::now(); + let timestamp = match env::var("JJ_TIMESTAMP") { + Ok(timestamp_str) => match DateTime::parse_from_rfc3339(×tamp_str) { + Ok(datetime) => Timestamp::from_datetime(datetime), + Err(_) => Timestamp::now(), + }, + Err(_) => Timestamp::now(), + }; Signature { name: settings.user_name(), email: settings.user_email(), diff --git a/src/testutils.rs b/src/testutils.rs index 0648ab00e..b9a66fff4 100644 --- a/src/testutils.rs +++ b/src/testutils.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::cell::RefCell; use std::path::{Path, PathBuf}; use itertools::Itertools; @@ -22,6 +23,7 @@ pub struct TestEnvironment { _temp_dir: TempDir, env_root: PathBuf, home_dir: PathBuf, + command_number: RefCell, } impl Default for TestEnvironment { @@ -33,6 +35,7 @@ impl Default for TestEnvironment { _temp_dir: tmp_dir, env_root, home_dir, + command_number: RefCell::new(0), } } } @@ -43,6 +46,11 @@ impl TestEnvironment { cmd.current_dir(current_dir); cmd.args(args); cmd.env("HOME", self.home_dir.to_str().unwrap()); + let timestamp = chrono::DateTime::parse_from_rfc3339("2001-02-03T04:05:06+07:00").unwrap(); + let mut command_number = self.command_number.borrow_mut(); + *command_number += 1; + let timestamp = timestamp + chrono::Duration::seconds(*command_number); + cmd.env("JJ_TIMESTAMP", timestamp.to_rfc3339()); cmd } diff --git a/tests/smoke_test.rs b/tests/smoke_test.rs index 04fb96259..62133db03 100644 --- a/tests/smoke_test.rs +++ b/tests/smoke_test.rs @@ -12,59 +12,64 @@ // See the License for the specific language governing permissions and // limitations under the License. -use jujutsu::testutils::{capture_matches, TestEnvironment}; +use jujutsu::testutils::TestEnvironment; #[test] fn smoke_test() { let test_env = TestEnvironment::default(); test_env - .jj_cmd(test_env.env_root(), &["init", "repo"]) + .jj_cmd(test_env.env_root(), &["init", "repo", "--git"]) .assert() .success(); let repo_path = test_env.env_root().join("repo"); // Check the output of `jj status` right after initializing repo - let assert = test_env.jj_cmd(&repo_path, &["status"]).assert().success(); - let output_regex = "^Parent commit: 000000000000 \nWorking copy : ([[:xdigit:]]+) \nThe \ - working copy is clean\n$"; - let (_, matches) = capture_matches(assert, output_regex); - let wc_hex_id_empty = matches[0].clone(); + let expected_output = + "Parent commit: 000000000000 \nWorking copy : 1d1984a23811 \nThe working copy is clean\n"; + test_env + .jj_cmd(&repo_path, &["status"]) + .assert() + .success() + .stdout(expected_output); // Write some files and check the output of `jj status` std::fs::write(repo_path.join("file1"), "file1").unwrap(); std::fs::write(repo_path.join("file2"), "file2").unwrap(); std::fs::write(repo_path.join("file3"), "file3").unwrap(); - let assert = test_env.jj_cmd(&repo_path, &["status"]).assert().success(); - let output_regex = "^Parent commit: 000000000000 \nWorking copy : ([[:xdigit:]]+) \nWorking \ - copy changes: + // The working copy's ID should have changed + let expected_output = "Parent commit: 000000000000 \nWorking copy : 5e60c5091e43 \nWorking \ + copy changes: A file1 A file2 A file3 -$"; - let (_, matches) = capture_matches(assert, output_regex); - let wc_hex_id_non_empty = matches[0].clone(); - - // The working copy's id should have changed - assert_ne!(wc_hex_id_non_empty, wc_hex_id_empty); +"; + test_env + .jj_cmd(&repo_path, &["status"]) + .assert() + .success() + .stdout(expected_output); // Running `jj status` again gives the same output - let assert = test_env.jj_cmd(&repo_path, &["status"]).assert().success(); - let (_, matches) = capture_matches(assert, output_regex); - let wc_hex_id_again = matches[0].clone(); - assert_eq!(wc_hex_id_again, wc_hex_id_non_empty); + test_env + .jj_cmd(&repo_path, &["status"]) + .assert() + .success() + .stdout(expected_output); // Add a commit description - let assert = test_env + let expected_output = "Working copy now at: 6f13b3e41065 add some files\n"; + test_env .jj_cmd(&repo_path, &["describe", "-m", "add some files"]) .assert() - .success(); - let output_regex = "^Working copy now at: [[:xdigit:]]+ add some files -$"; - assert.stdout(predicates::str::is_match(output_regex).unwrap()); + .success() + .stdout(expected_output); // Close the commit - let assert = test_env.jj_cmd(&repo_path, &["close"]).assert().success(); - let output_regex = "^Working copy now at: [[:xdigit:]]+ \n$"; - assert.stdout(predicates::str::is_match(output_regex).unwrap()); + let expected_output = "Working copy now at: 6ff8a22d8ce1 \n"; + test_env + .jj_cmd(&repo_path, &["close"]) + .assert() + .success() + .stdout(expected_output); } diff --git a/tests/test_global_opts.rs b/tests/test_global_opts.rs index afae08b6f..af08ecac3 100644 --- a/tests/test_global_opts.rs +++ b/tests/test_global_opts.rs @@ -12,62 +12,67 @@ // See the License for the specific language governing permissions and // limitations under the License. -use jujutsu::testutils::{get_stdout_string, TestEnvironment}; +use jujutsu::testutils::TestEnvironment; #[test] fn test_no_commit_working_copy() { let test_env = TestEnvironment::default(); test_env - .jj_cmd(test_env.env_root(), &["init", "repo"]) + .jj_cmd(test_env.env_root(), &["init", "repo", "--git"]) .assert() .success(); let repo_path = test_env.env_root().join("repo"); std::fs::write(repo_path.join("file"), "initial").unwrap(); - let assert = test_env + let expected_output = "@ 1e9ff0ea7220c37a1d2c4aab153e238c12ff3cd0 +o 0000000000000000000000000000000000000000 +"; + test_env .jj_cmd(&repo_path, &["log", "-T", "commit_id"]) .assert() - .success(); - let initial_commit_id_hex = get_stdout_string(&assert); + .success() + .stdout(expected_output); // Modify the file. With --no-commit-working-copy, we still get the same commit // ID. std::fs::write(repo_path.join("file"), "modified").unwrap(); - let assert = test_env + test_env .jj_cmd( &repo_path, &["log", "-T", "commit_id", "--no-commit-working-copy"], ) .assert() - .success(); - let still_initial_commit_id_hex = get_stdout_string(&assert); - assert_eq!(still_initial_commit_id_hex, initial_commit_id_hex); + .success() + .stdout(expected_output); + // But without --no-commit-working-copy, we get a new commit ID. - let assert = test_env + let expected_output = "@ cc12440b719c67fcd8c55848eb345f67b6e2d9f1 +o 0000000000000000000000000000000000000000 +"; + test_env .jj_cmd(&repo_path, &["log", "-T", "commit_id"]) .assert() - .success(); - let modified_commit_id_hex = get_stdout_string(&assert); - assert_ne!(modified_commit_id_hex, initial_commit_id_hex); + .success() + .stdout(expected_output); } #[test] fn test_repo_arg_with_init() { let test_env = TestEnvironment::default(); - let assert = test_env + test_env .jj_cmd(test_env.env_root(), &["init", "-R=.", "repo"]) .assert() - .failure(); - assert.stdout("Error: '--repository' cannot be used with 'init'\n"); + .failure() + .stdout("Error: '--repository' cannot be used with 'init'\n"); } #[test] fn test_repo_arg_with_git_clone() { let test_env = TestEnvironment::default(); - let assert = test_env + test_env .jj_cmd(test_env.env_root(), &["git", "clone", "-R=.", "remote"]) .assert() - .failure(); - assert.stdout("Error: '--repository' cannot be used with 'git clone'\n"); + .failure() + .stdout("Error: '--repository' cannot be used with 'git clone'\n"); }