use anyhow::Result;
use extism_pdk::{Memory, ToMemory};
mod harness {
#[link(wasm_import_module = "xtp:test/harness")]
extern "C" {
pub fn call(name: u64, input: u64) -> u64;
pub fn time(name: u64, input: u64) -> u64;
pub fn assert(name: u64, value: u64);
pub fn reset();
pub fn group(name: u64);
}
}
pub fn call(func_name: impl AsRef<str>, input: impl ToMemory) -> Result<Memory> {
let func_name = func_name.as_ref();
let func_mem = Memory::from_bytes(func_name)?;
let input_mem = input.to_memory()?;
let output_ptr = unsafe { harness::call(func_mem.offset(), input_mem.offset()) };
func_mem.free();
input_mem.free();
let output = match Memory::find(output_ptr) {
None => anyhow::bail!("Error in call to {func_name}: invalid output offset"),
Some(x) => x,
};
Ok(output)
}
pub fn call_bytes(func_name: impl AsRef<str>, input: impl ToMemory) -> Result<Vec<u8>> {
let output_mem = call(func_name, input)?;
let output = output_mem.to_vec();
output_mem.free();
Ok(output)
}
pub fn call_string(func_name: impl AsRef<str>, input: impl ToMemory) -> Result<String> {
let output_mem = call(func_name, input)?;
let output = output_mem.to_string()?;
output_mem.free();
Ok(output)
}
pub fn time_ns(func_name: impl AsRef<str>, input: impl ToMemory) -> Result<u64> {
let func_name = func_name.as_ref();
let func_mem = Memory::from_bytes(func_name)?;
let input_mem = input.to_memory()?;
let ns = unsafe { harness::time(func_mem.offset(), input_mem.offset()) };
func_mem.free();
input_mem.free();
Ok(ns)
}
pub fn time_sec(func_name: impl AsRef<str>, input: impl ToMemory) -> Result<f64> {
time_ns(func_name, input).map(|x| x as f64 / 1e9)
}
pub fn assert(msg: impl AsRef<str>, outcome: bool) {
let msg_mem = Memory::from_bytes(msg.as_ref()).expect("assert message Extism memory");
unsafe {
harness::assert(msg_mem.offset(), outcome as u64);
}
msg_mem.free();
}
pub fn assert_eq<U, T: PartialEq<U>>(msg: impl AsRef<str>, x: T, y: U) {
assert(msg, x == y);
}
pub fn assert_ne<U, T: PartialEq<U>>(msg: impl AsRef<str>, x: T, y: U) {
assert(msg, x != y);
}
fn start_group(name: impl AsRef<str>) {
let name_mem = Memory::from_bytes(name.as_ref()).expect("assert message Extism memory");
unsafe {
harness::group(name_mem.offset());
}
name_mem.free();
}
pub fn reset() {
unsafe {
harness::reset();
}
}
pub fn group(name: impl AsRef<str>, f: impl FnOnce() -> Result<()>) -> Result<()> {
reset();
start_group(name);
let res = f();
reset();
res
}