use super::*;
fn source_files() -> Vec<std::path::PathBuf> {
fn walk(dir: &std::path::Path, out: &mut Vec<std::path::PathBuf>) {
let entries = std::fs::read_dir(dir)
.unwrap_or_else(|e| panic!("cannot read {}: {e}", dir.display()));
for entry in entries {
let path = entry.expect("a directory entry").path();
if path.is_dir() {
walk(&path, out);
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
}
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut files = Vec::new();
walk(&root, &mut files);
files.sort();
assert!(
files.len() > 50,
"the source tree should have been walked, found {} files",
files.len()
);
files
}
fn relative(path: &std::path::Path) -> String {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
crate::platform::slash_path(path.strip_prefix(root).unwrap_or(path))
}
#[test]
fn test9660_only_the_sink_writes_to_the_terminal() {
let allowed = ["src/output/sink.rs", "src/output/sink_tests.rs"];
let banned = ["println!", "eprintln!", "print!", "eprint!"];
let mut offences = Vec::new();
for file in source_files() {
let name = relative(&file);
if allowed.contains(&name.as_str()) {
continue;
}
let text = std::fs::read_to_string(&file).expect("a source file must be readable");
for (number, line) in text.lines().enumerate() {
let code = code_of(line);
for macro_name in banned {
if contains_call(&code, macro_name) {
offences.push(format!("{name}:{}: {}", number + 1, line.trim()));
}
}
}
}
assert!(
offences.is_empty(),
"output must go through outln!/errln! so --jsonl can package it:\n{}",
offences.join("\n")
);
}
#[test]
fn test9661_only_the_output_module_builds_a_process() {
let allowed = ["src/output/child.rs", "src/output/sink_tests.rs"];
let runner = "src/web/runner.rs";
let mut offences = Vec::new();
for file in source_files() {
let name = relative(&file);
if allowed.contains(&name.as_str()) {
continue;
}
let text = std::fs::read_to_string(&file).expect("a source file must be readable");
for (number, line) in text.lines().enumerate() {
let code = code_of(line);
let builds_std = code.contains("std::process::Command");
let builds_tokio = code.contains("tokio::process::Command");
if builds_std || (builds_tokio && name != runner) {
offences.push(format!("{name}:{}: {}", number + 1, line.trim()));
}
}
}
assert!(
offences.is_empty(),
"a child process must be built with output::Command so its output can be packaged:\n{}",
offences.join("\n")
);
}
#[test]
fn test9676_only_the_session_asks_a_person_for_anything() {
let allowed = ["src/secret/session.rs", "src/output/sink_tests.rs"];
let mut offences = Vec::new();
for file in source_files() {
let name = relative(&file);
if allowed.contains(&name.as_str()) {
continue;
}
let text = std::fs::read_to_string(&file).expect("a source file must be readable");
for (number, line) in text.lines().enumerate() {
if code_of(line).contains("rpassword") {
offences.push(format!("{name}:{}: {}", number + 1, line.trim()));
}
}
}
assert!(
offences.is_empty(),
"a passphrase must be asked for through secret::session::ask_passphrase:\n{}",
offences.join("\n")
);
}
#[test]
fn test9662_the_flag_is_removed_from_any_position() {
let mut argv: Vec<String> = ["wsb", "test", "--jsonl", "--filter", "wsb"]
.iter()
.map(|s| s.to_string())
.collect();
assert!(take_jsonl_flag(&mut argv));
assert_eq!(argv, vec!["wsb", "test", "--filter", "wsb"]);
let mut trailing: Vec<String> = ["wsb", "publish", "--jsonl"]
.iter()
.map(|s| s.to_string())
.collect();
assert!(take_jsonl_flag(&mut trailing));
assert_eq!(trailing, vec!["wsb", "publish"]);
let mut absent: Vec<String> = ["wsb", "status"].iter().map(|s| s.to_string()).collect();
assert!(!take_jsonl_flag(&mut absent));
assert_eq!(absent, vec!["wsb", "status"]);
let mut lookalike: Vec<String> = ["wsb", "note", "--jsonl-ish"]
.iter()
.map(|s| s.to_string())
.collect();
assert!(!take_jsonl_flag(&mut lookalike));
assert_eq!(lookalike.len(), 3);
}
#[test]
fn test9663_a_write_becomes_one_record_per_line() {
assert_eq!(super::split_lines("one\n"), vec!["one".to_string()]);
assert_eq!(
super::split_lines("one\ntwo\n"),
vec!["one".to_string(), "two".to_string()]
);
assert_eq!(super::split_lines("partial"), vec!["partial".to_string()]);
assert_eq!(super::split_lines("\n"), vec![String::new()]);
assert!(super::split_lines("").is_empty());
}
#[test]
fn test9681_records_carry_no_terminal_control_sequences() {
assert_eq!(super::strip_ansi("\x1b[31mred\x1b[0m"), "red");
assert_eq!(
super::strip_ansi("\n\x1b[1m\x1b[0;36m--- MATRIX\x1b[0m"),
"\n--- MATRIX"
);
assert_eq!(super::strip_ansi("row\x1b[K"), "row");
assert_eq!(super::strip_ansi("\x1b[2Aup"), "up");
assert_eq!(super::strip_ansi("\x1b]0;a title\x07after"), "after");
assert_eq!(super::strip_ansi("[not an escape]"), "[not an escape]");
assert_eq!(
super::split_lines("\x1b[32mgreen\x1b[0m\n"),
vec!["green".to_string()]
);
}
fn code_of(line: &str) -> String {
let trimmed = line.trim_start();
if trimmed.starts_with("//") || trimmed.starts_with('*') {
return String::new();
}
let mut code = String::with_capacity(line.len());
let mut in_string = false;
let mut escaped = false;
let mut chars = line.chars().peekable();
while let Some(c) = chars.next() {
if in_string {
if escaped {
escaped = false;
} else if c == '\\' {
escaped = true;
} else if c == '"' {
in_string = false;
}
continue;
}
if c == '"' {
in_string = true;
continue;
}
if c == '/' && chars.peek() == Some(&'/') {
break;
}
code.push(c);
}
code
}
fn contains_call(code: &str, name: &str) -> bool {
let mut from = 0;
while let Some(at) = code[from..].find(name) {
let start = from + at;
let before = code[..start].chars().next_back();
let is_boundary = before.is_none_or(|c| !c.is_alphanumeric() && c != '_' && c != ':');
if is_boundary {
return true;
}
from = start + name.len();
}
false
}
#[test]
fn test12374_a_live_display_is_passed_on_and_its_settled_lines_kept() {
let drawn: &[u8] = b"mg-test: copying 10%\rmg-test: copying 90%\r \rmg-test: copying \x1b[32m5s\x1b[0m\n\n\rError: the source changed: audit/shots/a.png";
let mut lines = SettledLines::default();
let mut settled = Vec::new();
for piece in drawn.chunks(7) {
settled.extend(lines.feed(piece));
}
assert_eq!(settled, vec!["mg-test: copying 5s".to_string()], "one line per settled line, without redraws or colour");
assert_eq!(lines.finish().as_deref(), Some("Error: the source changed: audit/shots/a.png"));
let mut terminal = Vec::new();
pass_live_display(drawn, &mut terminal);
assert_eq!(terminal, drawn, "the terminal did not get what was drawn");
}