use std::any::Any;
use std::panic::{catch_unwind, AssertUnwindSafe};
use super::{EngineResult, EngineStatus};
use crate::pipeline::execution::InputCase;
use crate::spec::types::OpSpec;
pub(super) fn run_engine_harnesses(
backend: &dyn vyre::VyreBackend,
specs: &[OpSpec],
) -> Vec<EngineResult> {
vec![
guarded_engine_result("determinism", || determinism_result(backend, specs)),
guarded_engine_result("barrier", || barrier_result(backend)),
guarded_engine_result("atomics", || atomics_result(backend)),
guarded_engine_result("wire_format", || wire_format_result(backend)),
]
}
fn guarded_engine_result<F>(id: &str, run: F) -> EngineResult
where
F: FnOnce() -> EngineResult,
{
match catch_unwind(AssertUnwindSafe(run)) {
Ok(result) => result,
Err(payload) => engine_result(
id,
id,
vec![format!(
"Fix: engine invariant harness `{id}` panicked: {}.",
panic_message(payload.as_ref())
)],
0,
),
}
}
fn panic_message(payload: &(dyn Any + Send)) -> String {
if let Some(message) = payload.downcast_ref::<&str>() {
return (*message).to_string();
}
if let Some(message) = payload.downcast_ref::<String>() {
return message.clone();
}
"non-string panic payload".to_string()
}
fn determinism_result(backend: &dyn vyre::VyreBackend, specs: &[OpSpec]) -> EngineResult {
let mut failures = Vec::new();
let mut cases = 0_u64;
for spec in specs {
let input_len = spec.signature.min_input_bytes().max(4);
let input = InputCase::new("certificate-engine", "zero".to_string(), vec![0; input_len]);
let report = crate::enforce::enforcers::determinism::enforce_determinism(
backend,
spec,
&[input],
10,
);
cases = cases.saturating_add(10);
let op_id = report.op_id;
failures.extend(
report
.divergences
.into_iter()
.map(|d| format!("{op_id}: {}", d.message)),
);
}
engine_result("determinism", "Deterministic", failures, cases)
}
fn barrier_result(backend: &dyn vyre::VyreBackend) -> EngineResult {
let barrier = crate::enforce::enforcers::barrier::enforce_barrier(backend);
let failures = barrier
.passes
.iter()
.filter(|p| {
!matches!(
p.verdict,
crate::enforce::enforcers::barrier::BarrierVerdict::Passed
)
})
.map(|p| format!("{}: {:?}", p.pass_name, p.verdict))
.collect();
engine_result("barrier", "I8", failures, barrier.passes.len() as u64)
}
fn atomics_result(backend: &dyn vyre::VyreBackend) -> EngineResult {
let atomics = crate::enforce::enforcers::atomics::enforce_atomics(backend);
let failures = atomics.findings.iter().map(|f| f.message.clone()).collect();
engine_result("atomics", "I7", failures, atomics.findings.len() as u64)
}
fn wire_format_result(backend: &dyn vyre::VyreBackend) -> EngineResult {
let wire = crate::enforce::enforcers::wire_format_eq::enforce_wire_format_equivalence(backend);
let failures = wire.violations.iter().map(|v| format!("{v:?}")).collect();
engine_result("wire_format", "I4", failures, wire.tested as u64)
}
fn engine_result(
id: &str,
invariant: &str,
failures: Vec<String>,
cases_tested: u64,
) -> EngineResult {
let status = if failures.is_empty() {
EngineStatus::Pass
} else {
EngineStatus::Fail
};
EngineResult {
id: id.to_string(),
status,
invariants_verified: if status == EngineStatus::Pass {
vec![invariant.to_string()]
} else {
Vec::new()
},
invariants_failed: failures,
cases_tested,
}
}