Files
rtk-ai--rtk/tests/grep_context_test.rs
2026-07-13 12:01:57 +08:00

172 lines
4.6 KiB
Rust

#![cfg(unix)]
//! Regressions from the #2333 grep overhaul: -A/-B/-C must keep context lines,
//! -c/-o must not leak ripgrep's NUL separator.
use std::process::Command;
fn rtk() -> Command {
Command::new(env!("CARGO_BIN_EXE_rtk"))
}
fn rg_available() -> bool {
Command::new("rg")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
#[test]
fn after_context_lines_are_shown() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("ctx.txt");
std::fs::write(&f, "before1\nbefore2\nMATCHME\nafter1\nafter2\nafter3\n").unwrap();
let out = rtk()
.args(["grep", "-A2", "MATCHME", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("after1"),
"missing after-context line 1:\n{stdout}"
);
assert!(
stdout.contains("after2"),
"missing after-context line 2:\n{stdout}"
);
assert!(!stdout.contains("after3"), "leaked beyond -A2:\n{stdout}");
}
#[test]
fn before_context_lines_are_shown() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("ctx.txt");
std::fs::write(&f, "before1\nbefore2\nMATCHME\nafter1\n").unwrap();
let out = rtk()
.args(["grep", "-B1", "MATCHME", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("before2"),
"missing before-context line:\n{stdout}"
);
assert!(!stdout.contains("before1"), "leaked beyond -B1:\n{stdout}");
}
#[test]
fn count_output_has_no_nul_separator() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("nul.txt");
std::fs::write(&f, "TODO one\nnope\nTODO two\n").unwrap();
let out = rtk()
.args(["grep", "-c", "TODO", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains('\u{0}'),
"NUL leaked into -c output: {stdout:?}"
);
assert!(
stdout.contains('2'),
"count missing from -c output: {stdout:?}"
);
}
#[test]
fn group_separator_between_non_adjacent_matches() {
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("sep.txt");
std::fs::write(
&f,
"alpha match here\nfiller 1\nfiller 2\nfiller 3\nfiller 4\nfiller 5\nbeta match here\n",
)
.unwrap();
let out = rtk()
.args(["grep", "-A1", "match", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("--"),
"missing group separator between non-adjacent match blocks:\n{stdout}"
);
assert!(
stdout.contains("alpha match here"),
"first match must be present:\n{stdout}"
);
assert!(
stdout.contains("beta match here"),
"second match must be present:\n{stdout}"
);
}
#[test]
fn no_separator_without_context_flag() {
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("nosep.txt");
std::fs::write(
&f,
"match line 1\nfiller\nfiller\nfiller\nmatch line 2\nfiller\nfiller\nmatch line 3\n",
)
.unwrap();
let out = rtk()
.args(["grep", "-n", "match", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("--"),
"plain grep without -A/-B/-C must not insert -- separators:\n{stdout}"
);
assert!(
stdout.contains("match line 1"),
"first match must be present:\n{stdout}"
);
assert!(
stdout.contains("match line 3"),
"third match must be present:\n{stdout}"
);
}
#[test]
fn only_matching_output_has_no_nul_separator() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("nul.txt");
std::fs::write(&f, "TODO one\nnope\nTODO two\n").unwrap();
let out = rtk()
.args(["grep", "-o", "TODO", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains('\u{0}'),
"NUL leaked into -o output: {stdout:?}"
);
assert!(
stdout.contains("TODO"),
"match missing from -o output: {stdout:?}"
);
assert!(
!stdout.contains("1:TODO"),
"line-number prefix leaked into -o output: {stdout:?}"
);
}