jj/cli/tests/test_operations.rs
Evan Mesterhazy e1fd402d39 Fix the ContentHash implementations for std::Option, MergedTreeId, and RemoteRefState
The `ContentHash` documentation specifies that implementations for enums should
hash the ordinal number of the variant contained in the enum as a 32-bit
little-endian number and then hash the contents of the variant, if any.

The current implementations for `std::Option`, `MergedTreeId`, and
`RemoteRefState` are non-conformant since they hash the ordinal number as a u8
with platform specific endianness.


Fixes #3051
2024-02-16 09:27:32 -05:00

450 lines
20 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2022 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::path::Path;
use itertools::Itertools;
use regex::Regex;
use crate::common::{get_stdout_string, TestEnvironment};
#[test]
fn test_op_log() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "description 0"]);
let stdout = test_env.jj_cmd_success(
&repo_path,
&[
"op",
"log",
"--config-toml",
"template-aliases.'format_time_range(x)' = 'x'",
],
);
insta::assert_snapshot!(&stdout, @r###"
@ c12bcc2a82e7 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
│ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
│ args: jj describe -m 'description 0'
◉ 6ac4339ad699 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ add workspace 'default'
◉ 1b0049c19762 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ initialize repo
◉ 000000000000 root()
"###);
let op_log_lines = stdout.lines().collect_vec();
let add_workspace_id = op_log_lines[3].split(' ').nth(2).unwrap();
let initialize_repo_id = op_log_lines[5].split(' ').nth(2).unwrap();
// Can load the repo at a specific operation ID
insta::assert_snapshot!(get_log_output(&test_env, &repo_path, initialize_repo_id), @r###"
◉ 0000000000000000000000000000000000000000
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path, add_workspace_id), @r###"
@ 230dd059e1b059aefc0da06a2e5a7dbf22362f22
◉ 0000000000000000000000000000000000000000
"###);
// "@" resolves to the head operation
insta::assert_snapshot!(get_log_output(&test_env, &repo_path, "@"), @r###"
@ bc8f18aa6f396a93572811632313cbb5625d475d
◉ 0000000000000000000000000000000000000000
"###);
// "@-" resolves to the parent of the head operation
insta::assert_snapshot!(get_log_output(&test_env, &repo_path, "@-"), @r###"
@ 230dd059e1b059aefc0da06a2e5a7dbf22362f22
◉ 0000000000000000000000000000000000000000
"###);
insta::assert_snapshot!(
test_env.jj_cmd_failure(&repo_path, &["log", "--at-op", "@----"]), @r###"
Error: The "@----" expression resolved to no operations
"###);
// We get a reasonable message if an invalid operation ID is specified
insta::assert_snapshot!(test_env.jj_cmd_failure(&repo_path, &["log", "--at-op", "foo"]), @r###"
Error: Operation ID "foo" is not a valid hexadecimal prefix
"###);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "description 1"]);
test_env.jj_cmd_ok(
&repo_path,
&[
"describe",
"-m",
"description 2",
"--at-op",
add_workspace_id,
],
);
insta::assert_snapshot!(test_env.jj_cmd_failure(&repo_path, &["log", "--at-op", "@-"]), @r###"
Error: The "@" expression resolved to more than one operation
"###);
}
#[test]
fn test_op_log_limit() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "-Tdescription", "--limit=1"]);
insta::assert_snapshot!(stdout, @r###"
@ add workspace 'default'
"###);
}
#[test]
fn test_op_log_no_graph() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
let stdout =
test_env.jj_cmd_success(&repo_path, &["op", "log", "--no-graph", "--color=always"]);
insta::assert_snapshot!(stdout, @r###"
6ac4339ad699 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
1b0049c19762 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 root()
"###);
}
#[test]
fn test_op_log_no_graph_null_terminated() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "message1"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "message2"]);
let stdout = test_env.jj_cmd_success(
&repo_path,
&[
"op",
"log",
"--no-graph",
"--template",
r#"id.short(4) ++ "\0""#,
],
);
insta::assert_debug_snapshot!(stdout, @r###""050b\0c02e\06ac4\01b00\00000\0""###);
}
#[test]
fn test_op_log_template() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
let render = |template| test_env.jj_cmd_success(&repo_path, &["op", "log", "-T", template]);
insta::assert_snapshot!(render(r#"id ++ "\n""#), @r###"
@ 6ac4339ad6999058dd1806653ec37fc0091c1cc17419c750fddc5e8c1a6a77829e6dd70b3408403fb2c0b9839cf6bfd1c270f980674f7f89d4d78dc54082a8ef
◉ 1b0049c19762e43499f2499a45afc9f72b3004d75a2863d41d8867cfafb9bbc8e16aa447107e460d58a5c1462429f032d806f7487836c66c6f351df45746c218
◉ 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
"###);
insta::assert_snapshot!(
render(r#"separate(" ", id.short(5), current_operation, user,
time.start(), time.end(), time.duration()) ++ "\n""#), @r###"
@ 6ac43 true test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
◉ 1b004 false test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
◉ 00000 false @ 1970-01-01 00:00:00.000 +00:00 1970-01-01 00:00:00.000 +00:00 less than a microsecond
"###);
// Negative length shouldn't cause panic (and is clamped.)
// TODO: If we add runtime error, this will probably error out.
insta::assert_snapshot!(render(r#"id.short(-1) ++ "|""#), @r###"
@ |
◉ |
◉ |
"###);
// Test the default template, i.e. with relative start time and duration. We
// don't generally use that template because it depends on the current time,
// so we need to reset the time range format here.
test_env.add_config(
r#"
[template-aliases]
'format_time_range(time_range)' = 'time_range.start().ago() ++ ", lasted " ++ time_range.duration()'
"#,
);
let regex = Regex::new(r"\d\d years").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
insta::assert_snapshot!(regex.replace_all(&stdout, "NN years"), @r###"
@ 6ac4339ad699 test-username@host.example.com NN years ago, lasted less than a microsecond
│ add workspace 'default'
◉ 1b0049c19762 test-username@host.example.com NN years ago, lasted less than a microsecond
│ initialize repo
◉ 000000000000 root()
"###);
}
#[test]
fn test_op_log_builtin_templates() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
let render = |template| test_env.jj_cmd_success(&repo_path, &["op", "log", "-T", template]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "description 0"]);
insta::assert_snapshot!(render(r#"builtin_op_log_compact"#), @r###"
@ c12bcc2a82e7 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
│ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
│ args: jj describe -m 'description 0'
◉ 6ac4339ad699 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ add workspace 'default'
◉ 1b0049c19762 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ initialize repo
◉ 000000000000 root()
"###);
insta::assert_snapshot!(render(r#"builtin_op_log_comfortable"#), @r###"
@ c12bcc2a82e7 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
│ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
│ args: jj describe -m 'description 0'
◉ 6ac4339ad699 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ add workspace 'default'
◉ 1b0049c19762 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ initialize repo
◉ 000000000000 root()
"###);
}
#[test]
fn test_op_log_word_wrap() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
let render = |args: &[&str], columns: u32, word_wrap: bool| {
let mut args = args.to_vec();
if word_wrap {
args.push("--config-toml=ui.log-word-wrap=true");
}
let assert = test_env
.jj_cmd(&repo_path, &args)
.env("COLUMNS", columns.to_string())
.assert()
.success()
.stderr("");
get_stdout_string(&assert)
};
// ui.log-word-wrap option works
insta::assert_snapshot!(render(&["op", "log"], 40, false), @r###"
@ 6ac4339ad699 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ add workspace 'default'
◉ 1b0049c19762 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ initialize repo
◉ 000000000000 root()
"###);
insta::assert_snapshot!(render(&["op", "log"], 40, true), @r###"
@ 6ac4339ad699
│ test-username@host.example.com
│ 2001-02-03 04:05:07.000 +07:00 -
│ 2001-02-03 04:05:07.000 +07:00
│ add workspace 'default'
◉ 1b0049c19762
│ test-username@host.example.com
│ 2001-02-03 04:05:07.000 +07:00 -
│ 2001-02-03 04:05:07.000 +07:00
│ initialize repo
◉ 000000000000 root()
"###);
}
#[test]
fn test_op_log_configurable() {
let test_env = TestEnvironment::default();
test_env.add_config(
r#"operation.hostname = "my-hostname"
operation.username = "my-username"
"#,
);
test_env
.jj_cmd(test_env.env_root(), &["init", "repo", "--git"])
.env_remove("JJ_OP_HOSTNAME")
.env_remove("JJ_OP_USERNAME")
.assert()
.success();
let repo_path = test_env.env_root().join("repo");
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
assert!(stdout.contains("my-username@my-hostname"));
}
#[test]
fn test_op_abandon_ancestors() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 1"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 2"]);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ d4553a89325a test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
│ commit a8ac27b29a157ae7dabc0deb524df68823505730
│ args: jj commit -m 'commit 2'
◉ de5974401ad4 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
│ commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
│ args: jj commit -m 'commit 1'
◉ 6ac4339ad699 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ add workspace 'default'
◉ 1b0049c19762 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
│ initialize repo
◉ 000000000000 root()
"###);
// Abandon old operations. The working-copy operation id should be updated.
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "..@-"]);
insta::assert_snapshot!(stderr, @r###"
Abandoned 3 operations and reparented 1 descendant operations.
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("8d45b00ca36ad7cf1e50ed595eb1ddf744765ada1e1b11c44544666b1fa11eedb41bb925886894bc6a49332c86299b70cf4c486143935965ef1958d7fc17257b")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ 8d45b00ca36a test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
│ commit a8ac27b29a157ae7dabc0deb524df68823505730
│ args: jj commit -m 'commit 2'
◉ 000000000000 root()
"###);
// Abandon operation range.
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 3"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 4"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 5"]);
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "@---..@-"]);
insta::assert_snapshot!(stderr, @r###"
Abandoned 2 operations and reparented 1 descendant operations.
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ 49a67606c2ea test-username@host.example.com 2001-02-03 04:05:16.000 +07:00 - 2001-02-03 04:05:16.000 +07:00
│ commit e184d62c9ab118b0f62de91959b857550a9273a5
│ args: jj commit -m 'commit 5'
◉ 8d45b00ca36a test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
│ commit a8ac27b29a157ae7dabc0deb524df68823505730
│ args: jj commit -m 'commit 2'
◉ 000000000000 root()
"###);
// Can't abandon the current operation.
let stderr = test_env.jj_cmd_failure(&repo_path, &["op", "abandon", "..@"]);
insta::assert_snapshot!(stderr, @r###"
Error: Cannot abandon the current operation
Hint: Run `jj undo` to revert the current operation, then use `jj op abandon`
"###);
// Can't create concurrent abandoned operations explicitly.
let stderr = test_env.jj_cmd_failure(&repo_path, &["op", "abandon", "--at-op=@-", "@"]);
insta::assert_snapshot!(stderr, @r###"
Error: --at-op is not respected
"###);
// Abandon the current operation by undoing it first.
test_env.jj_cmd_ok(&repo_path, &["undo"]);
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "@-"]);
insta::assert_snapshot!(stderr, @r###"
Abandoned 1 operations and reparented 1 descendant operations.
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("3579f60625d3fc2768dd156488df7ccae6c0076de6ce66cfd02a951de182ac0652bad67c4c5e1f30a3369da8910f3c7cbaef1b5c2781d7c43da2a4404ab470fc")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ 3579f60625d3 test-username@host.example.com 2001-02-03 04:05:21.000 +07:00 - 2001-02-03 04:05:21.000 +07:00
│ undo operation 49a67606c2eacaa4af83229c46652c7d5b5c36abdc5f6480baeb7331a19f418f267911410491f558c3f88345f83540ee5a04eb4e8b818dd662a9f419b5eb8f66
│ args: jj undo
◉ 8d45b00ca36a test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
│ commit a8ac27b29a157ae7dabc0deb524df68823505730
│ args: jj commit -m 'commit 2'
◉ 000000000000 root()
"###);
// Abandon empty range.
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "@-..@-"]);
insta::assert_snapshot!(stderr, @r###"
Nothing changed.
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log", "-l1"]), @r###"
@ 3579f60625d3 test-username@host.example.com 2001-02-03 04:05:21.000 +07:00 - 2001-02-03 04:05:21.000 +07:00
│ undo operation 49a67606c2eacaa4af83229c46652c7d5b5c36abdc5f6480baeb7331a19f418f267911410491f558c3f88345f83540ee5a04eb4e8b818dd662a9f419b5eb8f66
│ args: jj undo
"###);
}
#[test]
fn test_op_abandon_without_updating_working_copy() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 1"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 2"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 3"]);
// Abandon without updating the working copy.
let (_stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["op", "abandon", "@-", "--ignore-working-copy"],
);
insta::assert_snapshot!(stderr, @r###"
Abandoned 1 operations and reparented 1 descendant operations.
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("880aaeffd50eb8682cd132b6d4a449a79c988ce8ff53fa50dd5b22849c8569ca345e313cd7f52b350d4b08e1567d39a556dbc437c24edbfccc9af23764e9b766")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["op", "log", "-l1", "--ignore-working-copy"]), @r###"
@ d4f54739fcd7 test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
│ commit 268f5f16139313ff25bef31280b2ec2e675200f3
│ args: jj commit -m 'commit 3'
"###);
// The working-copy operation id isn't updated if it differs from the repo.
// It could be updated if the tree matches, but there's no extra logic for
// that.
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "@-"]);
insta::assert_snapshot!(stderr, @r###"
Abandoned 1 operations and reparented 1 descendant operations.
The working copy operation 880aaeffd50e is not updated because it differs from the repo d4f54739fcd7.
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("880aaeffd50eb8682cd132b6d4a449a79c988ce8ff53fa50dd5b22849c8569ca345e313cd7f52b350d4b08e1567d39a556dbc437c24edbfccc9af23764e9b766")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["op", "log", "-l1", "--ignore-working-copy"]), @r###"
@ 1b403259869c test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
│ commit 268f5f16139313ff25bef31280b2ec2e675200f3
│ args: jj commit -m 'commit 3'
"###);
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path, op_id: &str) -> String {
test_env.jj_cmd_success(
repo_path,
&["log", "-T", "commit_id", "--at-op", op_id, "-r", "all()"],
)
}