use cu_profiler_core::Profiler;
use cu_profiler_core::backend::RecordedLogsBackend;
use cu_profiler_core::metadata::RunMetadata;
use cu_profiler_core::parser::{self, analyze};
use cu_profiler_core::program_registry::ProgramRegistry;
use cu_profiler_core::scenario::Scenario;
const FRAGMENTS: &[&str] = &[
"Program X invoke [1]",
"Program X invoke [2]",
"Program X invoke [99]",
"Program X invoke [notanumber]",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]",
"Program X consumed 1200 of 200000 compute units",
"Program X consumed of compute units",
"Program X consumed abc of 200000 compute units",
"Program X consumed 99999999999999999999999999 of 1 compute units",
"Program X success",
"Program X failed: custom program error: 0x1",
"Program failed to complete",
"Program log: hello world",
"Program log: CU_PROFILER_BEGIN name=a cu=200000",
"Program log: CU_PROFILER_END name=a cu=188000",
"Program log: CU_PROFILER_BEGIN name=b",
"Program log: CU_PROFILER_END name=zzz",
"Program log: CU_PROFILER_POINT name=mid cu=190000",
"Program data: AAAA",
"ComputeBudget111111111111111111111111111111 invoke [1]",
"totally unstructured %%% line",
"",
"Program ",
"consumed 5 of 10 compute units",
];
struct Lcg(u64);
impl Lcg {
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.0
}
fn pick<'a, T>(&mut self, slice: &'a [T]) -> &'a T {
&slice[(self.next() as usize) % slice.len()]
}
fn range(&mut self, n: usize) -> usize {
(self.next() as usize) % n
}
}
fn random_log(rng: &mut Lcg) -> Vec<String> {
let len = rng.range(40);
(0..len)
.map(|_| (*rng.pick(FRAGMENTS)).to_string())
.collect()
}
#[test]
fn analyze_never_panics_and_holds_invariants() {
let registry = ProgramRegistry::with_builtins();
let mut rng = Lcg(0x1234_5678_9abc_def0);
for _ in 0..4000 {
let logs = random_log(&mut rng);
let a = analyze(&logs, ®istry);
assert!(
a.unattributed_pct.is_finite(),
"non-finite unattributed_pct for logs: {logs:?}"
);
assert!(
(0.0..=100.0).contains(&a.unattributed_pct),
"unattributed_pct out of range ({}) for logs: {logs:?}",
a.unattributed_pct
);
for s in &a.scopes {
if let Some(p) = s.percentage_of_total {
assert!(p.is_finite() && (0.0..=100.0).contains(&p));
}
}
assert!(a.cpi_count <= 1_000);
}
}
#[test]
fn full_pipeline_and_json_round_trip_never_panic() {
let mut rng = Lcg(0xfeed_face_dead_beef);
for i in 0..1500 {
let logs = random_log(&mut rng);
let mut backend = RecordedLogsBackend::new();
let name = format!("fuzz_{i}");
backend.insert(name.clone(), logs, true);
let report = Profiler::new().run(
&backend,
&[Scenario::new(&name)],
None,
RunMetadata::recorded("0.0.0-fuzz"),
);
let json = serde_json::to_string(&report).expect("report serializes");
let back: cu_profiler_core::model::Report =
serde_json::from_str(&json).expect("report round-trips");
assert_eq!(report, back);
}
}
#[test]
fn pathological_inputs_are_handled() {
let registry = ProgramRegistry::with_builtins();
let deep: Vec<String> = (0..500)
.map(|i| format!("Program P{i} invoke [{}]", i + 1))
.collect();
let a = analyze(&deep, ®istry);
assert!(a.call_tree.children.len() <= 1);
let flat: Vec<String> = std::iter::repeat_n("Program X success".to_string(), 10_000).collect();
let _ = parser::analyze(&flat, ®istry); }