use std::io::Write;
use std::process::Command;
use tempfile::NamedTempFile;
fn run_sel(args: &[&str]) -> String {
let output = Command::new("cargo")
.args(["run", "--quiet", "--"])
.args(args)
.output()
.expect("Failed to execute sel");
String::from_utf8_lossy(&output.stdout).to_string()
}
fn create_test_file(lines: &[&str]) -> NamedTempFile {
let mut file = NamedTempFile::new().unwrap();
for line in lines {
writeln!(file, "{}", line).unwrap();
}
file.flush().unwrap();
file
}
#[test]
fn test_context_single_line() {
let file = create_test_file(&["line1", "line2", "line3"]);
let output = run_sel(&["-c", "1", "2", file.path().to_str().unwrap()]);
assert!(output.contains("line2") || !output.is_empty());
}
#[test]
fn test_context_at_start_of_file() {
let file = create_test_file(&["TARGET", "line2", "line3", "line4"]);
let output = run_sel(&["-c", "2", "1", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_at_end_of_file() {
let file = create_test_file(&["line1", "line2", "line3", "TARGET"]);
let output = run_sel(&["-c", "2", "4", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_middle_of_file() {
let file = create_test_file(&[
"context before 1",
"context before 2",
"TARGET LINE",
"context after 1",
"context after 2",
]);
let output = run_sel(&["-c", "2", "3", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET LINE"));
}
#[test]
fn test_context_zero_lines() {
let file = create_test_file(&["before", "TARGET", "after"]);
let output = run_sel(&["-c", "0", "2", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_large_value() {
let file = create_test_file(&["1", "2", "3", "4", "5", "TARGET", "7", "8", "9", "10", "11"]);
let output = run_sel(&["-c", "10", "6", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_multiple_targets() {
let file = create_test_file(&["line1", "TARGET1", "line3", "TARGET2", "line5"]);
let output = run_sel(&["-c", "1", "2,4", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET1") || output.contains("TARGET2"));
}
#[test]
fn test_context_overlapping() {
let file = create_test_file(&["line1", "line2", "TARGET1", "line4", "TARGET2", "line6"]);
let output = run_sel(&["-c", "2", "3,5", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET1") || output.contains("TARGET2"));
}
#[test]
fn test_context_with_range() {
let file = create_test_file(&[
"before1", "before2", "START", "middle", "END", "after1", "after2",
]);
let output = run_sel(&["-c", "1", "3-5", file.path().to_str().unwrap()]);
assert!(output.contains("START") || output.contains("END"));
}
#[test]
fn test_context_at_file_boundaries() {
let file = create_test_file(&["TARGET", "line2", "line3"]);
let output = run_sel(&["-c", "5", "1", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_with_no_line_numbers() {
let file = create_test_file(&["before", "TARGET", "after"]);
let output = run_sel(&["-c", "1", "-l", "2", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
assert!(!output.contains("2:"));
}
#[test]
fn test_context_single_line_range() {
let file = create_test_file(&["before", "TARGET", "after"]);
let output = run_sel(&["-c", "1", "2-2", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_far_apart_targets() {
let file = create_test_file(&[
"line1", "TARGET1", "line3", "line4", "line5", "line6", "line7", "TARGET2", "line9",
]);
let output = run_sel(&["-c", "1", "2,8", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET1"));
assert!(output.contains("TARGET2"));
}
#[test]
fn test_context_adjacent_targets() {
let file = create_test_file(&["before", "TARGET1", "TARGET2", "after"]);
let output = run_sel(&["-c", "1", "2,3", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET1") && output.contains("TARGET2"));
}
#[test]
fn test_context_with_positional_selector() {
let file = create_test_file(&["before", "TARGET content here", "after"]);
let output = run_sel(&["-c", "1", "2:5", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_char_context_together() {
let file = create_test_file(&["before", "TARGET", "after"]);
let output = run_sel(&["-c", "1", "-n", "5", "2:3", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_empty_file() {
let file = create_test_file(&[""]);
let _output = run_sel(&["-c", "2", "1", file.path().to_str().unwrap()]);
}
#[test]
fn test_context_beyond_end_of_file() {
let file = create_test_file(&["line1", "TARGET"]);
let output = run_sel(&["-c", "10", "2", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_negative_context_size() {
let file = create_test_file(&["TARGET"]);
let output = Command::new("cargo")
.args(["run", "--quiet", "--"])
.args(["-c", "-1", "1"])
.arg(file.path().to_str().unwrap())
.output();
if let Ok(out) = output {
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
if out.status.code() != Some(0) {
assert!(
stderr.contains("Error")
|| stderr.contains("Invalid")
|| stderr.contains("unexpected")
|| stderr.contains("digit")
);
} else {
assert!(stdout.contains("TARGET") || stdout.is_empty());
}
}
}
#[test]
fn test_context_with_long_lines() {
let long_line = "a".repeat(200);
let file = create_test_file(&[&long_line, "TARGET", &long_line]);
let output = run_sel(&["-c", "1", "2", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_multiline_output() {
let file = create_test_file(&[
"context line 1",
"context line 2",
"context line 3",
"TARGET",
"context line 5",
"context line 6",
"context line 7",
]);
let output = run_sel(&["-c", "3", "4", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
let lines: Vec<&str> = output.lines().collect();
assert!(lines.len() > 1);
}
#[test]
fn test_context_first_line_no_before_context() {
let file = create_test_file(&["TARGET on first", "line2", "line3"]);
let output = run_sel(&["-c", "2", "1", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET on first"));
}
#[test]
fn test_context_last_line_no_after_context() {
let file = create_test_file(&["line1", "line2", "TARGET on last"]);
let output = run_sel(&["-c", "2", "3", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET on last"));
}
#[test]
fn test_context_with_special_characters() {
let file = create_test_file(&["before\tline", "TARGET: here!", "after\0line"]);
let output = run_sel(&["-c", "1", "2", file.path().to_str().unwrap()]);
assert!(output.contains("TARGET"));
}
#[test]
fn test_context_ordering_preserved() {
let file = create_test_file(&[
"line1", "line2", "line3", "line4", "line5", "line6", "line7", "line8",
]);
let output = run_sel(&["-c", "1", "3,6", file.path().to_str().unwrap()]);
let lines: Vec<&str> = output.lines().collect();
let line_numbers: Vec<usize> = lines
.iter()
.filter_map(|l| l.split(':').next()?.parse().ok())
.collect();
if !line_numbers.is_empty() {
for i in 1..line_numbers.len() {
assert!(line_numbers[i] > line_numbers[i - 1]);
}
}
}