2177 lines · 72.7 KB · Rust
RawDownload
// Copyright 2024 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. #[cfg(unix)]use std::os::unix::fs::PermissionsExt as _;use std::path::PathBuf; use indoc::formatdoc;use indoc::indoc;use jj_lib::file_util::symlink_file;use testutils::TestResult; use crate::common::TestEnvironment;use crate::common::to_toml_value; fn set_up_fake_formatter(test_env: &mut TestEnvironment, args: &[&str]) {    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    test_env.add_config(formatdoc! {"        [fix.tools.fake-formatter]        command = {command}        patterns = ['all()']        ",        command = toml_edit::Value::from_iter(            [formatter_path.to_str().unwrap()]                .iter()                .chain(args)                .copied()        )    });    test_env.add_paths_to_normalize(formatter_path, "$FAKE_FORMATTER_PATH");} #[test]fn test_config_no_tools() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");     work_dir.write_file("file", "content\n");    let output = work_dir.run_jj(["fix"]);    insta::assert_snapshot!(output, @"    ------- stderr -------    Config error: No `fix.tools` are configured    For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.    [EOF]    [exit status: 1]    ");     let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);    insta::assert_snapshot!(output, @"    content    [EOF]    ");} #[test]fn test_config_nonexistent_tool() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    test_env.add_config(        r###"        [fix.tools.my-tool]        command = ["nonexistent-fix-tool-binary"]        patterns = ["glob:**"]        "###,    );     work_dir.write_file("file", "content\n");    let output = work_dir.run_jj(["fix"]);    // We inform the user about the non-existent tool    insta::assert_snapshot!(output, @r"    ------- stderr -------    Warning: Failed to start `nonexistent-fix-tool-binary`.    Fixed 0 commits of 1 checked.    Nothing changed.    [EOF]    ");} #[test]fn test_config_multiple_tools() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());    test_env.add_config(format!(        r###"        [fix.tools.tool-1]        command = [{formatter}, "--uppercase"]        patterns = ["foo"]         [fix.tools.tool-2]        command = [{formatter}, "--lowercase"]        patterns = ["bar"]        "###,    ));     work_dir.write_file("foo", "Foo\n");    work_dir.write_file("bar", "Bar\n");    work_dir.write_file("baz", "Baz\n");     work_dir.run_jj(["fix"]).success();     let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);    insta::assert_snapshot!(output, @"    FOO    [EOF]    ");    let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);    insta::assert_snapshot!(output, @"    bar    [EOF]    ");    let output = work_dir.run_jj(["file", "show", "baz", "-r", "@"]);    insta::assert_snapshot!(output, @"    Baz    [EOF]    ");} #[test]fn test_config_multiple_tools_with_same_name() {    let mut test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());     // Multiple definitions with the same `name` are not allowed, because it is    // likely to be a mistake, and mistakes are risky when they rewrite files.    test_env.add_config(format!(        r###"        [fix.tools.my-tool]        command = [{formatter}, "--uppercase"]        patterns = ["foo"]         [fix.tools.my-tool]        command = [{formatter}, "--lowercase"]        patterns = ["bar"]        "###,    ));     work_dir.write_file("foo", "Foo\n");    work_dir.write_file("bar", "Bar\n");     let output = work_dir.run_jj(["fix"]);    insta::assert_snapshot!(output, @"    ------- stderr -------    Config error: Configuration cannot be parsed as TOML document    Caused by: TOML parse error at line 6, column 20      |    6 |         [fix.tools.my-tool]      |                    ^^^^^^^    duplicate key     Hint: Check the config file: $TEST_ENV/config/config0002.toml    For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.    [EOF]    [exit status: 1]    ");     test_env.set_config_path("/dev/null");    let work_dir = test_env.work_dir("repo");    let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);    insta::assert_snapshot!(output, @"    Foo    [EOF]    ");    let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);    insta::assert_snapshot!(output, @"    Bar    [EOF]    ");} #[test]fn test_config_disabled_tools() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());    test_env.add_config(format!(        r###"        [fix.tools.tool-1]        # default is enabled        command = [{formatter}, "--uppercase"]        patterns = ["foo"]         [fix.tools.tool-2]        enabled = true        command = [{formatter}, "--lowercase"]        patterns = ["bar"]         [fix.tools.tool-3]        enabled = false        command = [{formatter}, "--lowercase"]        patterns = ["baz"]        "###    ));     work_dir.write_file("foo", "Foo\n");    work_dir.write_file("bar", "Bar\n");    work_dir.write_file("baz", "Baz\n");     work_dir.run_jj(["fix"]).success();     let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);    insta::assert_snapshot!(output, @"    FOO    [EOF]    ");    let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);    insta::assert_snapshot!(output, @"    bar    [EOF]    ");    let output = work_dir.run_jj(["file", "show", "baz", "-r", "@"]);    insta::assert_snapshot!(output, @"    Baz    [EOF]    ");} #[test]fn test_config_disabled_tools_warning_when_all_tools_are_disabled() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());    test_env.add_config(format!(        r###"        [fix.tools.tool-2]        enabled = false        command = [{formatter}, "--lowercase"]        patterns = ["bar"]        "###    ));     work_dir.write_file("bar", "Bar\n");     let output = work_dir.run_jj(["fix"]);    insta::assert_snapshot!(output, @"    ------- stderr -------    Config error: At least one entry of `fix.tools` must be enabled.    For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.    [EOF]    [exit status: 1]    ");} #[test]fn test_config_tables_overlapping_patterns() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());     test_env.add_config(format!(        r###"        [fix.tools.tool-1]        command = [{formatter}, "--append", "tool-1"]        patterns = ["foo", "bar"]         [fix.tools.tool-2]        command = [{formatter}, "--append", "tool-2"]        patterns = ["bar", "baz"]        "###,    ));     work_dir.write_file("foo", "foo\n");    work_dir.write_file("bar", "bar\n");    work_dir.write_file("baz", "baz\n");     work_dir.run_jj(["fix"]).success();     let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);    insta::assert_snapshot!(output, @"    foo    tool-1[EOF]    ");    let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);    insta::assert_snapshot!(output, @"    bar    tool-1tool-2[EOF]    ");    let output = work_dir.run_jj(["file", "show", "baz", "-r", "@"]);    insta::assert_snapshot!(output, @"    baz    tool-2[EOF]    ");} #[test]fn test_config_tables_all_commands_missing() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    test_env.add_config(        r###"        [fix.tools.my-tool-missing-command-1]        patterns = ["foo"]         [fix.tools.my-tool-missing-command-2]        patterns = ['glob:"ba*"']        "###,    );     work_dir.write_file("foo", "foo\n");     let output = work_dir.run_jj(["fix"]);    insta::assert_snapshot!(output.normalize_backslash(), @"    ------- stderr -------    Config error: Invalid type or value for fix.tools.my-tool-missing-command-1    Caused by: missing field `command`     Hint: Check the config file: $TEST_ENV/config/config0002.toml    For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.    [EOF]    [exit status: 1]    ");     let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);    insta::assert_snapshot!(output, @"    foo    [EOF]    ");} #[test]fn test_config_tables_some_commands_missing() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());    test_env.add_config(format!(        r###"        [fix.tools.tool-1]        command = [{formatter}, "--uppercase"]        patterns = ["foo"]         [fix.tools.my-tool-missing-command]        patterns = ['bar']        "###,    ));     work_dir.write_file("foo", "foo\n");     let output = work_dir.run_jj(["fix"]);    insta::assert_snapshot!(output.normalize_backslash(), @"    ------- stderr -------    Config error: Invalid type or value for fix.tools.my-tool-missing-command    Caused by: missing field `command`     Hint: Check the config file: $TEST_ENV/config/config0002.toml    For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.    [EOF]    [exit status: 1]    ");     let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);    insta::assert_snapshot!(output, @"    foo    [EOF]    ");} #[test]fn test_config_tables_empty_patterns_list() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());    test_env.add_config(format!(        r###"        [fix.tools.my-tool-empty-patterns]        command = [{formatter}, "--uppercase"]        patterns = []        "###,    ));     work_dir.write_file("foo", "foo\n");     let output = work_dir.run_jj(["fix"]);    insta::assert_snapshot!(output, @"    ------- stderr -------    Fixed 0 commits of 1 checked.    Nothing changed.    [EOF]    ");     let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);    insta::assert_snapshot!(output, @"    foo    [EOF]    ");} #[test]fn test_config_filesets() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());    test_env.add_config(format!(        r###"        [fix.tools.my-tool-match-one]        command = [{formatter}, "--uppercase"]        patterns = ['glob:"a*"']         [fix.tools.my-tool-match-two]        command = [{formatter}, "--reverse"]        patterns = ['glob:"b*"']         [fix.tools.my-tool-match-none]        command = [{formatter}, "--append", "SHOULD NOT APPEAR"]        patterns = ['glob:"this-doesnt-match-anything-*"']        "###,    ));     work_dir.write_file("a1", "a1\n");    work_dir.write_file("b1", "b1\n");    work_dir.write_file("b2", "b2\n");     work_dir.run_jj(["fix"]).success();     let output = work_dir.run_jj(["file", "show", "a1", "-r", "@"]);    insta::assert_snapshot!(output, @"    A1    [EOF]    ");    let output = work_dir.run_jj(["file", "show", "b1", "-r", "@"]);    insta::assert_snapshot!(output, @"     1b[EOF]    ");    let output = work_dir.run_jj(["file", "show", "b2", "-r", "@"]);    insta::assert_snapshot!(output, @"     2b[EOF]    ");} #[test]fn test_relative_paths() {    let test_env = TestEnvironment::default();    test_env.run_jj_in(".", ["git", "init", "repo"]).success();    let work_dir = test_env.work_dir("repo");    let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");    assert!(formatter_path.is_file());    let formatter = to_toml_value(formatter_path.to_str().unwrap());    test_env.add_config(format!(        r###"        [fix.tools.tool]        command = [{formatter}, "--stdout", "Fixed!"]        patterns = ['glob:"foo*"']        "###,    ));     let sub_dir = work_dir.create_dir("dir");    work_dir.write_file("foo1", "unfixed\n");    work_dir.write_file("foo2", "unfixed\n");    work_dir.write_file("dir/foo3", "unfixed\n");     // Positional arguments are cwd-relative, but the configured patterns are    // repo-relative, so this command fixes the empty intersection of those    // filesets.    sub_dir.run_jj(["fix", "foo3"]).success();    let output = work_dir.run_jj(["file", "show", "foo1", "-r", "@"]);    insta::assert_snapshot!(output, @"    unfixed    [EOF]    ");

Lines 1–500 of 2177.