// ============================================================
// Deterministic benchmark and regression harness
//
// This deliberately measures logical work (ticks and outcomes), not wall
// clock time. It is portable across wasm-gc, JS, and native backends and is
// therefore suitable for CI regression checks and reproducible reports.
// ============================================================
///|
/// Counts the observable outcomes of one benchmark run.
pub struct BenchmarkResult {
name : String
frames : Int
successes : Int
failures : Int
running : Int
}
///|
/// Execute exactly `frames` ticks and collect outcome counts.
pub fn benchmark_node(
name : String,
root : Node,
bb : Blackboard,
frames : Int,
) -> BenchmarkResult {
let mut successes = 0
let mut failures = 0
let mut running = 0
let limit = if frames > 0 { frames } else { 0 }
let mut i = 0
while i < limit {
match root.tick(bb) {
Status::BTSuccess => successes = successes + 1
Status::BTFailure => failures = failures + 1
Status::BTRunning => running = running + 1
}
i = i + 1
}
{ name, frames: limit, successes, failures, running }
}
///|
/// Access the scenario name.
pub fn BenchmarkResult::name(self : BenchmarkResult) -> String {
self.name
}
///|
/// Access the number of executed frames.
pub fn BenchmarkResult::frames(self : BenchmarkResult) -> Int {
self.frames
}
///|
/// Number of successful frames.
pub fn BenchmarkResult::successes(self : BenchmarkResult) -> Int {
self.successes
}
///|
/// Number of failed frames.
pub fn BenchmarkResult::failures(self : BenchmarkResult) -> Int {
self.failures
}
///|
/// Number of running frames.
pub fn BenchmarkResult::running(self : BenchmarkResult) -> Int {
self.running
}
///|
/// Return a stable CSV row for benchmark archives.
pub fn BenchmarkResult::to_csv(self : BenchmarkResult) -> String {
"\{self.name},\{self.frames},\{self.successes},\{self.failures},\{self.running}"
}
///|
/// Return a compact human-readable summary.
pub fn BenchmarkResult::summary(self : BenchmarkResult) -> String {
"\{self.name}: frames=\{self.frames}, success=\{self.successes}, failure=\{self.failures}, running=\{self.running}"
}
///|
/// Build a deterministic mixed workload used by examples and CI.
pub fn benchmark_workload() -> Node {
sequence_node([
condition_node(fn(bb) { bb.get_bool("enabled").unwrap_or(false) }),
stable_success_node(
action_node(fn(bb) {
let n = bb.get_int("work").unwrap_or(0)
bb.set_int("work", n + 1)
Status::BTSuccess
}),
2,
),
cooldown_node(action_node(fn(_) { Status::BTSuccess }), 1),
])
}