diff --git a/Cargo.lock b/Cargo.lock index 57d2975b..d11b1608 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,6 +381,8 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 74648607..208a07b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,10 @@ version = "2.29.4" default-features = false features = ["suggestions", "color"] +[dev-dependencies] +serde = "1" +serde_derive = "1" + [features] avx-accel = ["grep/avx-accel"] simd-accel = ["grep/simd-accel"] diff --git a/tests/feature.rs b/tests/feature.rs new file mode 100644 index 00000000..5b14a0d2 --- /dev/null +++ b/tests/feature.rs @@ -0,0 +1,631 @@ +use hay::{SHERLOCK, SHERLOCK_CRLF}; +use util::{Dir, TestCommand, sort_lines}; + +// 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("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/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("--stats").arg("Sherlock").stdout(); + 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("--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")); +}); + +// 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()); +}); + +// 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()); +}); diff --git a/tests/hay.rs b/tests/hay.rs index 74d2f6cc..15a53b15 100644 --- a/tests/hay.rs +++ b/tests/hay.rs @@ -7,18 +7,11 @@ but Doctor Watson has to have it taken out for him and dusted, and exhibited clearly, with a label attached. "; -pub const CODE: &'static str = "\ -extern crate snap; - -use std::io; - -fn main() { - let stdin = io::stdin(); - let stdout = io::stdout(); - - // Wrap the stdin reader in a Snappy reader. - let mut rdr = snap::Reader::new(stdin.lock()); - let mut wtr = stdout.lock(); - io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\"); -} +pub const SHERLOCK_CRLF: &'static str = "\ +For the Doctor Watsons of this world, as opposed to the Sherlock\r +Holmeses, success in the province of detective work must always\r +be, to a very large extent, the result of luck. Sherlock Holmes\r +can extract a clew from a wisp of straw or a flake of cigar ash;\r +but Doctor Watson has to have it taken out for him and dusted,\r +and exhibited clearly, with a label attached.\r "; diff --git a/tests/json.rs b/tests/json.rs new file mode 100644 index 00000000..4433103d --- /dev/null +++ b/tests/json.rs @@ -0,0 +1,263 @@ +use std::time; + +use serde_json as json; + +use hay::{SHERLOCK, SHERLOCK_CRLF}; +use util::{Dir, TestCommand}; + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +#[serde(tag = "type", content = "data")] +#[serde(rename_all = "snake_case")] +enum Message { + Begin(Begin), + End(End), + Match(Match), + Context(Context), + Summary(Summary), +} + +impl Message { + fn unwrap_begin(&self) -> Begin { + match *self { + Message::Begin(ref x) => x.clone(), + ref x => panic!("expected Message::Begin but got {:?}", x), + } + } + + fn unwrap_end(&self) -> End { + match *self { + Message::End(ref x) => x.clone(), + ref x => panic!("expected Message::End but got {:?}", x), + } + } + + fn unwrap_match(&self) -> Match { + match *self { + Message::Match(ref x) => x.clone(), + ref x => panic!("expected Message::Match but got {:?}", x), + } + } + + fn unwrap_context(&self) -> Context { + match *self { + Message::Context(ref x) => x.clone(), + ref x => panic!("expected Message::Context but got {:?}", x), + } + } + + fn unwrap_summary(&self) -> Summary { + match *self { + Message::Summary(ref x) => x.clone(), + ref x => panic!("expected Message::Summary but got {:?}", x), + } + } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct Begin { + path: Option, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct End { + path: Option, + binary_offset: Option, + stats: Stats, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct Summary { + elapsed_total: Duration, + stats: Stats, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct Match { + path: Option, + lines: Data, + line_number: Option, + absolute_offset: u64, + submatches: Vec, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct Context { + path: Option, + lines: Data, + line_number: Option, + absolute_offset: u64, + submatches: Vec, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct SubMatch { + #[serde(rename = "match")] + m: Data, + start: usize, + end: usize, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +#[serde(untagged)] +enum Data { + Text { text: String }, + // This variant is used when the data isn't valid UTF-8. The bytes are + // base64 encoded, so using a String here is OK. + Bytes { bytes: String }, +} + +impl Data { + fn text(s: &str) -> Data { Data::Text { text: s.to_string() } } + fn bytes(s: &str) -> Data { Data::Bytes { bytes: s.to_string() } } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct Stats { + elapsed: Duration, + searches: u64, + searches_with_match: u64, + bytes_searched: u64, + bytes_printed: u64, + matched_lines: u64, + matches: u64, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +struct Duration { + #[serde(flatten)] + duration: time::Duration, + human: String, +} + +/// Decode JSON Lines into a Vec. If there was an error decoding, +/// this function panics. +fn json_decode(jsonlines: &str) -> Vec { + json::Deserializer::from_str(jsonlines) + .into_iter() + .collect::, _>>() + .unwrap() +} + +rgtest!(basic, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--json").arg("-B1").arg("Sherlock Holmes").arg("sherlock"); + + let msgs = json_decode(&cmd.stdout()); + + assert_eq!( + msgs[0].unwrap_begin(), + Begin { path: Some(Data::text("sherlock")) } + ); + assert_eq!( + msgs[1].unwrap_context(), + Context { + path: Some(Data::text("sherlock")), + lines: Data::text("Holmeses, success in the province of detective work must always\n"), + line_number: Some(2), + absolute_offset: 65, + submatches: vec![], + } + ); + assert_eq!( + msgs[2].unwrap_match(), + Match { + path: Some(Data::text("sherlock")), + lines: Data::text("be, to a very large extent, the result of luck. Sherlock Holmes\n"), + line_number: Some(3), + absolute_offset: 129, + submatches: vec![ + SubMatch { + m: Data::text("Sherlock Holmes"), + start: 48, + end: 63, + }, + ], + } + ); + assert_eq!( + msgs[3].unwrap_end().path, + Some(Data::text("sherlock")) + ); + assert_eq!( + msgs[3].unwrap_end().binary_offset, + None + ); + assert_eq!( + msgs[4].unwrap_summary().stats.searches_with_match, + 1 + ); + assert_eq!( + msgs[4].unwrap_summary().stats.bytes_printed, + 494 + ); +}); + +#[cfg(unix)] +rgtest!(notutf8, |dir: Dir, mut cmd: TestCommand| { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + // This test does not work with PCRE2 because PCRE2 does not support the + // `u` flag. + if dir.is_pcre2() { + return; + } + // macOS doesn't like this either... sigh. + if cfg!(target_os = "macos") { + return; + } + + let name = &b"foo\xFFbar"[..]; + let contents = &b"quux\xFFbaz"[..]; + + // APFS does not support creating files with invalid UTF-8 bytes, so just + // skip the test if we can't create our file. + if !dir.try_create_bytes(OsStr::from_bytes(name), contents).is_ok() { + return; + } + cmd.arg("--json").arg(r"(?-u)\xFF"); + + let msgs = json_decode(&cmd.stdout()); + + assert_eq!( + msgs[0].unwrap_begin(), + Begin { path: Some(Data::bytes("Zm9v/2Jhcg==")) } + ); + assert_eq!( + msgs[1].unwrap_match(), + Match { + path: Some(Data::bytes("Zm9v/2Jhcg==")), + lines: Data::bytes("cXV1eP9iYXo="), + line_number: Some(1), + absolute_offset: 0, + submatches: vec![ + SubMatch { + m: Data::bytes("/w=="), + start: 4, + end: 5, + }, + ], + } + ); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/416 +// +// This test in particular checks that our match does _not_ include the `\r` +// even though the '$' may be rewritten as '(?:\r??$)' and could thus include +// `\r` in the match. +rgtest!(crlf, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK_CRLF); + cmd.arg("--json").arg("--crlf").arg(r"Sherlock$").arg("sherlock"); + + let msgs = json_decode(&cmd.stdout()); + + assert_eq!( + msgs[1].unwrap_match().submatches[0].clone(), + SubMatch { + m: Data::text("Sherlock"), + start: 56, + end: 64, + }, + ); +}); diff --git a/tests/macros.rs b/tests/macros.rs new file mode 100644 index 00000000..24bf13f8 --- /dev/null +++ b/tests/macros.rs @@ -0,0 +1,61 @@ +#[macro_export] +macro_rules! rgtest { + ($name:ident, $fun:expr) => { + #[test] + fn $name() { + let (dir, cmd) = ::util::setup(stringify!($name)); + $fun(dir, cmd); + + if cfg!(feature = "pcre2") { + let (dir, cmd) = ::util::setup_pcre2(stringify!($name)); + $fun(dir, cmd); + } + } + } +} + +#[macro_export] +macro_rules! eqnice { + ($expected:expr, $got:expr) => { + let expected = &*$expected; + let got = &*$got; + if expected != got { + panic!(" +printed outputs differ! + +expected: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +got: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +", expected, got); + } + } +} + +#[macro_export] +macro_rules! eqnice_repr { + ($expected:expr, $got:expr) => { + let expected = &*$expected; + let got = &*$got; + if expected != got { + panic!(" +printed outputs differ! + +expected: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{:?} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +got: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{:?} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +", expected, got); + } + } +} diff --git a/tests/misc.rs b/tests/misc.rs new file mode 100644 index 00000000..62226ceb --- /dev/null +++ b/tests/misc.rs @@ -0,0 +1,948 @@ +use hay::SHERLOCK; +use util::{Dir, TestCommand, cmd_exists, sort_lines}; + +// This file contains "miscellaneous" tests that were either written before +// features were tracked more explicitly, or were simply written without +// linking them to a specific issue number. We should try to minimize the +// addition of more tests in this file and instead add them to either the +// regression test suite or the feature test suite (found in regression.rs and +// feature.rs, respectively). + +rgtest!(single_file, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + + 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("Sherlock").arg("sherlock").stdout()); +}); + +rgtest!(dir, |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("Sherlock").stdout()); +}); + +rgtest!(line_numbers, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + + let expected = "\ +1:For the Doctor Watsons of this world, as opposed to the Sherlock +3:be, to a very large extent, the result of luck. Sherlock Holmes +"; + eqnice!(expected, cmd.arg("-n").arg("Sherlock").arg("sherlock").stdout()); +}); + +rgtest!(columns, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--column").arg("Sherlock").arg("sherlock"); + + let expected = "\ +1:57:For the Doctor Watsons of this world, as opposed to the Sherlock +3:49:be, to a very large extent, the result of luck. Sherlock Holmes +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(with_filename, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-H").arg("Sherlock").arg("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.stdout()); +}); + +rgtest!(with_heading, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + // This forces the issue since --with-filename is disabled by default + // when searching one file. + "--with-filename", "--heading", + "Sherlock", "sherlock", + ]); + + let expected = "\ +sherlock +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()); +}); + +rgtest!(with_heading_default, |dir: Dir, mut cmd: TestCommand| { + // Search two or more and get --with-filename enabled by default. + // Use -j1 to get deterministic results. + dir.create("sherlock", SHERLOCK); + dir.create("foo", "Sherlock Holmes lives on Baker Street."); + cmd.arg("-j1").arg("--heading").arg("Sherlock"); + + let expected = "\ +foo +Sherlock Holmes lives on Baker Street. + +sherlock +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!(sort_lines(expected), sort_lines(&cmd.stdout())); +}); + +rgtest!(inverted, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-v").arg("Sherlock").arg("sherlock"); + + let expected = "\ +Holmeses, success in the province of detective work must always +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. +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(inverted_line_numbers, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-n").arg("-v").arg("Sherlock").arg("sherlock"); + + let expected = "\ +2:Holmeses, success in the province of detective work must always +4:can extract a clew from a wisp of straw or a flake of cigar ash; +5:but Doctor Watson has to have it taken out for him and dusted, +6:and exhibited clearly, with a label attached. +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(case_insensitive, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-i").arg("sherlock").arg("sherlock"); + + 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()); +}); + +rgtest!(word, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-w").arg("as").arg("sherlock"); + + let expected = "\ +For the Doctor Watsons of this world, as opposed to the Sherlock +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(line, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-x", + "Watson|and exhibited clearly, with a label attached.", + "sherlock", + ]); + + let expected = "\ +and exhibited clearly, with a label attached. +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(literal, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file", "blib\n()\nblab\n"); + cmd.arg("-F").arg("()").arg("file"); + + eqnice!("()\n", cmd.stdout()); +}); + +rgtest!(quiet, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-q").arg("Sherlock").arg("sherlock"); + + assert!(cmd.stdout().is_empty()); +}); + +rgtest!(replace, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-r").arg("FooBar").arg("Sherlock").arg("sherlock"); + + let expected = "\ +For the Doctor Watsons of this world, as opposed to the FooBar +be, to a very large extent, the result of luck. FooBar Holmes +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(replace_groups, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-r", "$2, $1", "([A-Z][a-z]+) ([A-Z][a-z]+)", "sherlock", + ]); + + let expected = "\ +For the Watsons, Doctor of this world, as opposed to the Sherlock +be, to a very large extent, the result of luck. Holmes, Sherlock +but Watson, Doctor has to have it taken out for him and dusted, +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(replace_named_groups, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-r", "$last, $first", + "(?P[A-Z][a-z]+) (?P[A-Z][a-z]+)", + "sherlock", + ]); + + let expected = "\ +For the Watsons, Doctor of this world, as opposed to the Sherlock +be, to a very large extent, the result of luck. Holmes, Sherlock +but Watson, Doctor has to have it taken out for him and dusted, +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(replace_with_only_matching, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-o").arg("-r").arg("$1").arg(r"of (\w+)").arg("sherlock"); + + let expected = "\ +this +detective +luck +straw +cigar +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(file_types, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "Sherlock"); + dir.create("file.rs", "Sherlock"); + cmd.arg("-t").arg("rust").arg("Sherlock"); + + eqnice!("file.rs:Sherlock\n", cmd.stdout()); +}); + +rgtest!(file_types_all, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "Sherlock"); + cmd.arg("-t").arg("all").arg("Sherlock"); + + eqnice!("file.py:Sherlock\n", cmd.stdout()); +}); + +rgtest!(file_types_negate, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.remove("sherlock"); + dir.create("file.py", "Sherlock"); + dir.create("file.rs", "Sherlock"); + cmd.arg("-T").arg("rust").arg("Sherlock"); + + eqnice!("file.py:Sherlock\n", cmd.stdout()); +}); + +rgtest!(file_types_negate_all, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "Sherlock"); + cmd.arg("-T").arg("all").arg("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.stdout()); +}); + +rgtest!(file_type_clear, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "Sherlock"); + dir.create("file.rs", "Sherlock"); + cmd.arg("--type-clear").arg("rust").arg("-t").arg("rust").arg("Sherlock"); + + cmd.assert_non_empty_stderr(); +}); + +rgtest!(file_type_add, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "Sherlock"); + dir.create("file.rs", "Sherlock"); + dir.create("file.wat", "Sherlock"); + cmd.args(&[ + "--type-add", "wat:*.wat", "-t", "wat", "Sherlock", + ]); + + eqnice!("file.wat:Sherlock\n", cmd.stdout()); +}); + +rgtest!(file_type_add_compose, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "Sherlock"); + dir.create("file.rs", "Sherlock"); + dir.create("file.wat", "Sherlock"); + cmd.args(&[ + "--type-add", "wat:*.wat", + "--type-add", "combo:include:wat,py", + "-t", "combo", + "Sherlock", + ]); + + let expected = "\ +file.py:Sherlock +file.wat:Sherlock +"; + eqnice!(expected, sort_lines(&cmd.stdout())); +}); + +rgtest!(glob, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "Sherlock"); + dir.create("file.rs", "Sherlock"); + cmd.arg("-g").arg("*.rs").arg("Sherlock"); + + eqnice!("file.rs:Sherlock\n", cmd.stdout()); +}); + +rgtest!(glob_negate, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.remove("sherlock"); + dir.create("file.py", "Sherlock"); + dir.create("file.rs", "Sherlock"); + cmd.arg("-g").arg("!*.rs").arg("Sherlock"); + + eqnice!("file.py:Sherlock\n", cmd.stdout()); +}); + +rgtest!(glob_case_insensitive, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.HTML", "Sherlock"); + cmd.arg("--iglob").arg("*.html").arg("Sherlock"); + + eqnice!("file.HTML:Sherlock\n", cmd.stdout()); +}); + +rgtest!(glob_case_sensitive, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file1.HTML", "Sherlock"); + dir.create("file2.html", "Sherlock"); + cmd.arg("--glob").arg("*.html").arg("Sherlock"); + + eqnice!("file2.html:Sherlock\n", cmd.stdout()); +}); + +rgtest!(byte_offset_only_matching, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-b").arg("-o").arg("Sherlock"); + + let expected = "\ +sherlock:56:Sherlock +sherlock:177:Sherlock +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(count, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--count").arg("Sherlock"); + + let expected = "sherlock:2\n"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(count_matches, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--count-matches").arg("the"); + + let expected = "sherlock:4\n"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(count_matches_inverted, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--count-matches").arg("--invert-match").arg("Sherlock"); + + let expected = "sherlock:4\n"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(count_matches_via_only, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--count").arg("--only-matching").arg("the"); + + let expected = "sherlock:4\n"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(files_with_matches, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--files-with-matches").arg("Sherlock"); + + let expected = "sherlock\n"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(files_without_match, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file.py", "foo"); + cmd.arg("--files-without-match").arg("Sherlock"); + + let expected = "file.py\n"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(after_context, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-A").arg("1").arg("Sherlock").arg("sherlock"); + + 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 +can extract a clew from a wisp of straw or a flake of cigar ash; +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(after_context_line_numbers, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-A").arg("1").arg("-n").arg("Sherlock").arg("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 +3:be, to a very large extent, the result of luck. Sherlock Holmes +4-can extract a clew from a wisp of straw or a flake of cigar ash; +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(before_context, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-B").arg("1").arg("Sherlock").arg("sherlock"); + + 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.stdout()); +}); + +rgtest!(before_context_line_numbers, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-B").arg("1").arg("-n").arg("Sherlock").arg("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 +3:be, to a very large extent, the result of luck. Sherlock Holmes +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(context, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-C").arg("1").arg("world|attached").arg("sherlock"); + + let expected = "\ +For the Doctor Watsons of this world, as opposed to the Sherlock +Holmeses, success in the province of detective work must always +-- +but Doctor Watson has to have it taken out for him and dusted, +and exhibited clearly, with a label attached. +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(context_line_numbers, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("-C").arg("1").arg("-n").arg("world|attached").arg("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 +-- +5-but Doctor Watson has to have it taken out for him and dusted, +6:and exhibited clearly, with a label attached. +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(max_filesize_parse_errro_length, |_: Dir, mut cmd: TestCommand| { + cmd.arg("--max-filesize").arg("44444444444444444444"); + cmd.assert_non_empty_stderr(); +}); + +rgtest!(max_filesize_parse_error_suffix, |_: Dir, mut cmd: TestCommand| { + cmd.arg("--max-filesize").arg("45k"); + cmd.assert_non_empty_stderr(); +}); + +rgtest!(max_filesize_parse_no_suffix, |dir: Dir, mut cmd: TestCommand| { + dir.create_size("foo", 40); + dir.create_size("bar", 60); + cmd.arg("--max-filesize").arg("50").arg("--files"); + + eqnice!("foo\n", cmd.stdout()); +}); + +rgtest!(max_filesize_parse_k_suffix, |dir: Dir, mut cmd: TestCommand| { + dir.create_size("foo", 3048); + dir.create_size("bar", 4100); + cmd.arg("--max-filesize").arg("4K").arg("--files"); + + eqnice!("foo\n", cmd.stdout()); +}); + +rgtest!(max_filesize_parse_m_suffix, |dir: Dir, mut cmd: TestCommand| { + dir.create_size("foo", 1000000); + dir.create_size("bar", 1400000); + cmd.arg("--max-filesize").arg("1M").arg("--files"); + + eqnice!("foo\n", cmd.stdout()); +}); + +rgtest!(max_filesize_suffix_overflow, |dir: Dir, mut cmd: TestCommand| { + dir.create_size("foo", 1000000); + + // 2^35 * 2^30 would otherwise overflow + cmd.arg("--max-filesize").arg("34359738368G").arg("--files"); + cmd.assert_non_empty_stderr(); +}); + +rgtest!(ignore_hidden, |dir: Dir, mut cmd: TestCommand| { + dir.create(".sherlock", SHERLOCK); + cmd.arg("Sherlock").assert_err(); +}); + +rgtest!(no_ignore_hidden, |dir: Dir, mut cmd: TestCommand| { + dir.create(".sherlock", SHERLOCK); + cmd.arg("--hidden").arg("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.stdout()); +}); + +rgtest!(ignore_git, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create_dir(".git"); + dir.create(".gitignore", "sherlock\n"); + cmd.arg("Sherlock"); + + cmd.assert_err(); +}); + +rgtest!(ignore_generic, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create(".ignore", "sherlock\n"); + cmd.arg("Sherlock"); + + cmd.assert_err(); +}); + +rgtest!(ignore_ripgrep, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create(".rgignore", "sherlock\n"); + cmd.arg("Sherlock"); + + cmd.assert_err(); +}); + +rgtest!(no_ignore, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create(".gitignore", "sherlock\n"); + cmd.arg("--no-ignore").arg("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.stdout()); +}); + +rgtest!(ignore_git_parent, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "sherlock\n"); + dir.create_dir("foo"); + dir.create("foo/sherlock", SHERLOCK); + cmd.arg("Sherlock"); + + // Even though we search in foo/, which has no .gitignore, ripgrep will + // traverse parent directories and respect the gitignore files found. + cmd.current_dir(dir.path().join("foo")); + cmd.assert_err(); +}); + +rgtest!(ignore_git_parent_stop, |dir: Dir, mut cmd: TestCommand| { + // This tests that searching parent directories for .gitignore files stops + // after it sees a .git directory. To test this, we create this directory + // hierarchy: + // + // .gitignore (contains `sherlock`) + // foo/ + // .git/ + // bar/ + // sherlock + // + // And we perform the search inside `foo/bar/`. ripgrep will stop looking + // for .gitignore files after it sees `foo/.git/`, and therefore not + // respect the top-level `.gitignore` containing `sherlock`. + dir.create(".gitignore", "sherlock\n"); + dir.create_dir("foo"); + dir.create_dir("foo/.git"); + dir.create_dir("foo/bar"); + dir.create("foo/bar/sherlock", SHERLOCK); + cmd.arg("Sherlock"); + cmd.current_dir(dir.path().join("foo").join("bar")); + + 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.stdout()); +}); + +// Like ignore_git_parent_stop, but with a .git file instead of a .git +// directory. +rgtest!(ignore_git_parent_stop_file, |dir: Dir, mut cmd: TestCommand| { + // This tests that searching parent directories for .gitignore files stops + // after it sees a .git *file*. A .git file is used for submodules. To test + // this, we create this directory hierarchy: + // + // .gitignore (contains `sherlock`) + // foo/ + // .git + // bar/ + // sherlock + // + // And we perform the search inside `foo/bar/`. ripgrep will stop looking + // for .gitignore files after it sees `foo/.git`, and therefore not + // respect the top-level `.gitignore` containing `sherlock`. + dir.create(".gitignore", "sherlock\n"); + dir.create_dir("foo"); + dir.create("foo/.git", ""); + dir.create_dir("foo/bar"); + dir.create("foo/bar/sherlock", SHERLOCK); + cmd.arg("Sherlock"); + cmd.current_dir(dir.path().join("foo").join("bar")); + + 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.stdout()); +}); + +rgtest!(ignore_ripgrep_parent_no_stop, |dir: Dir, mut cmd: TestCommand| { + // This is like the `ignore_git_parent_stop` test, except it checks that + // ripgrep *doesn't* stop checking for .rgignore files. + dir.create(".rgignore", "sherlock\n"); + dir.create_dir("foo"); + dir.create_dir("foo/.git"); + dir.create_dir("foo/bar"); + dir.create("foo/bar/sherlock", SHERLOCK); + cmd.arg("Sherlock"); + cmd.current_dir(dir.path().join("foo").join("bar")); + + // The top-level .rgignore applies. + cmd.assert_err(); +}); + +rgtest!(no_parent_ignore_git, |dir: Dir, mut cmd: TestCommand| { + // Set up a directory hierarchy like this: + // + // .git/ + // .gitignore + // foo/ + // .gitignore + // sherlock + // watson + // + // Where `.gitignore` contains `sherlock` and `foo/.gitignore` contains + // `watson`. + // + // Now *do the search* from the foo directory. By default, ripgrep will + // search parent directories for .gitignore files. The --no-ignore-parent + // flag should prevent that. At the same time, the `foo/.gitignore` file + // will still be respected (since the search is happening in `foo/`). + // + // In other words, we should only see results from `sherlock`, not from + // `watson`. + dir.create_dir(".git"); + dir.create(".gitignore", "sherlock\n"); + dir.create_dir("foo"); + dir.create("foo/.gitignore", "watson\n"); + dir.create("foo/sherlock", SHERLOCK); + dir.create("foo/watson", SHERLOCK); + cmd.arg("--no-ignore-parent").arg("Sherlock"); + cmd.current_dir(dir.path().join("foo")); + + 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.stdout()); +}); + +rgtest!(symlink_nofollow, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("foo"); + dir.create_dir("foo/bar"); + dir.link_dir("foo/baz", "foo/bar/baz"); + dir.create_dir("foo/baz"); + dir.create("foo/baz/sherlock", SHERLOCK); + cmd.arg("Sherlock"); + cmd.current_dir(dir.path().join("foo/bar")); + + cmd.assert_err(); +}); + +#[cfg(not(windows))] +rgtest!(symlink_follow, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("foo"); + dir.create_dir("foo/bar"); + dir.create_dir("foo/baz"); + dir.create("foo/baz/sherlock", SHERLOCK); + dir.link_dir("foo/baz", "foo/bar/baz"); + cmd.arg("-L").arg("Sherlock"); + cmd.current_dir(dir.path().join("foo/bar")); + + let expected = "\ +baz/sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock +baz/sherlock:be, to a very large extent, the result of luck. Sherlock Holmes +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(unrestricted1, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create(".gitignore", "sherlock\n"); + cmd.arg("-u").arg("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.stdout()); +}); + +rgtest!(unrestricted2, |dir: Dir, mut cmd: TestCommand| { + dir.create(".sherlock", SHERLOCK); + cmd.arg("-uu").arg("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.stdout()); +}); + +rgtest!(unrestricted3, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("file", "foo\x00bar\nfoo\x00baz\n"); + cmd.arg("-uuu").arg("foo"); + + let expected = "\ +file:foo\x00bar +file:foo\x00baz +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(vimgrep, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--vimgrep").arg("Sherlock|Watson"); + + let expected = "\ +sherlock:1:16:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:1:57:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:3:49:be, to a very large extent, the result of luck. Sherlock Holmes +sherlock:5:12:but Doctor Watson has to have it taken out for him and dusted, +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(vimgrep_no_line, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--vimgrep").arg("-N").arg("Sherlock|Watson"); + + let expected = "\ +sherlock:16:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:57:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:49:be, to a very large extent, the result of luck. Sherlock Holmes +sherlock:12:but Doctor Watson has to have it taken out for him and dusted, +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(vimgrep_no_line_no_column, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.arg("--vimgrep").arg("-N").arg("--no-column").arg("Sherlock|Watson"); + + let expected = "\ +sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock +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 +sherlock:but Doctor Watson has to have it taken out for him and dusted, +"; + eqnice!(expected, cmd.stdout()); +}); + +rgtest!(preprocessing, |dir: Dir, mut cmd: TestCommand| { + if !cmd_exists("xzcat") { + return; + } + + dir.create_bytes("sherlock.xz", include_bytes!("./data/sherlock.xz")); + cmd.arg("--pre").arg("xzcat").arg("Sherlock").arg("sherlock.xz"); + + 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()); +}); + +rgtest!(compressed_gzip, |dir: Dir, mut cmd: TestCommand| { + if !cmd_exists("gzip") { + return; + } + + dir.create_bytes("sherlock.gz", include_bytes!("./data/sherlock.gz")); + cmd.arg("-z").arg("Sherlock").arg("sherlock.gz"); + + 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()); +}); + +rgtest!(compressed_bzip2, |dir: Dir, mut cmd: TestCommand| { + if !cmd_exists("bzip2") { + return; + } + + dir.create_bytes("sherlock.bz2", include_bytes!("./data/sherlock.bz2")); + cmd.arg("-z").arg("Sherlock").arg("sherlock.bz2"); + + 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()); +}); + +rgtest!(compressed_xz, |dir: Dir, mut cmd: TestCommand| { + if !cmd_exists("xz") { + return; + } + + dir.create_bytes("sherlock.xz", include_bytes!("./data/sherlock.xz")); + cmd.arg("-z").arg("Sherlock").arg("sherlock.xz"); + + 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()); +}); + +rgtest!(compressed_lz4, |dir: Dir, mut cmd: TestCommand| { + if !cmd_exists("lz4") { + return; + } + + dir.create_bytes("sherlock.lz4", include_bytes!("./data/sherlock.lz4")); + cmd.arg("-z").arg("Sherlock").arg("sherlock.lz4"); + + 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()); +}); + +rgtest!(compressed_lzma, |dir: Dir, mut cmd: TestCommand| { + if !cmd_exists("xz") { + return; + } + + dir.create_bytes("sherlock.lzma", include_bytes!("./data/sherlock.lzma")); + cmd.arg("-z").arg("Sherlock").arg("sherlock.lzma"); + + 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()); +}); + +rgtest!(compressed_failing_gzip, |dir: Dir, mut cmd: TestCommand| { + if !cmd_exists("gzip") { + return; + } + + dir.create("sherlock.gz", SHERLOCK); + cmd.arg("-z").arg("Sherlock").arg("sherlock.gz"); + + cmd.assert_non_empty_stderr(); +}); + +rgtest!(binary_nosearch, |dir: Dir, mut cmd: TestCommand| { + dir.create("file", "foo\x00bar\nfoo\x00baz\n"); + cmd.arg("foo").arg("file"); + + cmd.assert_err(); +}); + +// The following two tests show a discrepancy in search results between +// searching with memory mapped files and stream searching. Stream searching +// uses a heuristic (that GNU grep also uses) where NUL bytes are replaced with +// the EOL terminator, which tends to avoid allocating large amounts of memory +// for really long "lines." The memory map searcher has no need to worry about +// such things, and more than that, it would be pretty hard for it to match the +// semantics of streaming search in this case. +// +// Binary files with lots of NULs aren't really part of the use case of ripgrep +// (or any other grep-like tool for that matter), so we shouldn't feel too bad +// about it. +rgtest!(binary_search_mmap, |dir: Dir, mut cmd: TestCommand| { + dir.create("file", "foo\x00bar\nfoo\x00baz\n"); + cmd.arg("-a").arg("--mmap").arg("foo").arg("file"); + eqnice!("foo\x00bar\nfoo\x00baz\n", cmd.stdout()); +}); + +rgtest!(binary_search_no_mmap, |dir: Dir, mut cmd: TestCommand| { + dir.create("file", "foo\x00bar\nfoo\x00baz\n"); + cmd.arg("-a").arg("--no-mmap").arg("foo").arg("file"); + eqnice!("foo\x00bar\nfoo\x00baz\n", cmd.stdout()); +}); + +rgtest!(files, |dir: Dir, mut cmd: TestCommand| { + dir.create("file", ""); + dir.create_dir("dir"); + dir.create("dir/file", ""); + cmd.arg("--files"); + + eqnice!(sort_lines("file\ndir/file\n"), sort_lines(&cmd.stdout())); +}); + +rgtest!(type_list, |_: Dir, mut cmd: TestCommand| { + cmd.arg("--type-list"); + // This can change over time, so just make sure we print something. + assert!(!cmd.stdout().is_empty()); +}); diff --git a/tests/multiline.rs b/tests/multiline.rs new file mode 100644 index 00000000..5a569358 --- /dev/null +++ b/tests/multiline.rs @@ -0,0 +1,109 @@ +use hay::SHERLOCK; +use util::{Dir, TestCommand}; + +// This tests that multiline matches that span multiple lines, but where +// multiple matches may begin and end on the same line work correctly. +rgtest!(overlap1, |dir: Dir, mut cmd: TestCommand| { + dir.create("test", "xxx\nabc\ndefxxxabc\ndefxxx\nxxx"); + cmd.arg("-n").arg("-U").arg("abc\ndef").arg("test"); + eqnice!("2:abc\n3:defxxxabc\n4:defxxx\n", cmd.stdout()); +}); + +// Like overlap1, but tests the case where one match ends at precisely the same +// location at which the next match begins. +rgtest!(overlap2, |dir: Dir, mut cmd: TestCommand| { + dir.create("test", "xxx\nabc\ndefabc\ndefxxx\nxxx"); + cmd.arg("-n").arg("-U").arg("abc\ndef").arg("test"); + eqnice!("2:abc\n3:defabc\n4:defxxx\n", cmd.stdout()); +}); + +// Tests that even in a multiline search, a '.' does not match a newline. +rgtest!(dot_no_newline, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-n", "-U", "of this world.+detective work", "sherlock", + ]); + cmd.assert_err(); +}); + +// Tests that the --multiline-dotall flag causes '.' to match a newline. +rgtest!(dot_all, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-n", "-U", "--multiline-dotall", + "of this world.+detective work", "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, cmd.stdout()); +}); + +// Tests that --only-matching works in multiline mode. +rgtest!(only_matching, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-n", "-U", "--only-matching", + r"Watson|Sherlock\p{Any}+?Holmes", "sherlock", + ]); + + let expected = "\ +1:Watson +1:Sherlock +2:Holmes +3:Sherlock Holmes +5:Watson +"; + eqnice!(expected, cmd.stdout()); +}); + +// Tests that --vimgrep works in multiline mode. +rgtest!(vimgrep, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-n", "-U", "--vimgrep", + r"Watson|Sherlock\p{Any}+?Holmes", "sherlock", + ]); + + let expected = "\ +sherlock:1:16:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:1:57:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:2:57:Holmeses, success in the province of detective work must always +sherlock:3:49:be, to a very large extent, the result of luck. Sherlock Holmes +sherlock:5:12:but Doctor Watson has to have it taken out for him and dusted, +"; + eqnice!(expected, cmd.stdout()); +}); + +// Tests that multiline search works when reading from stdin. This is an +// important test because multiline search must read the entire contents of +// what it is searching into memory before executing the search. +rgtest!(stdin, |_: Dir, mut cmd: TestCommand| { + cmd.args(&[ + "-n", "-U", r"of this world\p{Any}+?detective work", + ]); + 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, cmd.pipe(SHERLOCK)); +}); + +// Test that multiline search and contextual matches work. +rgtest!(context, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + cmd.args(&[ + "-n", "-U", "-C1", + r"detective work\p{Any}+?result of luck", "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 +3:be, to a very large extent, the result of luck. Sherlock Holmes +4-can extract a clew from a wisp of straw or a flake of cigar ash; +"; + eqnice!(expected, cmd.stdout()); +}); diff --git a/tests/regression.rs b/tests/regression.rs new file mode 100644 index 00000000..f2553d96 --- /dev/null +++ b/tests/regression.rs @@ -0,0 +1,564 @@ +use hay::SHERLOCK; +use util::{Dir, TestCommand, sort_lines}; + +// See: https://github.com/BurntSushi/ripgrep/issues/16 +rgtest!(r16, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "ghi/"); + dir.create_dir("ghi"); + dir.create_dir("def/ghi"); + dir.create("ghi/toplevel.txt", "xyz"); + dir.create("def/ghi/subdir.txt", "xyz"); + + cmd.arg("xyz").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/25 +rgtest!(r25, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "/llvm/"); + dir.create_dir("src/llvm"); + dir.create("src/llvm/foo", "test"); + + cmd.arg("test"); + eqnice!("src/llvm/foo:test\n", cmd.stdout()); + + cmd.current_dir(dir.path().join("src")); + eqnice!("llvm/foo:test\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/30 +rgtest!(r30, |dir: Dir, mut cmd: TestCommand| { + dir.create(".gitignore", "vendor/**\n!vendor/manifest"); + dir.create_dir("vendor"); + dir.create("vendor/manifest", "test"); + + eqnice!("vendor/manifest:test\n", cmd.arg("test").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/49 +rgtest!(r49, |dir: Dir, mut cmd: TestCommand| { + dir.create(".gitignore", "foo/bar"); + dir.create_dir("test/foo/bar"); + dir.create("test/foo/bar/baz", "test"); + + cmd.arg("xyz").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/50 +rgtest!(r50, |dir: Dir, mut cmd: TestCommand| { + dir.create(".gitignore", "XXX/YYY/"); + dir.create_dir("abc/def/XXX/YYY"); + dir.create_dir("ghi/XXX/YYY"); + dir.create("abc/def/XXX/YYY/bar", "test"); + dir.create("ghi/XXX/YYY/bar", "test"); + + cmd.arg("xyz").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/64 +rgtest!(r64, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("dir"); + dir.create_dir("foo"); + dir.create("dir/abc", ""); + dir.create("foo/abc", ""); + + eqnice!("foo/abc\n", cmd.arg("--files").arg("foo").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/65 +rgtest!(r65, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "a/"); + dir.create_dir("a"); + dir.create("a/foo", "xyz"); + dir.create("a/bar", "xyz"); + + cmd.arg("xyz").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/67 +rgtest!(r67, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "/*\n!/dir"); + dir.create_dir("dir"); + dir.create_dir("foo"); + dir.create("foo/bar", "test"); + dir.create("dir/bar", "test"); + + eqnice!("dir/bar:test\n", cmd.arg("test").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/87 +rgtest!(r87, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "foo\n**no-vcs**"); + dir.create("foo", "test"); + + cmd.arg("test").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/90 +rgtest!(r90, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "!.foo"); + dir.create(".foo", "test"); + + eqnice!(".foo:test\n", cmd.arg("test").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/93 +rgtest!(r93, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "192.168.1.1"); + + eqnice!("foo:192.168.1.1\n", cmd.arg(r"(\d{1,3}\.){3}\d{1,3}").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/99 +rgtest!(r99, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo1", "test"); + dir.create("foo2", "zzz"); + dir.create("bar", "test"); + + eqnice!( + sort_lines("bar\ntest\n\nfoo1\ntest\n"), + sort_lines(&cmd.arg("-j1").arg("--heading").arg("test").stdout()) + ); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/105 +rgtest!(r105_part1, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "zztest"); + + eqnice!("foo:1:3:zztest\n", cmd.arg("--vimgrep").arg("test").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/105 +rgtest!(r105_part2, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "zztest"); + + eqnice!("foo:1:3:zztest\n", cmd.arg("--column").arg("test").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/127 +rgtest!(r127, |dir: Dir, mut cmd: TestCommand| { + // Set up a directory hierarchy like this: + // + // .gitignore + // foo/ + // sherlock + // watson + // + // Where `.gitignore` contains `foo/sherlock`. + // + // ripgrep should ignore 'foo/sherlock' giving us results only from + // 'foo/watson' but on Windows ripgrep will include both 'foo/sherlock' and + // 'foo/watson' in the search results. + dir.create_dir(".git"); + dir.create(".gitignore", "foo/sherlock\n"); + dir.create_dir("foo"); + dir.create("foo/sherlock", SHERLOCK); + dir.create("foo/watson", SHERLOCK); + + let expected = "\ +foo/watson:For the Doctor Watsons of this world, as opposed to the Sherlock +foo/watson:be, to a very large extent, the result of luck. Sherlock Holmes +"; + assert_eq!(expected, cmd.arg("Sherlock").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/128 +rgtest!(r128, |dir: Dir, mut cmd: TestCommand| { + dir.create_bytes("foo", b"01234567\x0b\n\x0b\n\x0b\n\x0b\nx"); + + eqnice!("foo:5:x\n", cmd.arg("-n").arg("x").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/131 +// +// TODO(burntsushi): Darwin doesn't like this test for some reason. Probably +// due to the weird file path. +#[cfg(not(target_os = "macos"))] +rgtest!(r131, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "TopÑapa"); + dir.create("TopÑapa", "test"); + + cmd.arg("test").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/137 +// +// TODO(burntsushi): Figure out how to make this test work on Windows. Right +// now it gives "access denied" errors when trying to create a file symlink. +// For now, disable test on Windows. +#[cfg(not(windows))] +rgtest!(r137, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.link_file("sherlock", "sym1"); + dir.link_file("sherlock", "sym2"); + + 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 +sym1:For the Doctor Watsons of this world, as opposed to the Sherlock +sym1:be, to a very large extent, the result of luck. Sherlock Holmes +sym2:For the Doctor Watsons of this world, as opposed to the Sherlock +sym2:be, to a very large extent, the result of luck. Sherlock Holmes +"; + cmd.arg("-j1").arg("Sherlock").arg("./").arg("sym1").arg("sym2"); + eqnice!(expected, cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/156 +rgtest!(r156, |dir: Dir, mut cmd: TestCommand| { + let expected = r#"#parse('widgets/foo_bar_macros.vm') +#parse ( 'widgets/mobile/foo_bar_macros.vm' ) +#parse ("widgets/foobarhiddenformfields.vm") +#parse ( "widgets/foo_bar_legal.vm" ) +#include( 'widgets/foo_bar_tips.vm' ) +#include('widgets/mobile/foo_bar_macros.vm') +#include ("widgets/mobile/foo_bar_resetpw.vm") +#parse('widgets/foo-bar-macros.vm') +#parse ( 'widgets/mobile/foo-bar-macros.vm' ) +#parse ("widgets/foo-bar-hiddenformfields.vm") +#parse ( "widgets/foo-bar-legal.vm" ) +#include( 'widgets/foo-bar-tips.vm' ) +#include('widgets/mobile/foo-bar-macros.vm') +#include ("widgets/mobile/foo-bar-resetpw.vm") +"#; + dir.create("testcase.txt", expected); + + cmd.arg("-N"); + cmd.arg(r#"#(?:parse|include)\s*\(\s*(?:"|')[./A-Za-z_-]+(?:"|')"#); + cmd.arg("testcase.txt"); + eqnice!(expected, cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/184 +rgtest!(r184, |dir: Dir, mut cmd: TestCommand| { + dir.create(".gitignore", ".*"); + dir.create_dir("foo/bar"); + dir.create("foo/bar/baz", "test"); + + cmd.arg("test"); + eqnice!("foo/bar/baz:test\n", cmd.stdout()); + + cmd.current_dir(dir.path().join("./foo/bar")); + eqnice!("baz:test\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/199 +rgtest!(r199, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "tEsT"); + + eqnice!("foo:tEsT\n", cmd.arg("--smart-case").arg(r"\btest\b").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/206 +rgtest!(r206, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("foo"); + dir.create("foo/bar.txt", "test"); + + cmd.arg("test").arg("-g").arg("*.txt"); + eqnice!("foo/bar.txt:test\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/210 +#[cfg(unix)] +rgtest!(r210, |dir: Dir, mut cmd: TestCommand| { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + let badutf8 = OsStr::from_bytes(&b"foo\xffbar"[..]); + + // APFS does not support creating files with invalid UTF-8 bytes. + // https://github.com/BurntSushi/ripgrep/issues/559 + if dir.try_create(badutf8, "test").is_ok() { + cmd.arg("-H").arg("test").arg(badutf8); + assert_eq!(b"foo\xffbar:test\n".to_vec(), cmd.output().stdout); + } +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/228 +rgtest!(r228, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("foo"); + + cmd.arg("--ignore-file").arg("foo").arg("test").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/229 +rgtest!(r229, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "economie"); + + cmd.arg("-S").arg("[E]conomie").assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/251 +rgtest!(r251, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "привет\nПривет\nПрИвЕт"); + + let expected = "foo:привет\nfoo:Привет\nfoo:ПрИвЕт\n"; + eqnice!(expected, cmd.arg("-i").arg("привет").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/256 +#[cfg(not(windows))] +rgtest!(r256, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("bar"); + dir.create("bar/baz", "test"); + dir.link_dir("bar", "foo"); + + eqnice!("foo/baz:test\n", cmd.arg("test").arg("foo").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/256 +#[cfg(not(windows))] +rgtest!(r256_j1, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("bar"); + dir.create("bar/baz", "test"); + dir.link_dir("bar", "foo"); + + eqnice!("foo/baz:test\n", cmd.arg("-j1").arg("test").arg("foo").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/270 +rgtest!(r270, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "-test"); + + cmd.arg("-e").arg("-test"); + eqnice!("foo:-test\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/279 +rgtest!(r279, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "test"); + + eqnice!("", cmd.arg("-q").arg("test").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/391 +rgtest!(r391, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create("lock", ""); + dir.create("bar.py", ""); + dir.create(".git/packed-refs", ""); + dir.create(".git/description", ""); + + cmd.args(&[ + "--no-ignore", "--hidden", "--follow", "--files", + "--glob", + "!{.git,node_modules,plugged}/**", + "--glob", + "*.{js,json,php,md,styl,scss,sass,pug,html,config,py,cpp,c,go,hs}", + ]); + eqnice!("bar.py\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/405 +rgtest!(r405, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir("foo/bar"); + dir.create_dir("bar/foo"); + dir.create("foo/bar/file1.txt", "test"); + dir.create("bar/foo/file2.txt", "test"); + + cmd.arg("-g").arg("!/foo/**").arg("test"); + eqnice!("bar/foo/file2.txt:test\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/428 +#[cfg(not(windows))] +rgtest!(r428_color_context_path, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", "foo\nbar"); + cmd.args(&[ + "-A1", "-H", "--no-heading", "-N", + "--colors=match:none", "--color=always", + "foo", + ]); + + let expected = format!( + "{colored_path}:foo\n{colored_path}-bar\n", + colored_path= + "\x1b\x5b\x30\x6d\x1b\x5b\x33\x35\x6dsherlock\x1b\x5b\x30\x6d" + ); + eqnice!(expected, cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/428 +rgtest!(r428_unrecognized_style, |_: Dir, mut cmd: TestCommand| { + cmd.arg("--colors=match:style:").arg("Sherlock"); + cmd.assert_err(); + + let output = cmd.cmd().output().unwrap(); + let stderr = String::from_utf8_lossy(&output.stderr); + let expected = "\ +unrecognized style attribute ''. Choose from: nobold, bold, nointense, \ +intense, nounderline, underline. +"; + eqnice!(expected, stderr); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/451 +rgtest!(r451_only_matching_as_in_issue, |dir: Dir, mut cmd: TestCommand| { + dir.create("digits.txt", "1 2 3\n"); + cmd.arg("--only-matching").arg(r"[0-9]+").arg("digits.txt"); + + let expected = "\ +1 +2 +3 +"; + eqnice!(expected, cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/451 +rgtest!(r451_only_matching, |dir: Dir, mut cmd: TestCommand| { + dir.create("digits.txt", "1 2 3\n123\n"); + cmd.args(&[ + "--only-matching", "--column", r"[0-9]", "digits.txt", + ]); + + let expected = "\ +1:1:1 +1:3:2 +1:5:3 +2:1:1 +2:2:2 +2:3:3 +"; + eqnice!(expected, cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/483 +rgtest!(r483_matching_no_stdout, |dir: Dir, mut cmd: TestCommand| { + dir.create("file.py", ""); + cmd.arg("--quiet").arg("--files").arg("--glob").arg("*.py"); + eqnice!("", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/483 +rgtest!(r483_non_matching_exit_code, |dir: Dir, mut cmd: TestCommand| { + dir.create("file.rs", ""); + cmd.arg("--quiet").arg("--files").arg("--glob").arg("*.py"); + cmd.assert_err(); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/493 +rgtest!(r493, |dir: Dir, mut cmd: TestCommand| { + dir.create("input.txt", "peshwaship 're seminomata"); + + cmd.arg("-o").arg(r"\b 're \b").arg("input.txt"); + assert_eq!(" 're \n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/506 +rgtest!(r506_word_not_parenthesized, |dir: Dir, mut cmd: TestCommand| { + dir.create("wb.txt", "min minimum amin\nmax maximum amax"); + cmd.arg("-w").arg("-o").arg("min|max").arg("wb.txt"); + eqnice!("min\nmax\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/553 +rgtest!(r553_switch, |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 +"; + cmd.arg("-i").arg("sherlock"); + eqnice!(expected, cmd.stdout()); + + // Repeat the `i` flag to make sure everything still works. + eqnice!(expected, cmd.arg("-i").stdout()); +}); + +rgtest!(r553_flag, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + + let expected = "\ +For the Doctor Watsons of this world, as opposed to the Sherlock +Holmeses, success in the province of detective work must always +-- +but Doctor Watson has to have it taken out for him and dusted, +and exhibited clearly, with a label attached. +"; + cmd.arg("-C").arg("1").arg(r"world|attached").arg("sherlock"); + eqnice!(expected, cmd.stdout()); + + let expected = "\ +For the Doctor Watsons of this world, as opposed to the Sherlock +and exhibited clearly, with a label attached. +"; + eqnice!(expected, cmd.arg("-C").arg("0").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/568 +rgtest!(r568_leading_hyphen_option_args, |dir: Dir, mut cmd: TestCommand| { + dir.create("file", "foo bar -baz\n"); + cmd.arg("-e-baz").arg("-e").arg("-baz").arg("file"); + eqnice!("foo bar -baz\n", cmd.stdout()); + + let mut cmd = dir.command(); + cmd.arg("-rni").arg("bar").arg("file"); + eqnice!("foo ni -baz\n", cmd.stdout()); + + let mut cmd = dir.command(); + cmd.arg("-r").arg("-n").arg("-i").arg("bar").arg("file"); + eqnice!("foo -n -baz\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/599 +// +// This test used to check that we emitted color escape sequences even for +// empty matches, but with the addition of the JSON output format, clients no +// longer need to rely on escape sequences to parse matches. Therefore, we no +// longer emit useless escape sequences. +rgtest!(r599, |dir: Dir, mut cmd: TestCommand| { + dir.create("input.txt", "\n\ntest\n"); + cmd.args(&[ + "--color", "ansi", + "--colors", "path:none", + "--colors", "line:none", + "--colors", "match:fg:red", + "--colors", "match:style:nobold", + "--line-number", + r"^$", + "input.txt", + ]); + + let expected = "\ +1: +2: +"; + eqnice_repr!(expected, cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/693 +rgtest!(r693_context_in_contextless_mode, |dir: Dir, mut cmd: TestCommand| { + dir.create("foo", "xyz\n"); + dir.create("bar", "xyz\n"); + + cmd.arg("-C1").arg("-c").arg("--sort-files").arg("xyz"); + eqnice!("bar:1\nfoo:1\n", cmd.stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/807 +rgtest!(r807, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", ".a/b"); + dir.create_dir(".a/b"); + dir.create_dir(".a/c"); + dir.create(".a/b/file", "test"); + dir.create(".a/c/file", "test"); + + eqnice!(".a/c/file:test\n", cmd.arg("--hidden").arg("test").stdout()); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/900 +rgtest!(r900, |dir: Dir, mut cmd: TestCommand| { + dir.create("sherlock", SHERLOCK); + dir.create("pat", ""); + + cmd.arg("-fpat").arg("sherlock").assert_err(); +}); diff --git a/tests/tests.rs b/tests/tests.rs index 1c40f22e..ceff7bc2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,2298 +1,25 @@ -/*! -This module contains *integration* tests. Their purpose is to test the CLI -interface. Namely, that passing a flag does what it says on the tin. +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate serde_json; -Tests for more fine grained behavior (like the search or the globber) should be -unit tests in their respective modules. -*/ - -#![allow(dead_code, unused_imports)] - -use std::process::Command; - -use workdir::WorkDir; +// Macros useful for testing. +#[macro_use] +mod macros; +// Corpora. mod hay; -mod workdir; - -macro_rules! sherlock { - ($name:ident, $fun:expr) => { - sherlock!($name, "Sherlock", $fun); - }; - ($name:ident, $query:expr, $fun:expr) => { - sherlock!($name, $query, "sherlock", $fun); - }; - ($name:ident, $query:expr, $path:expr, $fun:expr) => { - #[test] - fn $name() { - let wd = WorkDir::new(stringify!($name)); - wd.create("sherlock", hay::SHERLOCK); - let mut cmd = wd.command(); - cmd.arg($query).arg($path); - $fun(wd, cmd); - } - }; -} - -macro_rules! clean { - ($name:ident, $query:expr, $path:expr, $fun:expr) => { - #[test] - fn $name() { - let wd = WorkDir::new(stringify!($name)); - let mut cmd = wd.command(); - cmd.arg($query).arg($path); - $fun(wd, cmd); - } - }; -} - -fn path(unix: &str) -> String { - if cfg!(windows) { - unix.replace("/", "\\") - } else { - unix.to_string() - } -} - -fn paths(unix: &[&str]) -> Vec { - let mut xs: Vec<_> = unix.iter().map(|s| path(s)).collect(); - xs.sort(); - xs -} - -fn paths_from_stdout(stdout: String) -> Vec { - let mut paths: Vec<_> = stdout.lines().map(|s| { - s.split(':').next().unwrap().to_string() - }).collect(); - paths.sort(); - paths -} - -fn sort_lines(lines: &str) -> String { - let mut lines: Vec = - lines.trim().lines().map(|s| s.to_owned()).collect(); - lines.sort(); - format!("{}\n", lines.join("\n")) -} - -fn cmd_exists(name: &str) -> bool { - Command::new(name).arg("--help").output().is_ok() -} - -sherlock!(single_file, |wd: WorkDir, mut cmd| { - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(dir, "Sherlock", ".", |wd: WorkDir, mut cmd| { - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(line_numbers, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-n"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -1:For the Doctor Watsons of this world, as opposed to the Sherlock -3:be, to a very large extent, the result of luck. Sherlock Holmes -"; - assert_eq!(lines, expected); -}); - -sherlock!(columns, |wd: WorkDir, mut cmd: Command| { - cmd.arg("--column"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -1:57:For the Doctor Watsons of this world, as opposed to the Sherlock -3:49:be, to a very large extent, the result of luck. Sherlock Holmes -"; - assert_eq!(lines, expected); -}); - -sherlock!(with_filename, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-H"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(with_heading, |wd: WorkDir, mut cmd: Command| { - // This forces the issue since --with-filename is disabled by default - // when searching one file. - cmd.arg("--with-filename").arg("--heading"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -sherlock -For the Doctor Watsons of this world, as opposed to the Sherlock -be, to a very large extent, the result of luck. Sherlock Holmes -"; - assert_eq!(lines, expected); -}); - -sherlock!(with_heading_default, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - // Search two or more and get --with-filename enabled by default. - // Use -j1 to get deterministic results. - wd.create("foo", "Sherlock Holmes lives on Baker Street."); - cmd.arg("-j1").arg("--heading"); - let lines: String = wd.stdout(&mut cmd); - let expected1 = "\ -./foo -Sherlock Holmes lives on Baker Street. - -./sherlock -For the Doctor Watsons of this world, as opposed to the Sherlock -be, to a very large extent, the result of luck. Sherlock Holmes -"; - let expected2 = "\ -./sherlock -For the Doctor Watsons of this world, as opposed to the Sherlock -be, to a very large extent, the result of luck. Sherlock Holmes - -./foo -Sherlock Holmes lives on Baker Street. -"; - if lines != expected1 { - assert_eq!(lines, expected2); - } else { - assert_eq!(lines, expected1); - } -}); - -sherlock!(inverted, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-v"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -Holmeses, success in the province of detective work must always -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. -"; - assert_eq!(lines, expected); -}); - -sherlock!(inverted_line_numbers, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-n").arg("-v"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -2:Holmeses, success in the province of detective work must always -4:can extract a clew from a wisp of straw or a flake of cigar ash; -5:but Doctor Watson has to have it taken out for him and dusted, -6:and exhibited clearly, with a label attached. -"; - assert_eq!(lines, expected); -}); - -sherlock!(case_insensitive, "sherlock", |wd: WorkDir, mut cmd: Command| { - cmd.arg("-i"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(word, "as", |wd: WorkDir, mut cmd: Command| { - cmd.arg("-w"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Doctor Watsons of this world, as opposed to the Sherlock -"; - assert_eq!(lines, expected); -}); - -sherlock!(line, "Watson|and exhibited clearly, with a label attached.", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-x"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -and exhibited clearly, with a label attached. -"; - assert_eq!(lines, expected); -}); - -sherlock!(literal, "()", "file", |wd: WorkDir, mut cmd: Command| { - wd.create("file", "blib\n()\nblab\n"); - cmd.arg("-F"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "()\n"); -}); - -sherlock!(quiet, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-q"); - let lines: String = wd.stdout(&mut cmd); - assert!(lines.is_empty()); -}); - -sherlock!(replace, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-r").arg("FooBar"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Doctor Watsons of this world, as opposed to the FooBar -be, to a very large extent, the result of luck. FooBar Holmes -"; - assert_eq!(lines, expected); -}); - -sherlock!(replace_groups, "([A-Z][a-z]+) ([A-Z][a-z]+)", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-r").arg("$2, $1"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Watsons, Doctor of this world, as opposed to the Sherlock -be, to a very large extent, the result of luck. Holmes, Sherlock -but Watson, Doctor has to have it taken out for him and dusted, -"; - assert_eq!(lines, expected); -}); - -sherlock!(replace_named_groups, "(?P[A-Z][a-z]+) (?P[A-Z][a-z]+)", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-r").arg("$last, $first"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Watsons, Doctor of this world, as opposed to the Sherlock -be, to a very large extent, the result of luck. Holmes, Sherlock -but Watson, Doctor has to have it taken out for him and dusted, -"; - assert_eq!(lines, expected); -}); - -sherlock!(replace_with_only_matching, "of (\\w+)", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-o").arg("-r").arg("$1"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -this -detective -luck -straw -cigar -"; - assert_eq!(lines, expected); -}); - -sherlock!(file_types, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "Sherlock"); - wd.create("file.rs", "Sherlock"); - cmd.arg("-t").arg("rust"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.rs:Sherlock\n"); -}); - -sherlock!(file_types_all, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "Sherlock"); - cmd.arg("-t").arg("all"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.py:Sherlock\n"); -}); - -sherlock!(file_types_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create("file.py", "Sherlock"); - wd.create("file.rs", "Sherlock"); - cmd.arg("-T").arg("rust"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.py:Sherlock\n"); -}); - -sherlock!(file_types_negate_all, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "Sherlock"); - cmd.arg("-T").arg("all"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "\ -./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 -"); -}); - -sherlock!(file_type_clear, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "Sherlock"); - wd.create("file.rs", "Sherlock"); - cmd.arg("--type-clear").arg("rust").arg("-t").arg("rust"); - wd.assert_err(&mut cmd); -}); - -sherlock!(file_type_add, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "Sherlock"); - wd.create("file.rs", "Sherlock"); - wd.create("file.wat", "Sherlock"); - cmd.arg("--type-add").arg("wat:*.wat").arg("-t").arg("wat"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.wat:Sherlock\n"); -}); - -sherlock!(file_type_add_compose, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "Sherlock"); - wd.create("file.rs", "Sherlock"); - wd.create("file.wat", "Sherlock"); - cmd.arg("--type-add").arg("wat:*.wat"); - cmd.arg("--type-add").arg("combo:include:wat,py").arg("-t").arg("combo"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!( - sort_lines(&lines), - "./file.py:Sherlock\n./file.wat:Sherlock\n" - ); -}); - -sherlock!(glob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "Sherlock"); - wd.create("file.rs", "Sherlock"); - cmd.arg("-g").arg("*.rs"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.rs:Sherlock\n"); -}); - -sherlock!(glob_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create("file.py", "Sherlock"); - wd.create("file.rs", "Sherlock"); - cmd.arg("-g").arg("!*.rs"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.py:Sherlock\n"); -}); - -sherlock!(iglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file.HTML", "Sherlock"); - cmd.arg("--iglob").arg("*.html"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.HTML:Sherlock\n"); -}); - -sherlock!(csglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file1.HTML", "Sherlock"); - wd.create("file2.html", "Sherlock"); - cmd.arg("--glob").arg("*.html"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file2.html:Sherlock\n"); -}); - -sherlock!(byte_offset_only_matching, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-b").arg("-o"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./sherlock:56:Sherlock -./sherlock:177:Sherlock -"; - assert_eq!(lines, expected); -}); - -sherlock!(count, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - cmd.arg("--count"); - let lines: String = wd.stdout(&mut cmd); - let expected = "./sherlock:2\n"; - assert_eq!(lines, expected); -}); - -sherlock!(count_matches, "the", ".", |wd: WorkDir, mut cmd: Command| { - cmd.arg("--count-matches"); - let lines: String = wd.stdout(&mut cmd); - let expected = "./sherlock:4\n"; - assert_eq!(lines, expected); -}); - -sherlock!(count_matches_inverted, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - cmd.arg("--count-matches").arg("--invert-match"); - let lines: String = wd.stdout(&mut cmd); - let expected = "./sherlock:4\n"; - assert_eq!(lines, expected); -}); - -sherlock!(count_matches_via_only, "the", ".", |wd: WorkDir, mut cmd: Command| { - cmd.arg("--count").arg("--only-matching"); - let lines: String = wd.stdout(&mut cmd); - let expected = "./sherlock:4\n"; - assert_eq!(lines, expected); -}); - -sherlock!(files_with_matches, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - cmd.arg("--files-with-matches"); - let lines: String = wd.stdout(&mut cmd); - let expected = "./sherlock\n"; - assert_eq!(lines, expected); -}); - -sherlock!(files_without_matches, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "foo"); - cmd.arg("--files-without-match"); - let lines: String = wd.stdout(&mut cmd); - let expected = "./file.py\n"; - assert_eq!(lines, expected); -}); - -sherlock!(after_context, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-A").arg("1"); - let lines: String = wd.stdout(&mut cmd); - 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 -can extract a clew from a wisp of straw or a flake of cigar ash; -"; - assert_eq!(lines, expected); -}); - -sherlock!(after_context_line_numbers, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-A").arg("1").arg("-n"); - let lines: String = wd.stdout(&mut cmd); - 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 -3:be, to a very large extent, the result of luck. Sherlock Holmes -4-can extract a clew from a wisp of straw or a flake of cigar ash; -"; - assert_eq!(lines, expected); -}); - -sherlock!(before_context, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-B").arg("1"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(before_context_line_numbers, |wd: WorkDir, mut cmd: Command| { - cmd.arg("-B").arg("1").arg("-n"); - let lines: String = wd.stdout(&mut cmd); - 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 -3:be, to a very large extent, the result of luck. Sherlock Holmes -"; - assert_eq!(lines, expected); -}); - -sherlock!(context, "world|attached", |wd: WorkDir, mut cmd: Command| { - cmd.arg("-C").arg("1"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always --- -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached. -"; - assert_eq!(lines, expected); -}); - -sherlock!(context_line_numbers, "world|attached", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-C").arg("1").arg("-n"); - let lines: String = wd.stdout(&mut cmd); - 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 --- -5-but Doctor Watson has to have it taken out for him and dusted, -6:and exhibited clearly, with a label attached. -"; - assert_eq!(lines, expected); -}); - -sherlock!(max_filesize_parse_error_length, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--max-filesize").arg("44444444444444444444"); - wd.assert_err(&mut cmd); -}); - -sherlock!(max_filesize_parse_error_suffix, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--max-filesize").arg("45k"); - wd.assert_err(&mut cmd); -}); - -sherlock!(max_filesize_parse_no_suffix, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create_size("foo", 40); - wd.create_size("bar", 60); - - cmd.arg("--max-filesize").arg("50").arg("--files"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./foo -"; - assert_eq!(lines, expected); -}); - -sherlock!(max_filesize_parse_k_suffix, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create_size("foo", 3048); - wd.create_size("bar", 4100); - - cmd.arg("--max-filesize").arg("4K").arg("--files"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./foo -"; - assert_eq!(lines, expected); -}); - -sherlock!(max_filesize_parse_m_suffix, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create_size("foo", 1000000); - wd.create_size("bar", 1400000); - - cmd.arg("--max-filesize").arg("1M").arg("--files"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./foo -"; - assert_eq!(lines, expected); -}); - -sherlock!(max_filesize_suffix_overflow, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create_size("foo", 1000000); - - // 2^35 * 2^30 would otherwise overflow - cmd.arg("--max-filesize").arg("34359738368G").arg("--files"); - wd.assert_err(&mut cmd); -}); - -sherlock!(ignore_hidden, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create(".sherlock", hay::SHERLOCK); - wd.assert_err(&mut cmd); -}); - -sherlock!(no_ignore_hidden, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create(".sherlock", hay::SHERLOCK); - - cmd.arg("--hidden"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(ignore_git, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", "sherlock\n"); - wd.assert_err(&mut cmd); -}); - -sherlock!(ignore_generic, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".ignore", "sherlock\n"); - wd.assert_err(&mut cmd); -}); - -sherlock!(ignore_ripgrep, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".rgignore", "sherlock\n"); - wd.assert_err(&mut cmd); -}); - -sherlock!(no_ignore, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", "sherlock\n"); - cmd.arg("--no-ignore"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(ignore_git_parent, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create(".gitignore", "sherlock\n"); - wd.create_dir(".git"); - wd.create_dir("foo"); - wd.create("foo/sherlock", hay::SHERLOCK); - // Even though we search in foo/, which has no .gitignore, ripgrep will - // search parent directories and respect the gitignore files found. - cmd.current_dir(wd.path().join("foo")); - wd.assert_err(&mut cmd); -}); - -sherlock!(ignore_git_parent_stop, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - // This tests that searching parent directories for .gitignore files stops - // after it sees a .git directory. To test this, we create this directory - // hierarchy: - // - // .gitignore (contains `sherlock`) - // foo/ - // .git/ - // bar/ - // sherlock - // - // And we perform the search inside `foo/bar/`. ripgrep will stop looking - // for .gitignore files after it sees `foo/.git/`, and therefore not - // respect the top-level `.gitignore` containing `sherlock`. - wd.remove("sherlock"); - wd.create(".gitignore", "sherlock\n"); - wd.create_dir("foo"); - wd.create_dir("foo/.git"); - wd.create_dir("foo/bar"); - wd.create("foo/bar/sherlock", hay::SHERLOCK); - cmd.current_dir(wd.path().join("foo").join("bar")); - - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -// Like ignore_git_parent_stop, but with a .git file instead of a .git -// directory. -sherlock!(ignore_git_parent_stop_file, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - // This tests that searching parent directories for .gitignore files stops - // after it sees a .git *file*. A .git file is used for submodules. To test - // this, we create this directory hierarchy: - // - // .gitignore (contains `sherlock`) - // foo/ - // .git - // bar/ - // sherlock - // - // And we perform the search inside `foo/bar/`. ripgrep will stop looking - // for .gitignore files after it sees `foo/.git`, and therefore not - // respect the top-level `.gitignore` containing `sherlock`. - wd.remove("sherlock"); - wd.create(".gitignore", "sherlock\n"); - wd.create_dir("foo"); - wd.create("foo/.git", ""); - wd.create_dir("foo/bar"); - wd.create("foo/bar/sherlock", hay::SHERLOCK); - cmd.current_dir(wd.path().join("foo").join("bar")); - - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(ignore_ripgrep_parent_no_stop, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - // This is like the `ignore_git_parent_stop` test, except it checks that - // ripgrep *doesn't* stop checking for .rgignore files. - wd.remove("sherlock"); - wd.create(".rgignore", "sherlock\n"); - wd.create_dir("foo"); - wd.create_dir("foo/.git"); - wd.create_dir("foo/bar"); - wd.create("foo/bar/sherlock", hay::SHERLOCK); - cmd.current_dir(wd.path().join("foo").join("bar")); - // The top-level .rgignore applies. - wd.assert_err(&mut cmd); -}); - -sherlock!(no_parent_ignore_git, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - // Set up a directory hierarchy like this: - // - // .git/ - // .gitignore - // foo/ - // .gitignore - // sherlock - // watson - // - // Where `.gitignore` contains `sherlock` and `foo/.gitignore` contains - // `watson`. - // - // Now *do the search* from the foo directory. By default, ripgrep will - // search parent directories for .gitignore files. The --no-ignore-parent - // flag should prevent that. At the same time, the `foo/.gitignore` file - // will still be respected (since the search is happening in `foo/`). - // - // In other words, we should only see results from `sherlock`, not from - // `watson`. - wd.remove("sherlock"); - wd.create_dir(".git"); - wd.create(".gitignore", "sherlock\n"); - wd.create_dir("foo"); - wd.create("foo/.gitignore", "watson\n"); - wd.create("foo/sherlock", hay::SHERLOCK); - wd.create("foo/watson", hay::SHERLOCK); - cmd.current_dir(wd.path().join("foo")); - cmd.arg("--no-ignore-parent"); - - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -#[cfg(not(windows))] -sherlock!(symlink_nofollow, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create_dir("foo"); - wd.create_dir("foo/bar"); - wd.link_dir("foo/baz", "foo/bar/baz"); - wd.create_dir("foo/baz"); - wd.create("foo/baz/sherlock", hay::SHERLOCK); - cmd.current_dir(wd.path().join("foo/bar")); - wd.assert_err(&mut cmd); -}); - -#[cfg(not(windows))] -sherlock!(symlink_follow, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create_dir("foo"); - wd.create_dir("foo/bar"); - wd.create_dir("foo/baz"); - wd.create("foo/baz/sherlock", hay::SHERLOCK); - wd.link_dir("foo/baz", "foo/bar/baz"); - cmd.arg("-L"); - cmd.current_dir(wd.path().join("foo/bar")); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./baz/sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock -./baz/sherlock:be, to a very large extent, the result of luck. Sherlock Holmes -"; - assert_eq!(lines, path(expected)); -}); - -sherlock!(unrestricted1, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", "sherlock\n"); - cmd.arg("-u"); - - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(unrestricted2, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.create(".sherlock", hay::SHERLOCK); - cmd.arg("-uu"); - - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(unrestricted3, "foo", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("file", "foo\x00bar\nfoo\x00baz\n"); - cmd.arg("-uuu"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file:foo\x00bar\n./file:foo\x00baz\n"); -}); - -sherlock!(vimgrep, "Sherlock|Watson", ".", |wd: WorkDir, mut cmd: Command| { - cmd.arg("--vimgrep"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./sherlock:1:16:For the Doctor Watsons of this world, as opposed to the Sherlock -./sherlock:1:57:For the Doctor Watsons of this world, as opposed to the Sherlock -./sherlock:3:49:be, to a very large extent, the result of luck. Sherlock Holmes -./sherlock:5:12:but Doctor Watson has to have it taken out for him and dusted, -"; - assert_eq!(lines, expected); -}); - -sherlock!(vimgrep_no_line, "Sherlock|Watson", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--vimgrep").arg("-N"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./sherlock:16:For the Doctor Watsons of this world, as opposed to the Sherlock -./sherlock:57:For the Doctor Watsons of this world, as opposed to the Sherlock -./sherlock:49:be, to a very large extent, the result of luck. Sherlock Holmes -./sherlock:12:but Doctor Watson has to have it taken out for him and dusted, -"; - assert_eq!(lines, expected); -}); - -sherlock!(vimgrep_no_line_no_column, "Sherlock|Watson", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--vimgrep").arg("-N").arg("--no-column"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock -./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 -./sherlock:but Doctor Watson has to have it taken out for him and dusted, -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/16 -clean!(regression_16, "xyz", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", "ghi/"); - wd.create_dir("ghi"); - wd.create_dir("def/ghi"); - wd.create("ghi/toplevel.txt", "xyz"); - wd.create("def/ghi/subdir.txt", "xyz"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/25 -clean!(regression_25, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", "/llvm/"); - wd.create_dir("src/llvm"); - wd.create("src/llvm/foo", "test"); - - let lines: String = wd.stdout(&mut cmd); - let expected = path("./src/llvm/foo:test\n"); - assert_eq!(lines, expected); - - cmd.current_dir(wd.path().join("src")); - let lines: String = wd.stdout(&mut cmd); - let expected = path("./llvm/foo:test\n"); - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/30 -clean!(regression_30, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", "vendor/**\n!vendor/manifest"); - wd.create_dir("vendor"); - wd.create("vendor/manifest", "test"); - - let lines: String = wd.stdout(&mut cmd); - let expected = path("./vendor/manifest:test\n"); - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/49 -clean!(regression_49, "xyz", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", "foo/bar"); - wd.create_dir("test/foo/bar"); - wd.create("test/foo/bar/baz", "test"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/50 -clean!(regression_50, "xyz", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", "XXX/YYY/"); - wd.create_dir("abc/def/XXX/YYY"); - wd.create_dir("ghi/XXX/YYY"); - wd.create("abc/def/XXX/YYY/bar", "test"); - wd.create("ghi/XXX/YYY/bar", "test"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/65 -clean!(regression_65, "xyz", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", "a/"); - wd.create_dir("a"); - wd.create("a/foo", "xyz"); - wd.create("a/bar", "xyz"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/67 -clean!(regression_67, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", "/*\n!/dir"); - wd.create_dir("dir"); - wd.create_dir("foo"); - wd.create("foo/bar", "test"); - wd.create("dir/bar", "test"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, path("./dir/bar:test\n")); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/87 -clean!(regression_87, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", "foo\n**no-vcs**"); - wd.create("foo", "test"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/90 -clean!(regression_90, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", "!.foo"); - wd.create(".foo", "test"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./.foo:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/93 -clean!(regression_93, r"(\d{1,3}\.){3}\d{1,3}", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("foo", "192.168.1.1"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:192.168.1.1\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/99 -clean!(regression_99, "test", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("foo1", "test"); - wd.create("foo2", "zzz"); - wd.create("bar", "test"); - cmd.arg("-j1").arg("--heading"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!( - sort_lines(&lines), - sort_lines("./bar\ntest\n\n./foo1\ntest\n") - ); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/105 -clean!(regression_105_part1, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "zztest"); - cmd.arg("--vimgrep"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:1:3:zztest\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/105 -clean!(regression_105_part2, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "zztest"); - cmd.arg("--column"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:1:3:zztest\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/127 -clean!(regression_127, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - // Set up a directory hierarchy like this: - // - // .gitignore - // foo/ - // sherlock - // watson - // - // Where `.gitignore` contains `foo/sherlock`. - // - // ripgrep should ignore 'foo/sherlock' giving us results only from - // 'foo/watson' but on Windows ripgrep will include both 'foo/sherlock' and - // 'foo/watson' in the search results. - wd.create_dir(".git"); - wd.create(".gitignore", "foo/sherlock\n"); - wd.create_dir("foo"); - wd.create("foo/sherlock", hay::SHERLOCK); - wd.create("foo/watson", hay::SHERLOCK); - - let lines: String = wd.stdout(&mut cmd); - let expected = format!("\ -./{path}:For the Doctor Watsons of this world, as opposed to the Sherlock -./{path}:be, to a very large extent, the result of luck. Sherlock Holmes -", path=path("foo/watson")); - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/128 -clean!(regression_128, "x", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_bytes("foo", b"01234567\x0b\n\x0b\n\x0b\n\x0b\nx"); - cmd.arg("-n"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:5:x\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/131 -// -// TODO(burntsushi): Darwin doesn't like this test for some reason. -#[cfg(not(target_os = "macos"))] -clean!(regression_131, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", "TopÑapa"); - wd.create("TopÑapa", "test"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/137 -// -// TODO(burntsushi): Figure out why Windows gives "access denied" errors -// when trying to create a file symlink. For now, disable test on Windows. -#[cfg(not(windows))] -sherlock!(regression_137, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { - wd.link_file("sherlock", "sym1"); - wd.link_file("sherlock", "sym2"); - cmd.arg("sym1"); - cmd.arg("sym2"); - cmd.arg("-j1"); - - let lines: String = wd.stdout(&mut cmd); - 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 -sym1:For the Doctor Watsons of this world, as opposed to the Sherlock -sym1:be, to a very large extent, the result of luck. Sherlock Holmes -sym2:For the Doctor Watsons of this world, as opposed to the Sherlock -sym2:be, to a very large extent, the result of luck. Sherlock Holmes -"; - assert_eq!(lines, path(expected)); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/156 -clean!( - regression_156, - r#"#(?:parse|include)\s*\(\s*(?:"|')[./A-Za-z_-]+(?:"|')"#, - "testcase.txt", -|wd: WorkDir, mut cmd: Command| { - const TESTCASE: &'static str = r#"#parse('widgets/foo_bar_macros.vm') -#parse ( 'widgets/mobile/foo_bar_macros.vm' ) -#parse ("widgets/foobarhiddenformfields.vm") -#parse ( "widgets/foo_bar_legal.vm" ) -#include( 'widgets/foo_bar_tips.vm' ) -#include('widgets/mobile/foo_bar_macros.vm') -#include ("widgets/mobile/foo_bar_resetpw.vm") -#parse('widgets/foo-bar-macros.vm') -#parse ( 'widgets/mobile/foo-bar-macros.vm' ) -#parse ("widgets/foo-bar-hiddenformfields.vm") -#parse ( "widgets/foo-bar-legal.vm" ) -#include( 'widgets/foo-bar-tips.vm' ) -#include('widgets/mobile/foo-bar-macros.vm') -#include ("widgets/mobile/foo-bar-resetpw.vm") -"#; - wd.create("testcase.txt", TESTCASE); - cmd.arg("-N"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, TESTCASE); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/184 -clean!(regression_184, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", ".*"); - wd.create_dir("foo/bar"); - wd.create("foo/bar/baz", "test"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, format!("./{}:test\n", path("foo/bar/baz"))); - - cmd.current_dir(wd.path().join("./foo/bar")); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./baz:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/199 -clean!(regression_199, r"\btest\b", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "tEsT"); - cmd.arg("--smart-case"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:tEsT\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/206 -clean!(regression_206, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir("foo"); - wd.create("foo/bar.txt", "test"); - cmd.arg("-g").arg("*.txt"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, format!("./{}:test\n", path("foo/bar.txt"))); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/210 -#[cfg(unix)] -#[test] -fn regression_210() { - use std::ffi::OsStr; - use std::os::unix::ffi::OsStrExt; - - let badutf8 = OsStr::from_bytes(&b"foo\xffbar"[..]); - - let wd = WorkDir::new("regression_210"); - // APFS does not support creating files with invalid UTF-8 bytes. - // https://github.com/BurntSushi/ripgrep/issues/559 - if wd.try_create(badutf8, "test").is_ok() { - let mut cmd = wd.command(); - cmd.arg("-H").arg("test").arg(badutf8); - - let out = wd.output(&mut cmd); - assert_eq!(out.stdout, b"foo\xffbar:test\n".to_vec()); - } -} - -// See: https://github.com/BurntSushi/ripgrep/issues/228 -clean!(regression_228, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir("foo"); - cmd.arg("--ignore-file").arg("foo"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/229 -clean!(regression_229, "[E]conomie", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "economie"); - cmd.arg("-S"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/251 -clean!(regression_251, "привет", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "привет\nПривет\nПрИвЕт"); - cmd.arg("-i"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:привет\n./foo:Привет\n./foo:ПрИвЕт\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/256 -#[cfg(not(windows))] -clean!(regression_256, "test", "foo", |wd: WorkDir, mut cmd: Command| { - wd.create_dir("bar"); - wd.create("bar/baz", "test"); - wd.link_dir("bar", "foo"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "foo/baz:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/256 -#[cfg(not(windows))] -clean!(regression_256_j1, "test", "foo", |wd: WorkDir, mut cmd: Command| { - wd.create_dir("bar"); - wd.create("bar/baz", "test"); - wd.link_dir("bar", "foo"); - cmd.arg("-j1"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "foo/baz:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/279 -clean!(regression_279, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test"); - cmd.arg("-q"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, ""); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/405 -clean!(regression_405, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir("foo/bar"); - wd.create_dir("bar/foo"); - wd.create("foo/bar/file1.txt", "test"); - wd.create("bar/foo/file2.txt", "test"); - cmd.arg("-g").arg("!/foo/**"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, format!("./{}:test\n", path("bar/foo/file2.txt"))); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/428 -#[cfg(not(windows))] -clean!(regression_428_color_context_path, "foo", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("sherlock", "foo\nbar"); - cmd.arg("-A1").arg("-H").arg("--no-heading").arg("-N") - .arg("--colors=match:none").arg("--color=always"); - - let lines: String = wd.stdout(&mut cmd); - let expected = format!( - "{colored_path}:foo\n{colored_path}-bar\n", - colored_path=format!( - "\x1b\x5b\x30\x6d\x1b\x5b\x33\x35\x6d./{path}\x1b\x5b\x30\x6d", - path=path("sherlock"))); - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/428 -clean!(regression_428_unrecognized_style, "Sherlok", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--colors=match:style:"); - wd.assert_err(&mut cmd); - - let output = cmd.output().unwrap(); - let err = String::from_utf8_lossy(&output.stderr); - let expected = "\ -unrecognized style attribute ''. Choose from: nobold, bold, nointense, intense, \ -nounderline, underline. -"; - assert_eq!(err, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/493 -clean!(regression_493, r"\b 're \b", "input.txt", -|wd: WorkDir, mut cmd: Command| { - wd.create("input.txt", "peshwaship 're seminomata"); - cmd.arg("-o"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, " 're \n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/553 -sherlock!(regression_553_switch, "sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-i"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); - - // This repeats the `-i` flag. - cmd.arg("-i"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(regression_553_flag, "world|attached", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-C").arg("1"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always --- -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached. -"; - assert_eq!(lines, expected); - - cmd.arg("-C").arg("0"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Doctor Watsons of this world, as opposed to the Sherlock -and exhibited clearly, with a label attached. -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/599 -clean!(regression_599, "^$", "input.txt", |wd: WorkDir, mut cmd: Command| { - wd.create("input.txt", "\n\ntest\n"); - cmd.args(&[ - "--color", "ansi", - "--colors", "path:none", - "--colors", "line:none", - "--colors", "match:fg:red", - "--colors", "match:style:nobold", - "--line-number", - ]); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -1: -2: -"; - assert_eq!(expected, lines); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/807 -clean!(regression_807, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir(".git"); - wd.create(".gitignore", ".a/b"); - wd.create_dir(".a/b"); - wd.create_dir(".a/c"); - wd.create(".a/b/file", "test"); - wd.create(".a/c/file", "test"); - - cmd.arg("--hidden"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, format!("./{}:test\n", path(".a/c/file"))); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/900 -sherlock!(regression_900, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| { - wd.create("pat", ""); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/1 -clean!(feature_1_sjis, "Шерлок Холмс", ".", |wd: WorkDir, mut cmd: Command| { - let sherlock = - b"\x84Y\x84u\x84\x82\x84|\x84\x80\x84{ \x84V\x84\x80\x84|\x84}\x84\x83"; - wd.create_bytes("foo", &sherlock[..]); - cmd.arg("-Esjis"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:Шерлок Холмс\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/1 -clean!(feature_1_utf16_auto, "Шерлок Холмс", ".", -|wd: WorkDir, mut cmd: Command| { - let sherlock = - b"\xff\xfe(\x045\x04@\x04;\x04>\x04:\x04 \x00%\x04>\x04;\x04<\x04A\x04"; - wd.create_bytes("foo", &sherlock[..]); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:Шерлок Холмс\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/1 -clean!(feature_1_utf16_explicit, "Шерлок Холмс", ".", -|wd: WorkDir, mut cmd: Command| { - let sherlock = - b"\xff\xfe(\x045\x04@\x04;\x04>\x04:\x04 \x00%\x04>\x04;\x04<\x04A\x04"; - wd.create_bytes("foo", &sherlock[..]); - cmd.arg("-Eutf-16le"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:Шерлок Холмс\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/1 -clean!(feature_1_eucjp, "Шерлок Холмс", ".", -|wd: WorkDir, mut cmd: Command| { - let sherlock = - b"\xa7\xba\xa7\xd6\xa7\xe2\xa7\xdd\xa7\xe0\xa7\xdc \xa7\xb7\xa7\xe0\xa7\xdd\xa7\xde\xa7\xe3"; - wd.create_bytes("foo", &sherlock[..]); - cmd.arg("-Eeuc-jp"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:Шерлок Холмс\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/1 -sherlock!(feature_1_unknown_encoding, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-Efoobar"); - wd.assert_non_empty_stderr(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/1 -// Specific: https://github.com/BurntSushi/ripgrep/pull/398/files#r111109265 -sherlock!(feature_1_replacement_encoding, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-Ecsiso2022kr"); - wd.assert_non_empty_stderr(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/7 -sherlock!(feature_7, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| { - wd.create("pat", "Sherlock\nHolmes"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/7 -sherlock!(feature_7_dash, "-f-", ".", |wd: WorkDir, mut cmd: Command| { - let output = wd.pipe(&mut cmd, "Sherlock"); - let lines = String::from_utf8_lossy(&output.stdout); - 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 -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/20 -sherlock!(feature_20_no_filename, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--no-filename"); - - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/34 -sherlock!(feature_34_only_matching, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--only-matching"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./sherlock:Sherlock -./sherlock:Sherlock -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/34 -sherlock!(feature_34_only_matching_line_column, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--only-matching").arg("--column").arg("--line-number"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./sherlock:1:57:Sherlock -./sherlock:3:49:Sherlock -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/45 -sherlock!(feature_45_relative_cwd, "test", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create(".not-an-ignore", "foo\n/bar"); - wd.create_dir("bar"); - wd.create_dir("baz/bar"); - wd.create_dir("baz/baz/bar"); - wd.create("bar/test", "test"); - wd.create("baz/bar/test", "test"); - wd.create("baz/baz/bar/test", "test"); - wd.create("baz/foo", "test"); - wd.create("baz/test", "test"); - wd.create("foo", "test"); - wd.create("test", "test"); - - // First, get a baseline without applying ignore rules. - let lines = paths_from_stdout(wd.stdout(&mut cmd)); - assert_eq!(lines, paths(&[ - "./bar/test", "./baz/bar/test", "./baz/baz/bar/test", "./baz/foo", - "./baz/test", "./foo", "./test", - ])); - - // Now try again with the ignore file activated. - cmd.arg("--ignore-file").arg(".not-an-ignore"); - let lines = paths_from_stdout(wd.stdout(&mut cmd)); - assert_eq!(lines, paths(&[ - "./baz/bar/test", "./baz/baz/bar/test", "./baz/test", "./test", - ])); - - // 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 = wd.command(); - cmd.arg("test").arg(".").arg("--ignore-file").arg("../.not-an-ignore"); - cmd.current_dir(wd.path().join("baz")); - let lines = paths_from_stdout(wd.stdout(&mut cmd)); - assert_eq!(lines, paths(&["./baz/bar/test", "./test"])); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/45 -sherlock!(feature_45_precedence_with_others, "test", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create(".not-an-ignore", "*.log"); - wd.create(".ignore", "!imp.log"); - wd.create("imp.log", "test"); - wd.create("wat.log", "test"); - - cmd.arg("--ignore-file").arg(".not-an-ignore"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./imp.log:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/45 -sherlock!(feature_45_precedence_internal, "test", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create(".not-an-ignore1", "*.log"); - wd.create(".not-an-ignore2", "!imp.log"); - wd.create("imp.log", "test"); - wd.create("wat.log", "test"); - - cmd.arg("--ignore-file").arg(".not-an-ignore1"); - cmd.arg("--ignore-file").arg(".not-an-ignore2"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./imp.log:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/68 -clean!(feature_68_no_ignore_vcs, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create(".gitignore", "foo"); - wd.create(".ignore", "bar"); - wd.create("foo", "test"); - wd.create("bar", "test"); - cmd.arg("--no-ignore-vcs"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/70 -sherlock!(feature_70_smart_case, "sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--smart-case"); - - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/89 -sherlock!(feature_89_files_with_matches, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--null").arg("--files-with-matches"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./sherlock\x00"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/89 -sherlock!(feature_89_files_without_matches, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("file.py", "foo"); - cmd.arg("--null").arg("--files-without-match"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./file.py\x00"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/89 -sherlock!(feature_89_count, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--null").arg("--count"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./sherlock\x002\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/89 -sherlock!(feature_89_files, "NADA", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--null").arg("--files"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./sherlock\x00"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/89 -sherlock!(feature_89_match, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--null").arg("-C1"); - - let lines: String = wd.stdout(&mut cmd); - 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; -"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/109 -clean!(feature_109_max_depth, "far", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir("one"); - wd.create("one/pass", "far"); - wd.create_dir("one/too"); - wd.create("one/too/many", "far"); - - cmd.arg("--maxdepth").arg("2"); - - let lines: String = wd.stdout(&mut cmd); - let expected = path("./one/pass:far\n"); - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/124 -clean!(feature_109_case_sensitive_part1, "test", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("foo", "tEsT"); - cmd.arg("--smart-case").arg("--case-sensitive"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/124 -clean!(feature_109_case_sensitive_part2, "test", ".", -|wd: WorkDir, mut cmd: Command| { - wd.create("foo", "tEsT"); - cmd.arg("--ignore-case").arg("--case-sensitive"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/129 -clean!(feature_129_matches, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test\ntest abcdefghijklmnopqrstuvwxyz test"); - cmd.arg("-M26"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "./foo:test\n./foo:[Omitted long matching line]\n"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/129 -clean!(feature_129_context, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test\nabcdefghijklmnopqrstuvwxyz"); - cmd.arg("-M20").arg("-C1"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "./foo:test\n./foo-[Omitted long context line]\n"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/129 -clean!(feature_129_replace, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test\ntest abcdefghijklmnopqrstuvwxyz test"); - cmd.arg("-M26").arg("-rfoo"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "./foo:foo\n./foo:[Omitted long line with 2 matches]\n"; - assert_eq!(lines, expected); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/159 -clean!(feature_159_works, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test\ntest"); - cmd.arg("-m1"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/159 -clean!(feature_159_zero_max, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test\ntest"); - cmd.arg("-m0"); - wd.assert_err(&mut cmd); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/243 -clean!(feature_243_column_line, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test"); - cmd.arg("--column"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./foo:1:1:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/263 -clean!(feature_263_sort_files, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create("foo", "test"); - wd.create("abc", "test"); - wd.create("zoo", "test"); - wd.create("bar", "test"); - cmd.arg("--sort-files"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./abc:test\n./bar:test\n./foo:test\n./zoo:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/275 -clean!(feature_275_pathsep, "test", ".", |wd: WorkDir, mut cmd: Command| { - wd.create_dir("foo"); - wd.create("foo/bar", "test"); - cmd.arg("--path-separator").arg("Z"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, ".ZfooZbar:test\n"); -}); - -// See: https://github.com/BurntSushi/ripgrep/issues/362 -sherlock!(feature_362_dfa_size_limit, r"For\s", -|wd: WorkDir, mut cmd: Command| { - // This should fall back to the nfa engine but should still produce the - // expected result. - cmd.arg("--dfa-size-limit").arg("10"); - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -For the Doctor Watsons of this world, as opposed to the Sherlock -"; - assert_eq!(lines, expected); -}); - -sherlock!(feature_362_exceeds_regex_size_limit, r"[0-9]\w+", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--regex-size-limit").arg("10K"); - wd.assert_err(&mut cmd); -}); - -#[cfg(target_pointer_width = "32")] -sherlock!(feature_362_u64_to_narrow_usize_suffix_overflow, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - wd.remove("sherlock"); - wd.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"); - wd.assert_err(&mut cmd); -}); - - -// See: https://github.com/BurntSushi/ripgrep/issues/419 -sherlock!(feature_419_zero_as_shortcut_for_null, "Sherlock", ".", -|wd: WorkDir, mut cmd: Command| { - cmd.arg("-0").arg("--count"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "./sherlock\x002\n"); -}); - -#[test] -fn preprocessing() { - if !cmd_exists("xzcat") { - return; - } - let xz_file = include_bytes!("./data/sherlock.xz"); - - let wd = WorkDir::new("feature_preprocessing"); - wd.create_bytes("sherlock.xz", xz_file); - - let mut cmd = wd.command(); - cmd.arg("--pre").arg("xzcat").arg("Sherlock").arg("sherlock.xz"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -} - -#[test] -fn compressed_gzip() { - if !cmd_exists("gzip") { - return; - } - let gzip_file = include_bytes!("./data/sherlock.gz"); - - let wd = WorkDir::new("feature_search_compressed"); - wd.create_bytes("sherlock.gz", gzip_file); - - let mut cmd = wd.command(); - cmd.arg("-z").arg("Sherlock").arg("sherlock.gz"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -} - -#[test] -fn compressed_bzip2() { - if !cmd_exists("bzip2") { - return; - } - let bzip2_file = include_bytes!("./data/sherlock.bz2"); - - let wd = WorkDir::new("feature_search_compressed"); - wd.create_bytes("sherlock.bz2", bzip2_file); - - let mut cmd = wd.command(); - cmd.arg("-z").arg("Sherlock").arg("sherlock.bz2"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -} - -#[test] -fn compressed_xz() { - if !cmd_exists("xz") { - return; - } - let xz_file = include_bytes!("./data/sherlock.xz"); - - let wd = WorkDir::new("feature_search_compressed"); - wd.create_bytes("sherlock.xz", xz_file); - - let mut cmd = wd.command(); - cmd.arg("-z").arg("Sherlock").arg("sherlock.xz"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -} - -#[test] -fn compressed_lz4() { - if !cmd_exists("lz4") { - return; - } - let lz4_file = include_bytes!("./data/sherlock.lz4"); - - let wd = WorkDir::new("feature_search_compressed"); - wd.create_bytes("sherlock.lz4", lz4_file); - - let mut cmd = wd.command(); - cmd.arg("-z").arg("Sherlock").arg("sherlock.lz4"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -} - -#[test] -fn compressed_lzma() { - if !cmd_exists("xz") { - return; - } - let lzma_file = include_bytes!("./data/sherlock.lzma"); - - let wd = WorkDir::new("feature_search_compressed"); - wd.create_bytes("sherlock.lzma", lzma_file); - - let mut cmd = wd.command(); - cmd.arg("-z").arg("Sherlock").arg("sherlock.lzma"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -} - -#[test] -fn compressed_failing_gzip() { - if !cmd_exists("gzip") { - return; - } - let wd = WorkDir::new("feature_search_compressed"); - wd.create("sherlock.gz", hay::SHERLOCK); - - let mut cmd = wd.command(); - cmd.arg("-z").arg("Sherlock").arg("sherlock.gz"); - - wd.assert_non_empty_stderr(&mut cmd); - - let output = cmd.output().unwrap(); - let err = String::from_utf8_lossy(&output.stderr); - assert!(!err.is_empty()); -} - -sherlock!(feature_196_persistent_config, "sherlock", -|wd: WorkDir, mut cmd: Command| { - // Make sure we get no matches by default. - wd.assert_err(&mut cmd); - - // Now add our config file, and make sure it impacts ripgrep. - wd.create(".ripgreprc", "--ignore-case"); - cmd.env("RIPGREP_CONFIG_PATH", ".ripgreprc"); - let lines: String = wd.stdout(&mut cmd); - 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 -"; - assert_eq!(lines, expected); -}); - -sherlock!(feature_411_single_threaded_search_stats, -|wd: WorkDir, mut cmd: Command| { - cmd.arg("--stats"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines.contains("2 matched lines"), true); - assert_eq!(lines.contains("1 files contained matches"), true); - assert_eq!(lines.contains("1 files searched"), true); - assert_eq!(lines.contains("seconds"), true); -}); - -#[test] -fn feature_411_parallel_search_stats() { - let wd = WorkDir::new("feature_411"); - wd.create("sherlock_1", hay::SHERLOCK); - wd.create("sherlock_2", hay::SHERLOCK); - - let mut cmd = wd.command(); - cmd.arg("--stats"); - cmd.arg("Sherlock"); - cmd.arg("./"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines.contains("4 matched lines"), true); - assert_eq!(lines.contains("2 files contained matches"), true); - assert_eq!(lines.contains("2 files searched"), true); - assert_eq!(lines.contains("seconds"), true); -} - -#[test] -fn feature_740_passthru() { - let wd = WorkDir::new("feature_740"); - wd.create("file", "\nfoo\nbar\nfoobar\n\nbaz\n"); - wd.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 - let mut cmd = wd.command(); - cmd.args(common_args).arg("foo").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, foo_expected); - - let foo_bar_expected = "\ -1- -2:foo -3:bar -4:foobar -5- -6-baz -"; - - // With multiple -e patterns - let mut cmd = wd.command(); - cmd.args(common_args) - .arg("-e").arg("foo").arg("-e").arg("bar").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, foo_bar_expected); - - // With multiple -f patterns - let mut cmd = wd.command(); - cmd.args(common_args).arg("-f").arg("patterns").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, foo_bar_expected); - - // -c should override - let mut cmd = wd.command(); - cmd.args(common_args).arg("-c").arg("foo").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "2\n"); - - let only_foo_expected = "\ -1- -2:foo -3-bar -4:foo -5- -6-baz -"; - - // -o should conflict - let mut cmd = wd.command(); - cmd.args(common_args).arg("-o").arg("foo").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, only_foo_expected); - - let replace_foo_expected = "\ -1- -2:wat -3-bar -4:watbar -5- -6-baz -"; - - // -r should conflict - let mut cmd = wd.command(); - cmd.args(common_args).arg("-r").arg("wat").arg("foo").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, replace_foo_expected); -} - -#[test] -fn binary_nosearch() { - let wd = WorkDir::new("binary_nosearch"); - wd.create("file", "foo\x00bar\nfoo\x00baz\n"); - let mut cmd = wd.command(); - cmd.arg("foo").arg("file"); - wd.assert_err(&mut cmd); -} - -// The following two tests show a discrepancy in search results between -// searching with memory mapped files and stream searching. Stream searching -// uses a heuristic (that GNU grep also uses) where NUL bytes are replaced with -// the EOL terminator, which tends to avoid allocating large amounts of memory -// for really long "lines." The memory map searcher has no need to worry about -// such things, and more than that, it would be pretty hard for it to match -// the semantics of streaming search in this case. -// -// Binary files with lots of NULs aren't really part of the use case of ripgrep -// (or any other grep-like tool for that matter), so we shouldn't feel too bad -// about it. -#[test] -fn binary_search_mmap() { - let wd = WorkDir::new("binary_search_mmap"); - wd.create("file", "foo\x00bar\nfoo\x00baz\n"); - let mut cmd = wd.command(); - cmd.arg("-a").arg("--mmap").arg("foo").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "foo\x00bar\nfoo\x00baz\n"); -} - -#[test] -fn binary_search_no_mmap() { - let wd = WorkDir::new("binary_search_no_mmap"); - wd.create("file", "foo\x00bar\nfoo\x00baz\n"); - let mut cmd = wd.command(); - cmd.arg("-a").arg("--no-mmap").arg("foo").arg("file"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "foo\x00bar\nfoo\x00baz\n"); -} - -#[test] -fn files() { - let wd = WorkDir::new("files"); - wd.create("file", ""); - wd.create_dir("dir"); - wd.create("dir/file", ""); - - let mut cmd = wd.command(); - cmd.arg("--files"); - let lines: String = wd.stdout(&mut cmd); - assert!(lines == path("file\ndir/file\n") - || lines == path("dir/file\nfile\n")); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/64 -#[test] -fn regression_64() { - let wd = WorkDir::new("regression_64"); - wd.create_dir("dir"); - wd.create_dir("foo"); - wd.create("dir/abc", ""); - wd.create("foo/abc", ""); - - let mut cmd = wd.command(); - cmd.arg("--files").arg("foo"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, path("foo/abc\n")); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/270 -#[test] -fn regression_270() { - let wd = WorkDir::new("regression_270"); - wd.create("foo", "-test"); - - let mut cmd = wd.command(); - cmd.arg("-e").arg("-test").arg("./"); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, path("./foo:-test\n")); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/391 -#[test] -fn regression_391() { - let wd = WorkDir::new("regression_391"); - wd.create_dir(".git"); - wd.create("lock", ""); - wd.create("bar.py", ""); - wd.create(".git/packed-refs", ""); - wd.create(".git/description", ""); - - let mut cmd = wd.command(); - cmd.arg("--no-ignore").arg("--hidden").arg("--follow").arg("--files") - .arg("--glob") - .arg("!{.git,node_modules,plugged}/**") - .arg("--glob") - .arg("*.{js,json,php,md,styl,scss,sass,pug,html,config,py,cpp,c,go,hs}"); - - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "bar.py\n"); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/451 -#[test] -fn regression_451_only_matching_as_in_issue() { - let wd = WorkDir::new("regression_451_only_matching"); - let path = "digits.txt"; - wd.create(path, "1 2 3\n"); - - let mut cmd = wd.command(); - cmd.arg("[0-9]+").arg(path).arg("--only-matching"); - let lines: String = wd.stdout(&mut cmd); - - let expected = "\ -1 -2 -3 -"; - - assert_eq!(lines, expected); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/451 -#[test] -fn regression_451_only_matching() { - let wd = WorkDir::new("regression_451_only_matching"); - let path = "digits.txt"; - wd.create(path, "1 2 3\n123\n"); - - let mut cmd = wd.command(); - cmd.arg("[0-9]").arg(path) - .arg("--only-matching") - .arg("--column"); - let lines: String = wd.stdout(&mut cmd); - - let expected = "\ -1:1:1 -1:3:2 -1:5:3 -2:1:1 -2:2:2 -2:3:3 -"; - - assert_eq!(lines, expected); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/483 -#[test] -fn regression_483_matching_no_stdout() { - let wd = WorkDir::new("regression_483_matching_no_stdout"); - wd.create("file.py", ""); - - let mut cmd = wd.command(); - cmd.arg("--quiet") - .arg("--files") - .arg("--glob").arg("*.py"); - - let lines: String = wd.stdout(&mut cmd); - assert!(lines.is_empty()); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/483 -#[test] -fn regression_483_non_matching_exit_code() { - let wd = WorkDir::new("regression_483_non_matching_exit_code"); - wd.create("file.rs", ""); - - let mut cmd = wd.command(); - cmd.arg("--quiet") - .arg("--files") - .arg("--glob").arg("*.py"); - - wd.assert_err(&mut cmd); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/506 -#[test] -fn regression_506_word_boundaries_not_parenthesized() { - let wd = WorkDir::new("regression_506_word_boundaries_not_parenthesized"); - let path = "wb.txt"; - wd.create(path, "min minimum amin\n\ - max maximum amax"); - - let mut cmd = wd.command(); - cmd.arg("-w").arg("min|max").arg(path).arg("--only-matching"); - let lines: String = wd.stdout(&mut cmd); - - let expected = "min\nmax\n"; - - assert_eq!(lines, expected); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/568 -#[test] -fn regression_568_leading_hyphen_option_arguments() { - let wd = WorkDir::new("regression_568_leading_hyphen_option_arguments"); - let path = "file"; - wd.create(path, "foo bar -baz\n"); - - let mut cmd = wd.command(); - cmd.arg("-e-baz").arg("-e").arg("-baz").arg(path); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "foo bar -baz\n"); - - let mut cmd = wd.command(); - cmd.arg("-rni").arg("bar").arg(path); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "foo ni -baz\n"); - - let mut cmd = wd.command(); - cmd.arg("-r").arg("-n").arg("-i").arg("bar").arg(path); - let lines: String = wd.stdout(&mut cmd); - assert_eq!(lines, "foo -n -baz\n"); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/693 -#[test] -fn regression_693_context_option_in_contextless_mode() { - let wd = WorkDir::new("regression_693_context_option_in_contextless_mode"); - - wd.create("foo", "xyz\n"); - wd.create("bar", "xyz\n"); - - let mut cmd = wd.command(); - cmd.arg("-C1").arg("-c").arg("--sort-files").arg("xyz").arg("./"); - - let lines: String = wd.stdout(&mut cmd); - let expected = "\ -./bar:1 -./foo:1 -"; - assert_eq!(lines, expected); -} - -#[test] -fn type_list() { - let wd = WorkDir::new("type_list"); - - let mut cmd = wd.command(); - cmd.arg("--type-list"); - let lines: String = wd.stdout(&mut cmd); - // This can change over time, so just make sure we print something. - assert!(!lines.is_empty()); -} - -// See: https://github.com/BurntSushi/ripgrep/issues/948 -sherlock!( - exit_code_match_success, - ".", - ".", - |wd: WorkDir, mut cmd: Command| { - wd.assert_exit_code(0, &mut cmd); - } -); - -// See: https://github.com/BurntSushi/ripgrep/issues/948 -sherlock!( - exit_code_no_match, - "6d28e48b5224a42b167e{10}", - ".", - |wd: WorkDir, mut cmd: Command| { - wd.assert_exit_code(1, &mut cmd); - } -); - -// See: https://github.com/BurntSushi/ripgrep/issues/948 -sherlock!( - exit_code_error, - "*", - ".", - |wd: WorkDir, mut cmd: Command| { - wd.assert_exit_code(2, &mut cmd); - } -); +// Utilities for making tests nicer to read and easier to write. +mod util; + +// Tests related to most features in ripgrep. If you're adding something new +// to ripgrep, tests should probably go in here. +mod feature; +// Tests for ripgrep's JSON format. +mod json; +// Miscellaneous tests grouped in a haphazard manner. Try not to add more. +mod misc; +// Tests for ripgrep's multiline search support. +mod multiline; +// Regression tests. +mod regression; diff --git a/tests/workdir.rs b/tests/util.rs similarity index 58% rename from tests/workdir.rs rename to tests/util.rs index 7bf0172d..f8fa51a9 100644 --- a/tests/workdir.rs +++ b/tests/util.rs @@ -1,11 +1,10 @@ use std::env; use std::error; -use std::fmt; +use std::ffi::OsStr; use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; -use std::process; -use std::str::FromStr; +use std::process::{self, Command}; use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; use std::thread; use std::time::Duration; @@ -13,24 +12,60 @@ use std::time::Duration; static TEST_DIR: &'static str = "ripgrep-tests"; static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT; -/// `WorkDir` represents a directory in which tests are run. +/// Setup an empty work directory and return a command pointing to the ripgrep +/// executable whose CWD is set to the work directory. +/// +/// The name given will be used to create the directory. Generally, it should +/// correspond to the test name. +pub fn setup(test_name: &str) -> (Dir, TestCommand) { + let dir = Dir::new(test_name); + let cmd = dir.command(); + (dir, cmd) +} + +/// Like `setup`, but uses PCRE2 as the underlying regex engine. +pub fn setup_pcre2(test_name: &str) -> (Dir, TestCommand) { + let mut dir = Dir::new(test_name); + dir.pcre2(true); + let cmd = dir.command(); + (dir, cmd) +} + +/// Break the given string into lines, sort them and then join them back +/// together. This is useful for testing output from ripgrep that may not +/// always be in the same order. +pub fn sort_lines(lines: &str) -> String { + let mut lines: Vec<&str> = lines.trim().lines().collect(); + lines.sort(); + format!("{}\n", lines.join("\n")) +} + +/// Returns true if and only if the given program can be successfully executed +/// with a `--help` flag. +pub fn cmd_exists(program: &str) -> bool { + Command::new(program).arg("--help").output().is_ok() +} + +/// Dir represents a directory in which tests should be run. /// /// Directories are created from a global atomic counter to avoid duplicates. -#[derive(Debug)] -pub struct WorkDir { +#[derive(Clone, Debug)] +pub struct Dir { /// The directory in which this test executable is running. root: PathBuf, /// The directory in which the test should run. If a test needs to create /// files, they should go in here. This directory is also used as the CWD /// for any processes created by the test. dir: PathBuf, + /// Set to true when the test should use PCRE2 as the regex engine. + pcre2: bool, } -impl WorkDir { +impl Dir { /// Create a new test working directory with the given name. The name /// does not need to be distinct for each invocation, but should correspond /// to a logical grouping of tests. - pub fn new(name: &str) -> WorkDir { + pub fn new(name: &str) -> Dir { let id = NEXT_ID.fetch_add(1, Ordering::SeqCst); let root = env::current_exe() .unwrap() @@ -42,12 +77,24 @@ impl WorkDir { .join(name) .join(&format!("{}", id)); nice_err(&dir, repeat(|| fs::create_dir_all(&dir))); - WorkDir { + Dir { root: root, dir: dir, + pcre2: false, } } + /// Use PCRE2 for this test. + pub fn pcre2(&mut self, yes: bool) { + self.pcre2 = yes; + } + + /// Returns true if and only if this test is configured to use PCRE2 as + /// the regex engine. + pub fn is_pcre2(&self) -> bool { + self.pcre2 + } + /// Create a new file with the given name and contents in this directory, /// or panic on error. pub fn create>(&self, name: P, contents: &str) { @@ -75,18 +122,19 @@ impl WorkDir { /// Create a new file with the given name and contents in this directory, /// or panic on error. pub fn create_bytes>(&self, name: P, contents: &[u8]) { - let path = self.dir.join(name); - nice_err(&path, self.try_create_bytes(&path, contents)); + let path = self.dir.join(&name); + nice_err(&path, self.try_create_bytes(name, contents)); } /// Try to create a new file with the given name and contents in this /// directory. - fn try_create_bytes>( + pub fn try_create_bytes>( &self, - path: P, + name: P, contents: &[u8], ) -> io::Result<()> { - let mut file = File::create(&path)?; + let path = self.dir.join(name); + let mut file = File::create(path)?; file.write_all(contents)?; file.flush() } @@ -106,11 +154,22 @@ impl WorkDir { /// Creates a new command that is set to use the ripgrep executable in /// this working directory. - pub fn command(&self) -> process::Command { + /// + /// This also: + /// + /// * Unsets the `RIPGREP_CONFIG_PATH` environment variable. + /// * Sets the `--path-separator` to `/` so that paths have the same output + /// on all systems. Tests that need to check `--path-separator` itself + /// can simply pass it again to override it. + pub fn command(&self) -> TestCommand { let mut cmd = process::Command::new(&self.bin()); cmd.env_remove("RIPGREP_CONFIG_PATH"); cmd.current_dir(&self.dir); - cmd + cmd.arg("--path-separator").arg("/"); + if self.is_pcre2() { + cmd.arg("--pcre2"); + } + TestCommand { dir: self.clone(), cmd: cmd } } /// Returns the path to the ripgrep executable. @@ -174,16 +233,54 @@ impl WorkDir { let _ = fs::remove_file(&target); nice_err(&target, symlink_file(&src, &target)); } +} + +/// A simple wrapper around a process::Command with some conveniences. +#[derive(Debug)] +pub struct TestCommand { + /// The dir used to launched this command. + dir: Dir, + /// The actual command we use to control the process. + cmd: Command, +} + +impl TestCommand { + /// Returns a mutable reference to the underlying command. + pub fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + /// Add an argument to pass to the command. + pub fn arg>(&mut self, arg: A) -> &mut TestCommand { + self.cmd.arg(arg); + self + } + + /// Add any number of arguments to the command. + pub fn args( + &mut self, + args: I, + ) -> &mut TestCommand + where I: IntoIterator, + A: AsRef + { + self.cmd.args(args); + self + } + + /// Set the working directory for this command. + /// + /// Note that this does not need to be called normally, since the creation + /// of this TestCommand causes its working directory to be set to the + /// test's directory automatically. + pub fn current_dir>(&mut self, dir: P) -> &mut TestCommand { + self.cmd.current_dir(dir); + self + } /// Runs and captures the stdout of the given command. - /// - /// If the return type could not be created from a string, then this - /// panics. - pub fn stdout>( - &self, - cmd: &mut process::Command, - ) -> T { - let o = self.output(cmd); + pub fn stdout(&mut self) -> String { + let o = self.output(); let stdout = String::from_utf8_lossy(&o.stdout); match stdout.parse() { Ok(t) => t, @@ -197,23 +294,13 @@ impl WorkDir { } } - /// Gets the output of a command. If the command failed, then this panics. - pub fn output(&self, cmd: &mut process::Command) -> process::Output { - let output = cmd.output().unwrap(); - self.expect_success(cmd, output) - } - /// Pipe `input` to a command, and collect the output. - pub fn pipe( - &self, - cmd: &mut process::Command, - input: &str - ) -> process::Output { - cmd.stdin(process::Stdio::piped()); - cmd.stdout(process::Stdio::piped()); - cmd.stderr(process::Stdio::piped()); + pub fn pipe(&mut self, input: &str) -> String { + self.cmd.stdin(process::Stdio::piped()); + self.cmd.stdout(process::Stdio::piped()); + self.cmd.stderr(process::Stdio::piped()); - let mut child = cmd.spawn().unwrap(); + let mut child = self.cmd.spawn().unwrap(); // Pipe input to child process using a separate thread to avoid // risk of deadlock between parent and child process. @@ -223,20 +310,86 @@ impl WorkDir { write!(stdin, "{}", input) }); - let output = self.expect_success( - cmd, - child.wait_with_output().unwrap(), - ); + let output = self.expect_success(child.wait_with_output().unwrap()); worker.join().unwrap().unwrap(); - output + + let stdout = String::from_utf8_lossy(&output.stdout); + match stdout.parse() { + Ok(t) => t, + Err(err) => { + panic!( + "could not convert from string: {:?}\n\n{}", + err, + stdout + ); + } + } } - /// If `o` is not the output of a successful process run - fn expect_success( - &self, - cmd: &process::Command, - o: process::Output - ) -> process::Output { + /// Gets the output of a command. If the command failed, then this panics. + pub fn output(&mut self) -> process::Output { + let output = self.cmd.output().unwrap(); + self.expect_success(output) + } + + /// Runs the command and asserts that it resulted in an error exit code. + pub fn assert_err(&mut self) { + let o = self.cmd.output().unwrap(); + if o.status.success() { + panic!( + "\n\n===== {:?} =====\n\ + command succeeded but expected failure!\ + \n\ncwd: {}\ + \n\nstatus: {}\ + \n\nstdout: {}\n\nstderr: {}\ + \n\n=====\n", + self.cmd, + self.dir.dir.display(), + o.status, + String::from_utf8_lossy(&o.stdout), + String::from_utf8_lossy(&o.stderr) + ); + } + } + + /// Runs the command and asserts that its exit code matches expected exit + /// code. + pub fn assert_exit_code(&mut self, expected_code: i32) { + let code = self.cmd.output().unwrap().status.code().unwrap(); + assert_eq!( + expected_code, code, + "\n\n===== {:?} =====\n\ + expected exit code did not match\ + \n\nexpected: {}\ + \n\nfound: {}\ + \n\n=====\n", + self.cmd, + expected_code, + code + ); + } + + /// Runs the command and asserts that something was printed to stderr. + pub fn assert_non_empty_stderr(&mut self) { + let o = self.cmd.output().unwrap(); + if o.status.success() || o.stderr.is_empty() { + panic!( + "\n\n===== {:?} =====\n\ + command succeeded but expected failure!\ + \n\ncwd: {}\ + \n\nstatus: {}\ + \n\nstdout: {}\n\nstderr: {}\ + \n\n=====\n", + self.cmd, + self.dir.dir.display(), + o.status, + String::from_utf8_lossy(&o.stdout), + String::from_utf8_lossy(&o.stderr) + ); + } + } + + fn expect_success(&self, o: process::Output) -> process::Output { if !o.status.success() { let suggest = if o.stderr.is_empty() { @@ -254,81 +407,21 @@ impl WorkDir { \n\nstdout: {}\ \n\nstderr: {}\ \n\n==========\n", - suggest, cmd, self.dir.display(), o.status, + suggest, self.cmd, self.dir.dir.display(), o.status, String::from_utf8_lossy(&o.stdout), String::from_utf8_lossy(&o.stderr)); } o } - - /// Runs the given command and asserts that it resulted in an error exit - /// code. - pub fn assert_err(&self, cmd: &mut process::Command) { - let o = cmd.output().unwrap(); - if o.status.success() { - panic!( - "\n\n===== {:?} =====\n\ - command succeeded but expected failure!\ - \n\ncwd: {}\ - \n\nstatus: {}\ - \n\nstdout: {}\n\nstderr: {}\ - \n\n=====\n", - cmd, - self.dir.display(), - o.status, - String::from_utf8_lossy(&o.stdout), - String::from_utf8_lossy(&o.stderr) - ); - } - } - - /// Runs the given command and asserts that its exit code matches expected - /// exit code. - pub fn assert_exit_code( - &self, - expected_code: i32, - cmd: &mut process::Command, - ) { - let code = cmd.status().unwrap().code().unwrap(); - - assert_eq!( - expected_code, code, - "\n\n===== {:?} =====\n\ - expected exit code did not match\ - \n\nexpected: {}\ - \n\nfound: {}\ - \n\n=====\n", - cmd, expected_code, code - ); - } - - /// Runs the given command and asserts that something was printed to - /// stderr. - pub fn assert_non_empty_stderr(&self, cmd: &mut process::Command) { - let o = cmd.output().unwrap(); - if o.status.success() || o.stderr.is_empty() { - panic!("\n\n===== {:?} =====\n\ - command succeeded but expected failure!\ - \n\ncwd: {}\ - \n\nstatus: {}\ - \n\nstdout: {}\n\nstderr: {}\ - \n\n=====\n", - cmd, self.dir.display(), o.status, - String::from_utf8_lossy(&o.stdout), - String::from_utf8_lossy(&o.stderr)); - } - } } -fn nice_err, T, E: error::Error>( - path: P, +fn nice_err( + path: &Path, res: Result, ) -> T { match res { Ok(t) => t, - Err(err) => { - panic!("{}: {:?}", path.as_ref().display(), err); - } + Err(err) => panic!("{}: {:?}", path.display(), err), } }