use crate::hay::{SHERLOCK, SHERLOCK_CRLF};
use crate::util::{sort_lines, Dir, TestCommand};

// See: https://github.com/BurntSushi/ripgrep/issues/1
rgtest!(f1_sjis, |dir: Dir, mut cmd: TestCommand| {
    dir.create_bytes(
        "foo",
        b"\x84Y\x84u\x84\x82\x84|\x84\x80\x84{ \x84V\x84\x80\x84|\x84}\x84\x83"
    );
    cmd.arg("-Esjis").arg("Шерлок Холмс");
    eqnice!("foo:Шерлок Холмс\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1
rgtest!(f1_utf16_auto, |dir: Dir, mut cmd: TestCommand| {
    dir.create_bytes(
        "foo",
        b"\xff\xfe(\x045\x04@\x04;\x04>\x04:\x04 \x00%\x04>\x04;\x04<\x04A\x04"
    );
    cmd.arg("Шерлок Холмс");
    eqnice!("foo:Шерлок Холмс\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1
rgtest!(f1_utf16_explicit, |dir: Dir, mut cmd: TestCommand| {
    dir.create_bytes(
        "foo",
        b"\xff\xfe(\x045\x04@\x04;\x04>\x04:\x04 \x00%\x04>\x04;\x04<\x04A\x04"
    );
    cmd.arg("-Eutf-16le").arg("Шерлок Холмс");
    eqnice!("foo:Шерлок Холмс\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1
rgtest!(f1_eucjp, |dir: Dir, mut cmd: TestCommand| {
    dir.create_bytes(
        "foo",
        b"\xa7\xba\xa7\xd6\xa7\xe2\xa7\xdd\xa7\xe0\xa7\xdc \xa7\xb7\xa7\xe0\xa7\xdd\xa7\xde\xa7\xe3"
    );
    cmd.arg("-Eeuc-jp").arg("Шерлок Холмс");
    eqnice!("foo:Шерлок Холмс\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1
rgtest!(f1_unknown_encoding, |_: Dir, mut cmd: TestCommand| {
    cmd.arg("-Efoobar").assert_non_empty_stderr();
});

// See: https://github.com/BurntSushi/ripgrep/issues/1
rgtest!(f1_replacement_encoding, |_: Dir, mut cmd: TestCommand| {
    cmd.arg("-Ecsiso2022kr").assert_non_empty_stderr();
});

// See: https://github.com/BurntSushi/ripgrep/issues/7
rgtest!(f7, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    dir.create("pat", "Sherlock\nHolmes");

    let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
";
    eqnice!(expected, cmd.arg("-fpat").arg("sherlock").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/7
rgtest!(f7_stdin, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    let expected = "\
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
";
    eqnice!(expected, cmd.arg("-f-").pipe(b"Sherlock"));
});

// See: https://github.com/BurntSushi/ripgrep/issues/20
rgtest!(f20_no_filename, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    cmd.arg("--no-filename");

    let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock
be, to a very large extent, the result of luck. Sherlock Holmes
";
    eqnice!(expected, cmd.arg("--no-filename").arg("Sherlock").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/34
rgtest!(f34_only_matching, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    let expected = "\
sherlock:Sherlock
sherlock:Sherlock
";
    eqnice!(expected, cmd.arg("-o").arg("Sherlock").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/34
rgtest!(f34_only_matching_line_column, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    let expected = "\
sherlock:1:57:Sherlock
sherlock:3:49:Sherlock
";
    cmd.arg("-o").arg("--column").arg("-n").arg("Sherlock");
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/45
rgtest!(f45_relative_cwd, |dir: Dir, mut cmd: TestCommand| {
    dir.create(".not-an-ignore", "foo\n/bar");
    dir.create_dir("bar");
    dir.create_dir("baz/bar");
    dir.create_dir("baz/baz/bar");
    dir.create("bar/test", "test");
    dir.create("baz/bar/test", "test");
    dir.create("baz/baz/bar/test", "test");
    dir.create("baz/foo", "test");
    dir.create("baz/test", "test");
    dir.create("foo", "test");
    dir.create("test", "test");

    cmd.arg("-l").arg("test");

    // First, get a baseline without applying ignore rules.
    let expected = "
bar/test
baz/bar/test
baz/baz/bar/test
baz/foo
baz/test
foo
test
";
    eqnice!(sort_lines(expected), sort_lines(&cmd.stdout()));

    // Now try again with the ignore file activated.
    cmd.arg("--ignore-file").arg(".not-an-ignore");
    let expected = "
baz/bar/test
baz/baz/bar/test
baz/test
test
";
    eqnice!(sort_lines(expected), sort_lines(&cmd.stdout()));

    // Now do it again, but inside the baz directory. Since the ignore file
    // is interpreted relative to the CWD, this will cause the /bar anchored
    // pattern to filter out baz/bar, which is a subtle difference between true
    // parent ignore files and manually specified ignore files.
    let mut cmd = dir.command();
    cmd.args(&["--ignore-file", "../.not-an-ignore", "-l", "test"]);
    cmd.current_dir(dir.path().join("baz"));
    let expected = "
baz/bar/test
test
";
    eqnice!(sort_lines(expected), sort_lines(&cmd.stdout()));
});

// See: https://github.com/BurntSushi/ripgrep/issues/45
rgtest!(f45_precedence_with_others, |dir: Dir, mut cmd: TestCommand| {
    dir.create(".not-an-ignore", "*.log");
    dir.create(".ignore", "!imp.log");
    dir.create("imp.log", "test");
    dir.create("wat.log", "test");

    cmd.arg("--ignore-file").arg(".not-an-ignore").arg("test");
    eqnice!("imp.log:test\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/45
rgtest!(f45_precedence_internal, |dir: Dir, mut cmd: TestCommand| {
    dir.create(".not-an-ignore1", "*.log");
    dir.create(".not-an-ignore2", "!imp.log");
    dir.create("imp.log", "test");
    dir.create("wat.log", "test");

    cmd.args(&[
        "--ignore-file",
        ".not-an-ignore1",
        "--ignore-file",
        ".not-an-ignore2",
        "test",
    ]);
    eqnice!("imp.log:test\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/68
rgtest!(f68_no_ignore_vcs, |dir: Dir, mut cmd: TestCommand| {
    dir.create_dir(".git");
    dir.create(".gitignore", "foo");
    dir.create(".ignore", "bar");
    dir.create("foo", "test");
    dir.create("bar", "test");

    eqnice!("foo:test\n", cmd.arg("--no-ignore-vcs").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/70
rgtest!(f70_smart_case, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    let expected = "\
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
";
    eqnice!(expected, cmd.arg("-S").arg("sherlock").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/89
rgtest!(f89_files_with_matches, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    cmd.arg("--null").arg("--files-with-matches").arg("Sherlock");
    eqnice!("sherlock\x00", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/89
rgtest!(f89_files_without_match, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    dir.create("file.py", "foo");

    cmd.arg("--null").arg("--files-without-match").arg("Sherlock");
    eqnice!("file.py\x00", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/89
rgtest!(f89_count, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    cmd.arg("--null").arg("--count").arg("Sherlock");
    eqnice!("sherlock\x002\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/89
rgtest!(f89_files, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    eqnice!("sherlock\x00", cmd.arg("--null").arg("--files").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/89
rgtest!(f89_match, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    let expected = "\
sherlock\x00For the Doctor Watsons of this world, as opposed to the Sherlock
sherlock\x00Holmeses, success in the province of detective work must always
sherlock\x00be, to a very large extent, the result of luck. Sherlock Holmes
sherlock\x00can extract a clew from a wisp of straw or a flake of cigar ash;
";
    eqnice!(expected, cmd.arg("--null").arg("-C1").arg("Sherlock").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/109
rgtest!(f109_max_depth, |dir: Dir, mut cmd: TestCommand| {
    dir.create_dir("one");
    dir.create("one/pass", "far");
    dir.create_dir("one/too");
    dir.create("one/too/many", "far");

    cmd.arg("--maxdepth").arg("2").arg("far");
    eqnice!("one/pass:far\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/124
rgtest!(f109_case_sensitive_part1, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "tEsT");

    cmd.arg("--smart-case").arg("--case-sensitive").arg("test").assert_err();
});

// See: https://github.com/BurntSushi/ripgrep/issues/124
rgtest!(f109_case_sensitive_part2, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "tEsT");
    cmd.arg("--ignore-case").arg("--case-sensitive").arg("test").assert_err();
});

// See: https://github.com/BurntSushi/ripgrep/issues/129
rgtest!(f129_matches, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test\ntest abcdefghijklmnopqrstuvwxyz test");

    let expected = "foo:test\nfoo:[Omitted long matching line]\n";
    eqnice!(expected, cmd.arg("-M26").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/129
rgtest!(f129_context, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test\nabcdefghijklmnopqrstuvwxyz");

    let expected = "foo:test\nfoo-[Omitted long context line]\n";
    eqnice!(expected, cmd.arg("-M20").arg("-C1").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/129
rgtest!(f129_replace, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test\ntest abcdefghijklmnopqrstuvwxyz test");

    let expected = "foo:foo\nfoo:[Omitted long line with 2 matches]\n";
    eqnice!(expected, cmd.arg("-M26").arg("-rfoo").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/159
rgtest!(f159_max_count, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test\ntest");

    eqnice!("foo:test\n", cmd.arg("-m1").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/159
rgtest!(f159_max_count_zero, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test\ntest");

    cmd.arg("-m0").arg("test").assert_err();
});

// See: https://github.com/BurntSushi/ripgrep/issues/196
rgtest!(f196_persistent_config, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    cmd.arg("sherlock").arg("sherlock");

    // Make sure we get no matches by default.
    cmd.assert_err();

    // Now add our config file, and make sure it impacts ripgrep.
    dir.create(".ripgreprc", "--ignore-case");
    cmd.cmd().env("RIPGREP_CONFIG_PATH", ".ripgreprc");

    let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock
be, to a very large extent, the result of luck. Sherlock Holmes
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/243
rgtest!(f243_column_line, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test");

    eqnice!("foo:1:1:test\n", cmd.arg("--column").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/263
rgtest!(f263_sort_files, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test");
    dir.create("abc", "test");
    dir.create("zoo", "test");
    dir.create("bar", "test");

    let expected = "abc:test\nbar:test\nfoo:test\nzoo:test\n";
    eqnice!(expected, cmd.arg("--sort-files").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/263
rgtest!(f263_sort_files_reverse, |dir: Dir, mut cmd: TestCommand| {
    dir.create("foo", "test");
    dir.create("abc", "test");
    dir.create("zoo", "test");
    dir.create("bar", "test");

    let expected = "zoo:test\nfoo:test\nbar:test\nabc:test\n";
    eqnice!(expected, cmd.arg("--sortr=path").arg("test").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/275
rgtest!(f275_pathsep, |dir: Dir, mut cmd: TestCommand| {
    dir.create_dir("foo");
    dir.create("foo/bar", "test");

    cmd.arg("test").arg("--path-separator").arg("Z");
    eqnice!("fooZbar:test\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/362
rgtest!(f362_dfa_size_limit, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    // This should fall back to the nfa engine but should still produce the
    // expected result.
    cmd.arg("--dfa-size-limit").arg("10").arg(r"For\s").arg("sherlock");

    let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/362
rgtest!(f362_exceeds_regex_size_limit, |dir: Dir, mut cmd: TestCommand| {
    // --regex-size-limit doesn't apply to PCRE2.
    if dir.is_pcre2() {
        return;
    }
    cmd.arg("--regex-size-limit").arg("10K").arg(r"[0-9]\w+").assert_err();
});

// See: https://github.com/BurntSushi/ripgrep/issues/362
#[cfg(target_pointer_width = "32")]
rgtest!(
    f362_u64_to_narrow_usize_overflow,
    |dir: Dir, mut cmd: TestCommand| {
        // --dfa-size-limit doesn't apply to PCRE2.
        if dir.is_pcre2() {
            return;
        }
        dir.create_size("foo", 1000000);

        // 2^35 * 2^20 is ok for u64, but not for usize
        cmd.arg("--dfa-size-limit").arg("34359738368M").arg("--files");
        cmd.assert_err();
    }
);

// See: https://github.com/BurntSushi/ripgrep/issues/411
rgtest!(
    f411_single_threaded_search_stats,
    |dir: Dir, mut cmd: TestCommand| {
        dir.create("sherlock", SHERLOCK);

        let lines = cmd.arg("-j1").arg("--stats").arg("Sherlock").stdout();
        assert!(lines.contains("Sherlock"));
        assert!(lines.contains("2 matched lines"));
        assert!(lines.contains("1 files contained matches"));
        assert!(lines.contains("1 files searched"));
        assert!(lines.contains("seconds"));
    }
);

rgtest!(f411_parallel_search_stats, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock_1", SHERLOCK);
    dir.create("sherlock_2", SHERLOCK);

    let lines = cmd.arg("-j2").arg("--stats").arg("Sherlock").stdout();
    assert!(lines.contains("4 matched lines"));
    assert!(lines.contains("2 files contained matches"));
    assert!(lines.contains("2 files searched"));
    assert!(lines.contains("seconds"));
});

rgtest!(
    f411_single_threaded_quiet_search_stats,
    |dir: Dir, mut cmd: TestCommand| {
        dir.create("sherlock", SHERLOCK);

        let lines = cmd
            .arg("--quiet")
            .arg("-j1")
            .arg("--stats")
            .arg("Sherlock")
            .stdout();
        assert!(!lines.contains("Sherlock"));
        assert!(lines.contains("2 matched lines"));
        assert!(lines.contains("1 files contained matches"));
        assert!(lines.contains("1 files searched"));
        assert!(lines.contains("seconds"));
    }
);

rgtest!(f411_parallel_quiet_search_stats, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock_1", SHERLOCK);
    dir.create("sherlock_2", SHERLOCK);

    let lines =
        cmd.arg("-j2").arg("--quiet").arg("--stats").arg("Sherlock").stdout();
    assert!(!lines.contains("Sherlock"));
    assert!(lines.contains("4 matched lines"));
    assert!(lines.contains("2 files contained matches"));
    assert!(lines.contains("2 files searched"));
    assert!(lines.contains("seconds"));
});

// See: https://github.com/BurntSushi/ripgrep/issues/416
rgtest!(f416_crlf, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK_CRLF);
    cmd.arg("--crlf").arg(r"Sherlock$").arg("sherlock");

    let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock\r
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/416
rgtest!(f416_crlf_multiline, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK_CRLF);
    cmd.arg("--crlf").arg("-U").arg(r"Sherlock$").arg("sherlock");

    let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock\r
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/416
rgtest!(f416_crlf_only_matching, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK_CRLF);
    cmd.arg("--crlf").arg("-o").arg(r"Sherlock$").arg("sherlock");

    let expected = "\
Sherlock\r
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/419
rgtest!(f419_zero_as_shortcut_for_null, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    cmd.arg("-0").arg("--count").arg("Sherlock");
    eqnice!("sherlock\x002\n", cmd.stdout());
});

rgtest!(f740_passthru, |dir: Dir, mut cmd: TestCommand| {
    dir.create("file", "\nfoo\nbar\nfoobar\n\nbaz\n");
    dir.create("patterns", "foo\nbar\n");

    // We can't assume that the way colour specs are translated to ANSI
    // sequences will remain stable, and --replace doesn't currently work with
    // pass-through, so for now we don't actually test the match sub-strings
    let common_args = &["-n", "--passthru"];
    let foo_expected = "\
1-
2:foo
3-bar
4:foobar
5-
6-baz
";

    // With single pattern
    cmd.args(common_args).arg("foo").arg("file");
    eqnice!(foo_expected, cmd.stdout());

    let foo_bar_expected = "\
1-
2:foo
3:bar
4:foobar
5-
6-baz
";

    // With multiple -e patterns
    let mut cmd = dir.command();
    cmd.args(common_args);
    cmd.args(&["-e", "foo", "-e", "bar", "file"]);
    eqnice!(foo_bar_expected, cmd.stdout());

    // With multiple -f patterns
    let mut cmd = dir.command();
    cmd.args(common_args);
    cmd.args(&["-f", "patterns", "file"]);
    eqnice!(foo_bar_expected, cmd.stdout());

    // -c should override
    let mut cmd = dir.command();
    cmd.args(common_args);
    cmd.args(&["-c", "foo", "file"]);
    eqnice!("2\n", cmd.stdout());

    let only_foo_expected = "\
1-
2:foo
3-bar
4:foo
5-
6-baz
";

    // -o should work
    let mut cmd = dir.command();
    cmd.args(common_args);
    cmd.args(&["-o", "foo", "file"]);
    eqnice!(only_foo_expected, cmd.stdout());

    let replace_foo_expected = "\
1-
2:wat
3-bar
4:watbar
5-
6-baz
";

    // -r should work
    let mut cmd = dir.command();
    cmd.args(common_args);
    cmd.args(&["-r", "wat", "foo", "file"]);
    eqnice!(replace_foo_expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/948
rgtest!(f948_exit_code_match, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    cmd.arg(".");

    cmd.assert_exit_code(0);
});

// See: https://github.com/BurntSushi/ripgrep/issues/948
rgtest!(f948_exit_code_no_match, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    cmd.arg("NADA");

    cmd.assert_exit_code(1);
});

// See: https://github.com/BurntSushi/ripgrep/issues/948
rgtest!(f948_exit_code_error, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    cmd.arg("*");

    cmd.assert_exit_code(2);
});

// See: https://github.com/BurntSushi/ripgrep/issues/917
rgtest!(f917_trim, |dir: Dir, mut cmd: TestCommand| {
    const SHERLOCK: &'static str = "\
zzz
    For the Doctor Watsons of this world, as opposed to the Sherlock
  Holmeses, success in the province of detective work must always
\tbe, to a very large extent, the result of luck. Sherlock Holmes
     can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
 and exhibited clearly, with a label attached.
";
    dir.create("sherlock", SHERLOCK);
    cmd.args(&["-n", "-B1", "-A2", "--trim", "Holmeses", "sherlock"]);

    let expected = "\
2-For the Doctor Watsons of this world, as opposed to the Sherlock
3:Holmeses, success in the province of detective work must always
4-be, to a very large extent, the result of luck. Sherlock Holmes
5-can extract a clew from a wisp of straw or a flake of cigar ash;
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/917
//
// This is like f917_trim, except this tests that trimming occurs even when the
// whitespace is part of a match.
rgtest!(f917_trim_match, |dir: Dir, mut cmd: TestCommand| {
    const SHERLOCK: &'static str = "\
zzz
    For the Doctor Watsons of this world, as opposed to the Sherlock
  Holmeses, success in the province of detective work must always
\tbe, to a very large extent, the result of luck. Sherlock Holmes
     can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
 and exhibited clearly, with a label attached.
";
    dir.create("sherlock", SHERLOCK);
    cmd.args(&["-n", "-B1", "-A2", "--trim", r"\s+Holmeses", "sherlock"]);

    let expected = "\
2-For the Doctor Watsons of this world, as opposed to the Sherlock
3:Holmeses, success in the province of detective work must always
4-be, to a very large extent, the result of luck. Sherlock Holmes
5-can extract a clew from a wisp of straw or a flake of cigar ash;
";
    eqnice!(expected, cmd.stdout());
});

rgtest!(f917_trim_multi_standard, |dir: Dir, mut cmd: TestCommand| {
    const HAYSTACK: &str = "     0123456789abcdefghijklmnopqrstuvwxyz";
    dir.create("haystack", HAYSTACK);
    cmd.args(&["--multiline", "--trim", "-r$0", "--no-filename", r"a\n?bc"]);

    let expected = "0123456789abcdefghijklmnopqrstuvwxyz\n";
    eqnice!(expected, cmd.stdout());
});

rgtest!(f917_trim_max_columns_normal, |dir: Dir, mut cmd: TestCommand| {
    const HAYSTACK: &str = "     0123456789abcdefghijklmnopqrstuvwxyz";
    dir.create("haystack", HAYSTACK);
    cmd.args(&[
        "--trim",
        "--max-columns-preview",
        "-M8",
        "--no-filename",
        "abc",
    ]);

    let expected = "01234567 [... omitted end of long line]\n";
    eqnice!(expected, cmd.stdout());
});

rgtest!(f917_trim_max_columns_matches, |dir: Dir, mut cmd: TestCommand| {
    const HAYSTACK: &str = "     0123456789abcdefghijklmnopqrstuvwxyz";
    dir.create("haystack", HAYSTACK);
    cmd.args(&[
        "--trim",
        "--max-columns-preview",
        "-M8",
        "--color=always",
        "--colors=path:none",
        "--no-filename",
        "abc",
    ]);

    let expected = "01234567 [... 1 more match]\n";
    eqnice!(expected, cmd.stdout());
});

rgtest!(
    f917_trim_max_columns_multi_standard,
    |dir: Dir, mut cmd: TestCommand| {
        const HAYSTACK: &str = "     0123456789abcdefghijklmnopqrstuvwxyz";
        dir.create("haystack", HAYSTACK);
        cmd.args(&[
            "--multiline",
            "--trim",
            "--max-columns-preview",
            "-M8",
            // Force the "slow" printing path without actually
            // putting colors in the output.
            "--color=always",
            "--colors=path:none",
            "--no-filename",
            r"a\n?bc",
        ]);

        let expected = "01234567 [... 1 more match]\n";
        eqnice!(expected, cmd.stdout());
    }
);

rgtest!(
    f917_trim_max_columns_multi_only_matching,
    |dir: Dir, mut cmd: TestCommand| {
        const HAYSTACK: &str = "     0123456789abcdefghijklmnopqrstuvwxyz";
        dir.create("haystack", HAYSTACK);
        cmd.args(&[
            "--multiline",
            "--trim",
            "--max-columns-preview",
            "-M8",
            "--only-matching",
            "--no-filename",
            r".*a\n?bc.*",
        ]);

        let expected = "01234567 [... 0 more matches]\n";
        eqnice!(expected, cmd.stdout());
    }
);

rgtest!(
    f917_trim_max_columns_multi_per_match,
    |dir: Dir, mut cmd: TestCommand| {
        const HAYSTACK: &str = "     0123456789abcdefghijklmnopqrstuvwxyz";
        dir.create("haystack", HAYSTACK);
        cmd.args(&[
            "--multiline",
            "--trim",
            "--max-columns-preview",
            "-M8",
            "--vimgrep",
            "--no-filename",
            r".*a\n?bc.*",
        ]);

        let expected = "1:1:01234567 [... 0 more matches]\n";
        eqnice!(expected, cmd.stdout());
    }
);

// See: https://github.com/BurntSushi/ripgrep/issues/993
rgtest!(f993_null_data, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "foo\x00bar\x00\x00\x00baz\x00");
    cmd.arg("--null-data").arg(r".+").arg("test");

    // If we just used -a instead of --null-data, then the result would include
    // all NUL bytes.
    let expected = "foo\x00bar\x00baz\x00";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1078
//
// N.B. There are many more tests in the grep-printer crate.
rgtest!(f1078_max_columns_preview1, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    cmd.args(&[
        "-M46",
        "--max-columns-preview",
        "exhibited|dusted|has to have it",
    ]);

    let expected = "\
sherlock:but Doctor Watson has to have it taken out for [... omitted end of long line]
sherlock:and exhibited clearly, with a label attached.
";
    eqnice!(expected, cmd.stdout());
});

rgtest!(f1078_max_columns_preview2, |dir: Dir, mut cmd: TestCommand| {
    dir.create("sherlock", SHERLOCK);
    cmd.args(&[
        "-M43",
        "--max-columns-preview",
        // Doing a replacement forces ripgrep to show the number of remaining
        // matches. Normally, this happens by default when printing a tty with
        // colors.
        "-rxxx",
        "exhibited|dusted|has to have it",
    ]);

    let expected = "\
sherlock:but Doctor Watson xxx taken out for him and [... 1 more match]
sherlock:and xxx clearly, with a label attached.
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1138
rgtest!(f1138_no_ignore_dot, |dir: Dir, mut cmd: TestCommand| {
    dir.create_dir(".git");
    dir.create(".gitignore", "foo");
    dir.create(".ignore", "bar");
    dir.create(".fzf-ignore", "quux");
    dir.create("foo", "");
    dir.create("bar", "");
    dir.create("quux", "");

    cmd.arg("--sort").arg("path").arg("--files");
    eqnice!("quux\n", cmd.stdout());
    eqnice!("bar\nquux\n", cmd.arg("--no-ignore-dot").stdout());
    eqnice!("bar\n", cmd.arg("--ignore-file").arg(".fzf-ignore").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1155
rgtest!(f1155_auto_hybrid_regex, |dir: Dir, mut cmd: TestCommand| {
    // No sense in testing a hybrid regex engine with only one engine!
    if !dir.is_pcre2() {
        return;
    }

    dir.create("sherlock", SHERLOCK);
    cmd.arg("--no-pcre2").arg("--auto-hybrid-regex").arg(r"(?<=the )Sherlock");

    let expected = "\
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
";
    eqnice!(expected, cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1207
//
// Tests if without encoding 'none' flag null bytes are consumed by automatic
// encoding detection.
rgtest!(f1207_auto_encoding, |dir: Dir, mut cmd: TestCommand| {
    dir.create_bytes("foo", b"\xFF\xFE\x00\x62");
    cmd.arg("-a").arg("\\x00").arg("foo");
    cmd.assert_exit_code(1);
});

// See: https://github.com/BurntSushi/ripgrep/issues/1207
//
// Tests if encoding 'none' flag does treat file as raw bytes
rgtest!(f1207_ignore_encoding, |dir: Dir, mut cmd: TestCommand| {
    // PCRE2 chokes on this test because it can't search invalid non-UTF-8
    // and the point of this test is to search raw UTF-16.
    if dir.is_pcre2() {
        return;
    }

    dir.create_bytes("foo", b"\xFF\xFE\x00\x62");
    cmd.arg("--encoding").arg("none").arg("-a").arg("\\x00").arg("foo");
    eqnice!("\u{FFFD}\u{FFFD}\x00b\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1414
rgtest!(f1414_no_require_git, |dir: Dir, mut cmd: TestCommand| {
    dir.create(".gitignore", "foo");
    dir.create("foo", "");
    dir.create("bar", "");

    let stdout = cmd.args(&["--sort", "path", "--files"]).stdout();
    eqnice!("bar\nfoo\n", stdout);

    let stdout =
        cmd.args(&["--sort", "path", "--files", "--no-require-git"]).stdout();
    eqnice!("bar\n", stdout);

    let stdout = cmd
        .args(&[
            "--sort",
            "path",
            "--files",
            "--no-require-git",
            "--require-git",
        ])
        .stdout();
    eqnice!("bar\nfoo\n", stdout);
});

// See: https://github.com/BurntSushi/ripgrep/pull/1420
rgtest!(f1420_no_ignore_exclude, |dir: Dir, mut cmd: TestCommand| {
    dir.create_dir(".git/info");
    dir.create(".git/info/exclude", "foo");
    dir.create("bar", "");
    dir.create("foo", "");

    cmd.arg("--sort").arg("path").arg("--files");
    eqnice!("bar\n", cmd.stdout());
    eqnice!("bar\nfoo\n", cmd.arg("--no-ignore-exclude").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/pull/1466
rgtest!(f1466_no_ignore_files, |dir: Dir, mut cmd: TestCommand| {
    dir.create(".myignore", "bar");
    dir.create("bar", "");
    dir.create("foo", "");

    // Test that --no-ignore-files disables --ignore-file.
    // And that --ignore-files overrides --no-ignore-files.
    cmd.arg("--sort").arg("path").arg("--files");
    eqnice!("bar\nfoo\n", cmd.stdout());
    eqnice!("foo\n", cmd.arg("--ignore-file").arg(".myignore").stdout());
    eqnice!("bar\nfoo\n", cmd.arg("--no-ignore-files").stdout());
    eqnice!("foo\n", cmd.arg("--ignore-files").stdout());

    // Test that the -u flag does not disable --ignore-file.
    let mut cmd = dir.command();
    cmd.arg("--sort").arg("path").arg("--files");
    cmd.arg("--ignore-file").arg(".myignore");
    eqnice!("foo\n", cmd.stdout());
    eqnice!("foo\n", cmd.arg("-u").stdout());
});

// See: https://github.com/BurntSushi/ripgrep/pull/2361
rgtest!(f2361_sort_nested_files, |dir: Dir, mut cmd: TestCommand| {
    use std::{thread::sleep, time::Duration};

    if crate::util::is_cross() {
        return;
    }
    dir.create("foo", "1");
    sleep(Duration::from_millis(100));
    dir.create_dir("dir");
    sleep(Duration::from_millis(100));
    dir.create(dir.path().join("dir").join("bar"), "1");

    cmd.arg("--sort").arg("accessed").arg("--files");
    eqnice!("foo\ndir/bar\n", cmd.stdout());

    dir.create("foo", "2");
    sleep(Duration::from_millis(100));
    dir.create(dir.path().join("dir").join("bar"), "2");
    sleep(Duration::from_millis(100));

    cmd.arg("--sort").arg("accessed").arg("--files");
    eqnice!("foo\ndir/bar\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1404
rgtest!(f1404_nothing_searched_warning, |dir: Dir, mut cmd: TestCommand| {
    dir.create(".ignore", "ignored-dir/**");
    dir.create_dir("ignored-dir");
    dir.create("ignored-dir/foo", "needle");

    // Test that, if ripgrep searches only ignored folders/files, then there
    // is a non-zero exit code.
    cmd.arg("needle");
    cmd.assert_err();

    // Test that we actually get an error message that we expect.
    let output = cmd.raw_output();
    let stderr = String::from_utf8_lossy(&output.stderr);
    let expected = "\
        rg: No files were searched, which means ripgrep probably applied \
        a filter you didn't expect.\n\
        Running with --debug will show why files are being skipped.\n\
    ";
    eqnice!(expected, stderr);
});

// See: https://github.com/BurntSushi/ripgrep/issues/1404
rgtest!(f1404_nothing_searched_ignored, |dir: Dir, mut cmd: TestCommand| {
    dir.create(".ignore", "ignored-dir/**");
    dir.create_dir("ignored-dir");
    dir.create("ignored-dir/foo", "needle");

    // Test that, if ripgrep searches only ignored folders/files, then there
    // is a non-zero exit code.
    cmd.arg("--no-messages").arg("needle");
    cmd.assert_err();

    // But since --no-messages is given, there should not be any error message
    // printed.
    let output = cmd.raw_output();
    let stderr = String::from_utf8_lossy(&output.stderr);
    let expected = "";
    eqnice!(expected, stderr);
});

// See: https://github.com/BurntSushi/ripgrep/issues/1842
rgtest!(f1842_field_context_separator, |dir: Dir, _: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    // Test the default.
    let base = &["-n", "-A1", "Doctor Watsons", "sherlock"];
    let expected = "\
1:For the Doctor Watsons of this world, as opposed to the Sherlock
2-Holmeses, success in the province of detective work must always
";
    eqnice!(expected, dir.command().args(base).stdout());

    // Test that it can be overridden.
    let mut args = vec!["--field-context-separator", "!"];
    args.extend(base);
    let expected = "\
1:For the Doctor Watsons of this world, as opposed to the Sherlock
2!Holmeses, success in the province of detective work must always
";
    eqnice!(expected, dir.command().args(&args).stdout());

    // Test that it can use multiple bytes.
    let mut args = vec!["--field-context-separator", "!!"];
    args.extend(base);
    let expected = "\
1:For the Doctor Watsons of this world, as opposed to the Sherlock
2!!Holmeses, success in the province of detective work must always
";
    eqnice!(expected, dir.command().args(&args).stdout());

    // Test that unescaping works.
    let mut args = vec!["--field-context-separator", r"\x7F"];
    args.extend(base);
    let expected = "\
1:For the Doctor Watsons of this world, as opposed to the Sherlock
2\x7FHolmeses, success in the province of detective work must always
";
    eqnice!(expected, dir.command().args(&args).stdout());

    // Test that an empty separator is OK.
    let mut args = vec!["--field-context-separator", r""];
    args.extend(base);
    let expected = "\
1:For the Doctor Watsons of this world, as opposed to the Sherlock
2Holmeses, success in the province of detective work must always
";
    eqnice!(expected, dir.command().args(&args).stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/1842
rgtest!(f1842_field_match_separator, |dir: Dir, _: TestCommand| {
    dir.create("sherlock", SHERLOCK);

    // Test the default.
    let base = &["-n", "Doctor Watsons", "sherlock"];
    let expected = "\
1:For the Doctor Watsons of this world, as opposed to the Sherlock
";
    eqnice!(expected, dir.command().args(base).stdout());

    // Test that it can be overridden.
    let mut args = vec!["--field-match-separator", "!"];
    args.extend(base);
    let expected = "\
1!For the Doctor Watsons of this world, as opposed to the Sherlock
";
    eqnice!(expected, dir.command().args(&args).stdout());

    // Test that it can use multiple bytes.
    let mut args = vec!["--field-match-separator", "!!"];
    args.extend(base);
    let expected = "\
1!!For the Doctor Watsons of this world, as opposed to the Sherlock
";
    eqnice!(expected, dir.command().args(&args).stdout());

    // Test that unescaping works.
    let mut args = vec!["--field-match-separator", r"\x7F"];
    args.extend(base);
    let expected = "\
1\x7FFor the Doctor Watsons of this world, as opposed to the Sherlock
";
    eqnice!(expected, dir.command().args(&args).stdout());

    // Test that an empty separator is OK.
    let mut args = vec!["--field-match-separator", r""];
    args.extend(base);
    let expected = "\
1For the Doctor Watsons of this world, as opposed to the Sherlock
";
    eqnice!(expected, dir.command().args(&args).stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/2288
rgtest!(f2288_context_partial_override, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "1\n2\n3\n4\n5\n6\n7\n8\n9\n");
    cmd.args(&["-C1", "-A2", "5", "test"]);
    eqnice!("4\n5\n6\n7\n", cmd.stdout());
});

// See: https://github.com/BurntSushi/ripgrep/issues/2288
rgtest!(
    f2288_context_partial_override_rev,
    |dir: Dir, mut cmd: TestCommand| {
        dir.create("test", "1\n2\n3\n4\n5\n6\n7\n8\n9\n");
        cmd.args(&["-A2", "-C1", "5", "test"]);
        eqnice!("4\n5\n6\n7\n", cmd.stdout());
    }
);

rgtest!(no_context_sep, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
    cmd.args(&["-A1", "--no-context-separator", "foo", "test"]);
    eqnice!("foo\nctx\nfoo\nctx\n", cmd.stdout());
});

rgtest!(no_context_sep_overrides, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
    cmd.args(&[
        "-A1",
        "--context-separator",
        "AAA",
        "--no-context-separator",
        "foo",
        "test",
    ]);
    eqnice!("foo\nctx\nfoo\nctx\n", cmd.stdout());
});

rgtest!(no_context_sep_overridden, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
    cmd.args(&[
        "-A1",
        "--no-context-separator",
        "--context-separator",
        "AAA",
        "foo",
        "test",
    ]);
    eqnice!("foo\nctx\nAAA\nfoo\nctx\n", cmd.stdout());
});

rgtest!(context_sep, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
    cmd.args(&["-A1", "--context-separator", "AAA", "foo", "test"]);
    eqnice!("foo\nctx\nAAA\nfoo\nctx\n", cmd.stdout());
});

rgtest!(context_sep_default, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
    cmd.args(&["-A1", "foo", "test"]);
    eqnice!("foo\nctx\n--\nfoo\nctx\n", cmd.stdout());
});

rgtest!(context_sep_empty, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
    cmd.args(&["-A1", "--context-separator", "", "foo", "test"]);
    eqnice!("foo\nctx\n\nfoo\nctx\n", cmd.stdout());
});

rgtest!(no_unicode, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "δ");
    cmd.arg("-i").arg("--no-unicode").arg("Δ").assert_err();
});

// See: https://github.com/BurntSushi/ripgrep/issues/1790
rgtest!(stop_on_nonmatch, |dir: Dir, mut cmd: TestCommand| {
    dir.create("test", "line1\nline2\nline3\nline4\nline5");
    cmd.args(&["--stop-on-nonmatch", "[235]"]);
    eqnice!("test:line2\ntest:line3\n", cmd.stdout());
});
