Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Rust build artifacts
/target

# cargo-mutants output
/mutants.out/
/mutants.out.old/

# Editor / OS noise
*.rs.bk
*.swp
Expand Down
48 changes: 48 additions & 0 deletions crates/cu-profiler-cli/src/commands/baseline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,51 @@ pub fn approve(args: &BaselineApproveArgs, quiet: bool) -> Result<ExitCode> {
)))
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::args::{BaselineSaveArgs, CommonRun};

#[test]
fn save_records_simulated_scenarios_and_skips_unsimulated() {
let base = std::env::temp_dir().join(format!("cu-bl-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let logs = base.join(".cu").join("logs");
std::fs::create_dir_all(&logs).unwrap();
// Two scenarios; only `good` has a log, so `missing` simulates to Unknown
// and must NOT be written to the baseline.
std::fs::write(
base.join("cu-profiler.toml"),
"[project]\nname=\"t\"\n[scenario.good]\n[scenario.missing]\n",
)
.unwrap();
std::fs::write(
logs.join("good.log"),
"Program P invoke [1]\nProgram P consumed 1000 of 200000 compute units\nProgram P success",
)
.unwrap();
let baseline = base.join("baseline.json");
let args = BaselineSaveArgs {
common: CommonRun {
config: base.join("cu-profiler.toml"),
logs_dir: logs,
scenarios: vec![],
tags: vec![],
samples: None,
},
baseline: baseline.clone(),
};
save(&args, true).expect("baseline save");
let store = BaselineStore::load(&baseline).unwrap();
let _ = std::fs::remove_dir_all(&base);
assert!(
store.get("good").is_some(),
"simulated scenario must be recorded"
);
assert!(
store.get("missing").is_none(),
"unsimulated (Unknown) scenario must be skipped"
);
}
}
2 changes: 1 addition & 1 deletion crates/cu-profiler-cli/src/commands/bench.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! `cu-profiler bench` — turnkey real-CU path.
//!
//! `bench` validates a declarative [`BenchPlan`](cu_profiler_core::bench::BenchPlan),
//! `bench` validates a declarative [`BenchPlan`],
//! optionally builds the program with `cargo build-sbf`, then **delegates the real
//! Mollusk measurement** to the Linux-only `cu-profiler-bench` executor, found over
//! `PATH` (a runtime sibling, never a build dependency — so the main CLI keeps the
Expand Down
140 changes: 140 additions & 0 deletions crates/cu-profiler-cli/src/commands/ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,143 @@ fn write_artifact(path: &std::path::Path, contents: &str) -> Result<()> {
std::fs::write(path, contents)?;
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use crate::args::CommonRun;
use std::path::PathBuf;

fn temp_project(config_toml: &str, scenario: &str, log: &str) -> (PathBuf, RunArgs) {
let base = std::env::temp_dir().join(format!("cu-ci-{}-{scenario}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let logs = base.join(".cu").join("logs");
std::fs::create_dir_all(&logs).unwrap();
std::fs::write(base.join("cu-profiler.toml"), config_toml).unwrap();
std::fs::write(logs.join(format!("{scenario}.log")), log).unwrap();
let args = RunArgs {
common: CommonRun {
config: base.join("cu-profiler.toml"),
logs_dir: logs,
scenarios: vec![],
tags: vec![],
samples: None,
},
format: None,
output: None,
baseline: None,
strict: false,
fail_on_budget: false,
fail_on_regression: false,
fail_on_low_confidence: false,
};
(base, args)
}

#[test]
fn ci_config_default_enforces_budget() {
let config = "[project]\nname=\"t\"\n[defaults]\nfail_on_budget=true\n\
[scenario.over]\nbudget=100000\n";
let log = "Program P invoke [1]\n\
Program P consumed 120000 of 200000 compute units\nProgram P success";
let (base, args) = temp_project(config, "over", log);
let code = run(&args, true);
let _ = std::fs::remove_dir_all(&base);
assert_eq!(code.unwrap(), ExitCode::BudgetOrRegression);
}

#[test]
fn ci_strict_fails_low_confidence() {
let config = "[project]\nname=\"t\"\n[scenario.shaky]\n";
let log = "Program P invoke [1]\nProgram log: CU_PROFILER_BEGIN name=x\n\
Program P consumed 1000 of 200000 compute units\nProgram P success";
let (base, mut args) = temp_project(config, "shaky", log);
args.strict = true;
let code = run(&args, true);
let _ = std::fs::remove_dir_all(&base);
assert_eq!(code.unwrap(), ExitCode::LowConfidence);
}

#[test]
fn ci_config_default_enforces_regression() {
// Record a baseline, then re-run a higher-consuming log. `fail_on_regression`
// comes only from the config default (no CLI flag), so the `||` between the
// flag and the config must hold — an `||`→`&&` flip would mask the regression.
use crate::args::BaselineSaveArgs;

let config = "[project]\nname=\"t\"\n[defaults]\n\
fail_on_regression=true\nmax_regression_pct=5\n[scenario.s]\n";
let (base, mut args) = temp_project(
config,
"s",
"Program P invoke [1]\nProgram P consumed 90000 of 200000 compute units\nProgram P success",
);
let baseline = base.join("baseline.json");

// 1) Record the baseline at 90k CU.
let save_args = BaselineSaveArgs {
common: args.common.clone(),
baseline: baseline.clone(),
};
crate::commands::baseline_save(&save_args, true).expect("baseline save");

// 2) Bump the measured CU to 110k (+22% > 5% allowance).
std::fs::write(
args.common.logs_dir.join("s.log"),
"Program P invoke [1]\nProgram P consumed 110000 of 200000 compute units\nProgram P success",
)
.unwrap();

// 3) Re-run via `ci`; the regression must fail from the config default alone.
args.baseline = Some(baseline);
let code = run(&args, true);
let _ = std::fs::remove_dir_all(&base);
assert_eq!(code.unwrap(), ExitCode::BudgetOrRegression);
}

#[test]
fn ci_writes_configured_artifacts_into_nested_dirs() {
// A configured json_path with a not-yet-existing parent must be written.
// Guards both `write_artifact` and its parent-creation branch. Uses an
// absolute path (forward slashes are valid TOML everywhere) so the test
// never touches the process CWD — safe under parallel test execution.
let base = std::env::temp_dir().join(format!("cu-ci-art-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let logs = base.join(".cu").join("logs");
std::fs::create_dir_all(&logs).unwrap();
let json = base.join("artifacts").join("report.json"); // nested, absent
let json_toml = json.to_string_lossy().replace('\\', "/");
let config = format!(
"[project]\nname=\"t\"\n\
[output]\ndefault_format=\"table\"\njson_path=\"{json_toml}\"\n\
[scenario.s]\n"
);
std::fs::write(base.join("cu-profiler.toml"), &config).unwrap();
std::fs::write(
logs.join("s.log"),
"Program P invoke [1]\nProgram P consumed 1000 of 200000 compute units\nProgram P success",
)
.unwrap();
let args = RunArgs {
common: CommonRun {
config: base.join("cu-profiler.toml"),
logs_dir: logs,
scenarios: vec![],
tags: vec![],
samples: None,
},
format: None,
output: None,
baseline: None,
strict: false,
fail_on_budget: false,
fail_on_regression: false,
fail_on_low_confidence: false,
};
let code = run(&args, true);
let exists = json.exists();
let _ = std::fs::remove_dir_all(&base);
assert_eq!(code.unwrap(), ExitCode::Success);
assert!(exists, "ci did not write the configured json artifact");
}
}
35 changes: 35 additions & 0 deletions crates/cu-profiler-cli/src/commands/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,39 @@ mod tests {
assert_eq!(find_in_page(&comments, "cu-profiler-report"), Some(2));
assert_eq!(find_in_page(&comments, "other-marker"), None);
}

fn comment_args() -> CommentArgs {
use crate::args::CommonRun;
CommentArgs {
common: CommonRun {
config: "cu-profiler.toml".into(),
logs_dir: ".cu/logs".into(),
scenarios: vec![],
tags: vec![],
samples: None,
},
input: None,
pr: None,
repo: None,
marker: "cu-profiler-report".into(),
dry_run: false,
}
}

#[test]
fn resolve_repo_uses_the_explicit_slug() {
let mut args = comment_args();
args.repo = Some("my-org/my-repo".into());
assert_eq!(
resolve_repo(&args).unwrap(),
("my-org".to_string(), "my-repo".to_string())
);
}

#[test]
fn resolve_pr_prefers_the_explicit_flag() {
let mut args = comment_args();
args.pr = Some(42);
assert_eq!(resolve_pr(&args), Some(42));
}
}
62 changes: 62 additions & 0 deletions crates/cu-profiler-cli/src/commands/explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,66 @@ mod tests {
assert!(text.contains("Confidence:"));
assert!(text.contains("near_budget_limit") || text.contains("near its compute budget"));
}

#[test]
fn explain_text_quantifies_a_scope_with_a_cu_snapshot() {
// A scope carrying both a CU estimate and a percentage must render the
// quantified line ("… CU (…%, …)"), not the "CU unknown" fallback.
let mut backend = RecordedLogsBackend::new();
backend.insert_blob(
"swap",
"Program User111 invoke [1]\n\
Program log: CU_PROFILER_BEGIN name=validate cu=200000\n\
Program log: CU_PROFILER_END name=validate cu=188000\n\
Program User111 consumed 96000 of 100000 compute units\n\
Program User111 success",
true,
);
let report = Profiler::new().run(
&backend,
&[Scenario::new("swap")],
None,
RunMetadata::recorded("0.1.0"),
);
let text = explain_text(&report.scenarios[0]);
assert!(text.contains("CU ("), "scope CU/percentage missing: {text}");
assert!(!text.contains("validate (parent: -) — CU unknown"));
}

#[test]
fn run_finds_and_explains_the_requested_scenario() {
// Drives `run()` end-to-end so the `s.name == args.scenario` lookup is
// exercised: the narrowed run profiles only the requested scenario, so an
// `==`→`!=` flip finds nothing and returns an error instead of Success.
use crate::args::CommonRun;

let base = std::env::temp_dir().join(format!("cu-explain-run-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let logs = base.join(".cu").join("logs");
std::fs::create_dir_all(&logs).unwrap();
// Two scenarios so the lookup must select by name, not by position.
std::fs::write(
base.join("cu-profiler.toml"),
"[project]\nname=\"t\"\n[scenario.a]\n[scenario.b]\n",
)
.unwrap();
std::fs::write(
logs.join("b.log"),
"Program P invoke [1]\nProgram P consumed 1000 of 200000 compute units\nProgram P success",
)
.unwrap();
let args = ExplainArgs {
scenario: "b".into(),
common: CommonRun {
config: base.join("cu-profiler.toml"),
logs_dir: logs,
scenarios: vec![],
tags: vec![],
samples: None,
},
};
let code = run(&args, true);
let _ = std::fs::remove_dir_all(&base);
assert_eq!(code.unwrap(), ExitCode::Success);
}
}
36 changes: 36 additions & 0 deletions crates/cu-profiler-cli/src/commands/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,40 @@ mod tests {
let err = logs_from_response(&v, "SIG", "rpc").unwrap_err();
assert!(err.to_string().contains("Invalid param"));
}

#[cfg(feature = "remote")]
#[test]
fn max_rpc_bytes_is_thirty_two_mib() {
assert_eq!(MAX_RPC_BYTES, 33_554_432); // 32 * 1024 * 1024
}

#[test]
fn import_from_file_writes_log_into_a_nested_logs_dir() {
use crate::args::ImportArgs;
let base = std::env::temp_dir().join(format!("cu-import-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
std::fs::create_dir_all(&base).unwrap();
let json = base.join("tx.json");
std::fs::write(
&json,
r#"{"result":{"meta":{"logMessages":["Program P invoke [1]","Program P success"]}}}"#,
)
.unwrap();
// logs_dir has a parent component that does not exist yet → exercises the
// create_dir_all branch.
let logs_dir = base.join("nested").join("logs");
let args = ImportArgs {
file: Some(json),
signature: None,
rpc: "unused".into(),
commitment: "confirmed".into(),
name: Some("mytx".into()),
logs_dir: logs_dir.clone(),
};
let code = run(&args, true).expect("import from file");
assert_eq!(code, ExitCode::Success);
let written = std::fs::read_to_string(logs_dir.join("mytx.log")).unwrap();
assert!(written.contains("Program P invoke [1]"));
let _ = std::fs::remove_dir_all(&base);
}
}
Loading