use std::path::PathBuf;
use std::process::Command;
const BIN: &str = env!("CARGO_BIN_EXE_cu-profiler");
fn scratch_dir(tag: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!("cu-profiler-it-{}-{tag}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn run(dir: &PathBuf, args: &[&str]) -> std::process::Output {
Command::new(BIN)
.args(args)
.current_dir(dir)
.output()
.expect("binary runs")
}
#[test]
fn init_then_run_reports_table_and_exits_zero() {
let dir = scratch_dir("run");
let init = run(&dir, &["init"]);
assert!(init.status.success(), "init failed: {init:?}");
let out = run(&dir, &["run"]);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.contains("swap_exact_in"), "stdout: {stdout}");
assert!(stdout.contains("Status"));
}
#[test]
fn baseline_save_then_regression_compare_exits_one() {
let dir = scratch_dir("regress");
assert!(run(&dir, &["init"]).status.success());
assert!(run(&dir, &["baseline", "save"]).status.success());
let log = dir.join(".cu/logs/swap_exact_in.log");
let text = std::fs::read_to_string(&log)
.unwrap()
.replace("96812", "120000");
std::fs::write(&log, text).unwrap();
let out = run(&dir, &["compare"]);
assert_eq!(
out.status.code(),
Some(1),
"expected budget/regression exit code 1"
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.contains("FAIL"));
}
#[test]
fn compare_without_baseline_is_missing_baseline_exit_four() {
let dir = scratch_dir("nobaseline");
assert!(run(&dir, &["init"]).status.success());
let out = run(&dir, &["compare"]);
assert_eq!(
out.status.code(),
Some(4),
"expected missing-baseline exit code 4"
);
}
#[test]
fn missing_config_is_config_error_exit_two() {
let dir = scratch_dir("noconfig");
let out = run(&dir, &["run"]);
assert_eq!(
out.status.code(),
Some(2),
"expected config error exit code 2"
);
}
#[test]
fn json_output_then_inspect_round_trips() {
let dir = scratch_dir("inspect");
assert!(run(&dir, &["init"]).status.success());
let out = run(
&dir,
&["run", "--format", "json", "--output", "report.json"],
);
assert!(out.status.success());
let inspect = run(&dir, &["inspect", "report.json"]);
assert!(inspect.status.success());
let stdout = String::from_utf8_lossy(&inspect.stdout);
assert!(stdout.contains("swap_exact_in"));
}
#[test]
fn demo_fixtures_warn_on_stderr_not_stdout() {
let dir = scratch_dir("demo-warn");
assert!(run(&dir, &["init"]).status.success());
let out = run(&dir, &["run"]);
assert!(out.status.success());
let stderr = String::from_utf8_lossy(&out.stderr);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stderr.contains("DEMO"),
"expected demo warning on stderr: {stderr}"
);
assert!(
!stdout.contains("DEMO"),
"warning leaked into stdout: {stdout}"
);
}
#[test]
fn real_logs_emit_no_demo_warning() {
let dir = scratch_dir("real-nowarn");
assert!(run(&dir, &["init"]).status.success());
std::fs::write(
dir.join(".cu/logs/swap_exact_in.log"),
"Program P invoke [1]\nProgram P consumed 1234 of 200000 compute units\nProgram P success\n",
)
.unwrap();
let out = run(&dir, &["run", "--scenario", "swap_exact_in"]);
assert!(out.status.success());
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
!stderr.contains("DEMO"),
"unexpected demo warning for real logs: {stderr}"
);
}
#[test]
fn import_real_tx_json_then_run_measures_it() {
let dir = scratch_dir("import");
assert!(run(&dir, &["init"]).status.success());
let tx = r#"{"result":{"meta":{"logMessages":[
"Program Vote111 invoke [1]",
"Program Vote111 consumed 4321 of 200000 compute units",
"Program Vote111 success"
]}}}"#;
std::fs::write(dir.join("tx.json"), tx).unwrap();
let imp = run(&dir, &["import", "tx.json", "--name", "real_vote"]);
assert!(imp.status.success(), "import failed: {imp:?}");
assert!(dir.join(".cu/logs/real_vote.log").exists());
let cfg = dir.join("cu-profiler.toml");
let mut text = std::fs::read_to_string(&cfg).unwrap();
text.push_str("\n[scenario.real_vote]\nbudget = 200000\n");
std::fs::write(&cfg, text).unwrap();
let out = run(&dir, &["run", "--scenario", "real_vote"]);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("4,321"),
"expected imported CU in report: {stdout}"
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
!stderr.contains("DEMO"),
"unexpected demo warning: {stderr}"
);
}
#[cfg(feature = "anchor")]
#[test]
fn anchor_idl_labels_program_in_report() {
let dir = scratch_dir("anchor");
assert!(run(&dir, &["init"]).status.success());
std::fs::write(
dir.join("amm.idl.json"),
r#"{"address":"SwapPRogram1111111111111111111111111111","metadata":{"name":"amm"},"instructions":[],"errors":[]}"#,
)
.unwrap();
let cfg = dir.join("cu-profiler.toml");
let mut text = std::fs::read_to_string(&cfg).unwrap();
text.push_str("\n[anchor]\nidl = \"amm.idl.json\"\n");
std::fs::write(&cfg, text).unwrap();
let out = run(
&dir,
&["run", "--scenario", "swap_exact_in", "--format", "json"],
);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("\"label\": \"amm\""),
"IDL label missing:\n{stdout}"
);
}