#![allow(clippy::indexing_slicing)]
#![allow(clippy::panic)]
#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::process::Command;
const GOLDEN_TRACES_DIR: &str = "tests/golden_traces";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GoldenTrace {
pub command: Vec<String>,
pub workdir: PathBuf,
pub syscall_counts: HashMap<String, u64>,
pub file_operations: Vec<FileOperation>,
pub total_syscalls: u64,
pub renacer_version: String,
pub captured_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileOperation {
pub syscall: String,
pub path: PathBuf,
pub flags: Option<String>,
}
pub fn capture_golden_trace(
name: &str,
command: &[&str],
workdir: Option<&Path>,
) -> Result<GoldenTrace> {
let workdir = workdir.unwrap_or_else(|| Path::new("."));
std::fs::create_dir_all(GOLDEN_TRACES_DIR)
.context("Failed to create golden traces directory")?;
let output = Command::new("renacer")
.args(["--format", "json", "--summary", "--"])
.args(command)
.current_dir(workdir)
.output()
.context("Failed to execute renacer")?;
if !output.status.success() {
anyhow::bail!(
"Renacer command failed:\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
let trace_json =
String::from_utf8(output.stdout).context("Renacer output is not valid UTF-8")?;
let golden = GoldenTrace {
command: command.iter().map(|s| s.to_string()).collect(),
workdir: workdir.to_path_buf(),
syscall_counts: parse_syscall_summary(&trace_json)?,
file_operations: vec![], total_syscalls: 0, renacer_version: "0.6.2".to_string(),
captured_at: chrono::Utc::now().to_rfc3339(),
};
let golden_path = Path::new(GOLDEN_TRACES_DIR).join(format!("{}.json", name));
let golden_json =
serde_json::to_string_pretty(&golden).context("Failed to serialize golden trace")?;
std::fs::write(&golden_path, golden_json).context("Failed to write golden trace")?;
eprintln!("✅ Golden trace captured: {}", golden_path.display());
Ok(golden)
}
pub fn load_golden_trace(name: &str) -> Result<GoldenTrace> {
let golden_path = Path::new(GOLDEN_TRACES_DIR).join(format!("{}.json", name));
let golden_json = std::fs::read_to_string(&golden_path)
.with_context(|| format!("Failed to read golden trace: {}", golden_path.display()))?;
serde_json::from_str(&golden_json)
.with_context(|| format!("Failed to parse golden trace: {}", golden_path.display()))
}
pub fn compare_against_golden(name: &str, command: &[&str], workdir: Option<&Path>) -> Result<()> {
let golden = load_golden_trace(name)?;
let workdir = workdir.unwrap_or_else(|| Path::new("."));
let output = Command::new("renacer")
.args(["--format", "json", "--summary", "--"])
.args(command)
.current_dir(workdir)
.output()
.context("Failed to execute renacer")?;
if !output.status.success() {
anyhow::bail!("Renacer command failed");
}
let trace_json = String::from_utf8(output.stdout)?;
let current_syscalls = parse_syscall_summary(&trace_json)?;
let mut differences = vec![];
for (syscall, count) in ¤t_syscalls {
if !golden.syscall_counts.contains_key(syscall) {
differences.push(format!("NEW SYSCALL: {} (count: {})", syscall, count));
}
}
for (syscall, golden_count) in &golden.syscall_counts {
match current_syscalls.get(syscall) {
None => differences.push(format!(
"REMOVED SYSCALL: {} (was: {})",
syscall, golden_count
)),
Some(current_count) if current_count != golden_count => {
differences.push(format!(
"CHANGED COUNT: {} (was: {}, now: {})",
syscall, golden_count, current_count
));
}
_ => {}
}
}
if differences.is_empty() {
eprintln!("✅ Trace matches golden: {}", name);
Ok(())
} else {
eprintln!("❌ Trace differs from golden: {}", name);
for diff in &differences {
eprintln!(" - {}", diff);
}
anyhow::bail!("Syscall pattern regression detected")
}
}
fn parse_syscall_summary(_json: &str) -> Result<HashMap<String, u64>> {
Ok(HashMap::new())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] fn capture_bashrs_version_trace() {
let result = capture_golden_trace(
"bashrs_version",
&["cargo", "run", "--bin", "bashrs", "--", "--version"],
None,
);
assert!(
result.is_ok(),
"Failed to capture golden trace: {:?}",
result
);
}
#[test]
#[ignore] fn compare_bashrs_version_trace() {
let result = compare_against_golden(
"bashrs_version",
&["cargo", "run", "--bin", "bashrs", "--", "--version"],
None,
);
assert!(result.is_ok(), "Trace regression detected: {:?}", result);
}
#[test]
#[ignore]
fn capture_bashrs_parse_trace() {
let result = capture_golden_trace(
"bashrs_parse_hello",
&[
"cargo",
"run",
"--bin",
"bashrs",
"--",
"parse",
"examples/hello.rs",
],
None,
);
assert!(
result.is_ok(),
"Failed to capture golden trace: {:?}",
result
);
}
#[test]
fn golden_traces_dir_exists() {
std::fs::create_dir_all(GOLDEN_TRACES_DIR).expect("Failed to create directory");
assert!(Path::new(GOLDEN_TRACES_DIR).exists());
}
}