use packr::abi::{Value, ValueType};
use packr::Runtime;
use std::path::Path;
const ADD_MODULE: &str = r#"
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
(export "add" (func $add))
)
"#;
#[test]
fn run_add_module() {
let wasm_bytes = wat::parse_str(ADD_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let result = instance
.call_i32_i32_to_i32("add", 2, 3)
.expect("failed to call add");
assert_eq!(result, 5);
assert_eq!(instance.call_i32_i32_to_i32("add", 0, 0).unwrap(), 0);
assert_eq!(instance.call_i32_i32_to_i32("add", -1, 1).unwrap(), 0);
assert_eq!(instance.call_i32_i32_to_i32("add", 100, 200).unwrap(), 300);
}
const ADD64_MODULE: &str = r#"
(module
(func $add64 (param $a i64) (param $b i64) (result i64)
local.get $a
local.get $b
i64.add
)
(export "add64" (func $add64))
)
"#;
#[test]
fn run_add64_module() {
let wasm_bytes = wat::parse_str(ADD64_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let result = instance
.call_i64_i64_to_i64("add64", 1_000_000_000_000, 2_000_000_000_000)
.expect("failed to call add64");
assert_eq!(result, 3_000_000_000_000);
}
#[test]
fn call_missing_function() {
let wasm_bytes = wat::parse_str(ADD_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let err = instance.call_i32_i32_to_i32("nonexistent", 1, 2);
assert!(err.is_err());
}
const SUM_BYTES_MODULE: &str = r#"
(module
(memory (export "memory") 1)
;; Sum bytes from ptr to ptr+len
(func $sum_bytes (param $ptr i32) (param $len i32) (result i32)
(local $sum i32)
(local $end i32)
;; end = ptr + len
(local.set $end (i32.add (local.get $ptr) (local.get $len)))
;; while ptr < end
(block $break
(loop $continue
;; if ptr >= end, break
(br_if $break (i32.ge_u (local.get $ptr) (local.get $end)))
;; sum += *ptr
(local.set $sum
(i32.add
(local.get $sum)
(i32.load8_u (local.get $ptr))))
;; ptr++
(local.set $ptr (i32.add (local.get $ptr) (i32.const 1)))
(br $continue)
)
)
(local.get $sum)
)
(export "sum_bytes" (func $sum_bytes))
)
"#;
#[test]
fn memory_read_write() {
let wasm_bytes = wat::parse_str(SUM_BYTES_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let data = [1u8, 2, 3, 4, 5];
instance
.write_memory(0, &data)
.expect("failed to write memory");
let result = instance
.call_i32_i32_to_i32("sum_bytes", 0, 5)
.expect("failed to call sum_bytes");
assert_eq!(result, 15);
let read_back = instance.read_memory(0, 5).expect("failed to read memory");
assert_eq!(read_back, data);
}
#[test]
fn memory_string_roundtrip() {
let wasm_bytes = wat::parse_str(SUM_BYTES_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let message = "Hello, WebAssembly!";
instance
.write_memory(100, message.as_bytes())
.expect("failed to write string");
let read_back = instance
.read_memory(100, message.len())
.expect("failed to read string");
let read_string = String::from_utf8(read_back).expect("invalid utf8");
assert_eq!(read_string, message);
let sum: i32 = message.as_bytes().iter().map(|&b| b as i32).sum();
let result = instance
.call_i32_i32_to_i32("sum_bytes", 100, message.len() as i32)
.expect("failed to call sum_bytes");
assert_eq!(result, sum);
}
const REVERSE_MODULE: &str = r#"
(module
(memory (export "memory") 1)
;; Reverse bytes from ptr to ptr+len in place
(func $reverse (param $ptr i32) (param $len i32)
(local $left i32)
(local $right i32)
(local $tmp i32)
(local.set $left (local.get $ptr))
(local.set $right (i32.sub (i32.add (local.get $ptr) (local.get $len)) (i32.const 1)))
(block $break
(loop $continue
;; if left >= right, break
(br_if $break (i32.ge_u (local.get $left) (local.get $right)))
;; swap *left and *right
(local.set $tmp (i32.load8_u (local.get $left)))
(i32.store8 (local.get $left) (i32.load8_u (local.get $right)))
(i32.store8 (local.get $right) (local.get $tmp))
;; left++, right--
(local.set $left (i32.add (local.get $left) (i32.const 1)))
(local.set $right (i32.sub (local.get $right) (i32.const 1)))
(br $continue)
)
)
)
(export "reverse" (func $reverse))
)
"#;
#[test]
fn memory_reverse_string() {
let wasm_bytes = wat::parse_str(REVERSE_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let message = "Hello";
instance
.write_memory(0, message.as_bytes())
.expect("failed to write");
instance
.call_i32_i32("reverse", 0, message.len() as i32)
.expect("failed to call reverse");
let result = instance.read_memory(0, 5).expect("read back");
assert_eq!(result, b"olleH");
}
const ECHO_MODULE: &str = r#"
(module
(memory (export "memory") 1)
;; Fixed output buffer location (after result slots at 16KB+8)
(global $output_offset i32 (i32.const 16392))
;; Echo: copy input to output buffer and write ptr/len to result slots
;; Returns 0 on success
(func $echo (param $in_ptr i32) (param $in_len i32) (param $out_ptr_ptr i32) (param $out_len_ptr i32) (result i32)
;; Copy input to output buffer
(memory.copy (global.get $output_offset) (local.get $in_ptr) (local.get $in_len))
;; Write output pointer to the ptr slot
(i32.store (local.get $out_ptr_ptr) (global.get $output_offset))
;; Write output length to the len slot
(i32.store (local.get $out_len_ptr) (local.get $in_len))
;; Return 0 (success)
(i32.const 0)
)
(export "echo" (func $echo))
)
"#;
#[test]
fn graph_abi_echo_roundtrip() {
let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::S64(42);
let output = instance
.call_with_value("echo", &input)
.expect("failed to call echo");
assert_eq!(output, input);
}
#[test]
fn graph_abi_echo_string() {
let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::String("Hello, Graph ABI!".to_string());
let output = instance
.call_with_value("echo", &input)
.expect("failed to call echo");
assert_eq!(output, input);
}
#[test]
fn graph_abi_echo_list() {
let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::List {
elem_type: ValueType::S64,
items: vec![Value::S64(1), Value::S64(2), Value::S64(3)],
};
let output = instance
.call_with_value("echo", &input)
.expect("failed to call echo");
assert_eq!(output, input);
}
#[test]
fn graph_abi_echo_variant() {
let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::Variant {
type_name: "node".to_string(),
case_name: "sym".to_string(),
tag: 1,
payload: vec![Value::String("symbol".to_string())],
};
let output = instance
.call_with_value("echo", &input)
.expect("failed to call echo");
assert_eq!(output, input);
}
#[test]
fn graph_abi_echo_nested() {
let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT");
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load module");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::List {
elem_type: ValueType::Variant("node".to_string()),
items: vec![
Value::Variant {
type_name: "node".to_string(),
case_name: "sym".to_string(),
tag: 0,
payload: vec![Value::String("list".to_string())],
},
Value::List {
elem_type: ValueType::Variant("node".to_string()),
items: vec![
Value::Variant {
type_name: "node".to_string(),
case_name: "sym".to_string(),
tag: 0,
payload: vec![Value::String("a".to_string())],
},
Value::Variant {
type_name: "node".to_string(),
case_name: "num".to_string(),
tag: 1,
payload: vec![Value::S64(42)],
},
],
},
],
};
let output = instance
.call_with_value("echo", &input)
.expect("failed to call echo");
assert_eq!(output, input);
}
fn load_rust_echo_package() -> Vec<u8> {
let wasm_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("packages/echo/target/wasm32-unknown-unknown/release/echo_package.wasm");
std::fs::read(&wasm_path).unwrap_or_else(|e| {
panic!(
"Failed to read Rust package at {}: {}. Run: cd packages/echo && cargo build --target wasm32-unknown-unknown --release",
wasm_path.display(),
e
)
})
}
#[test]
fn rust_package_echo_roundtrip() {
let wasm_bytes = load_rust_echo_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load Rust package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::S64(42);
let output = instance
.call_with_value("echo", &input)
.expect("failed to call echo");
assert_eq!(output, input);
}
#[test]
fn rust_package_echo_complex() {
let wasm_bytes = load_rust_echo_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load Rust package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::List {
elem_type: ValueType::String, items: vec![
Value::String("hello".to_string()),
Value::Variant {
type_name: "node".to_string(),
case_name: "list".to_string(),
tag: 2,
payload: vec![Value::List {
elem_type: ValueType::S64,
items: vec![Value::S64(1), Value::S64(2), Value::S64(3)],
}],
},
Value::Option {
inner_type: ValueType::Bool,
value: Some(Box::new(Value::Bool(true))),
},
],
};
let output = instance
.call_with_value("echo", &input)
.expect("failed to call echo");
assert_eq!(output, input);
}
#[test]
fn rust_package_transform_doubles_s64() {
let wasm_bytes = load_rust_echo_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load Rust package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::S64(21);
let output = instance
.call_with_value("transform", &input)
.expect("failed to call transform");
assert_eq!(output, Value::S64(42));
}
#[test]
fn rust_package_transform_nested() {
let wasm_bytes = load_rust_echo_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load Rust package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::List {
elem_type: ValueType::Variant("node".to_string()),
items: vec![
Value::S64(10),
Value::S64(20),
Value::Variant {
type_name: "node".to_string(),
case_name: "num".to_string(),
tag: 1,
payload: vec![Value::S64(50)],
},
],
};
let expected = Value::List {
elem_type: ValueType::Variant("node".to_string()),
items: vec![
Value::S64(20), Value::S64(40), Value::Variant {
type_name: "node".to_string(),
case_name: "num".to_string(),
tag: 1,
payload: vec![Value::S64(100)], },
],
};
let output = instance
.call_with_value("transform", &input)
.expect("failed to call transform");
assert_eq!(output, expected);
}
#[test]
fn rust_package_transform_preserves_strings() {
let wasm_bytes = load_rust_echo_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load Rust package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = Value::String("hello world".to_string());
let output = instance
.call_with_value("transform", &input)
.expect("failed to call transform");
assert_eq!(output, input);
}
use packr::runtime::HostImports;
fn load_rust_logger_package() -> Vec<u8> {
let wasm_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("packages/logger/target/wasm32-unknown-unknown/release/logger_package.wasm");
std::fs::read(&wasm_path).unwrap_or_else(|e| {
panic!(
"Failed to read logger package at {}: {}. Run: cd packages/logger && cargo build --target wasm32-unknown-unknown --release",
wasm_path.display(),
e
)
})
}
#[test]
fn host_imports_logging() {
let wasm_bytes = load_rust_logger_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load logger package");
let imports = HostImports::new();
let mut instance = module
.instantiate_with_imports(imports)
.expect("failed to instantiate with imports");
let input = Value::S64(42);
let output = instance
.call_with_value("process", &input)
.expect("failed to call process");
assert_eq!(output, Value::S64(84));
let logs = instance.get_logs();
assert!(!logs.is_empty(), "Expected log messages");
assert!(
logs.iter().any(|m| m.contains("starting")),
"Expected 'starting' log"
);
assert!(
logs.iter().any(|m| m.contains("got S64")),
"Expected S64 type log"
);
assert!(
logs.iter().any(|m| m.contains("done")),
"Expected 'done' log"
);
}
#[test]
fn host_imports_logging_with_string() {
let wasm_bytes = load_rust_logger_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load logger package");
let imports = HostImports::new();
let mut instance = module
.instantiate_with_imports(imports)
.expect("failed to instantiate with imports");
let input = Value::String("test message".to_string());
let output = instance
.call_with_value("process", &input)
.expect("failed to call process");
assert_eq!(output, input);
let logs = instance.get_logs();
assert!(
logs.iter().any(|m| m.contains("got String")),
"Expected String type log"
);
assert!(
logs.iter().any(|m| m.contains("test message")),
"Expected the actual string in logs"
);
}
#[test]
fn host_imports_transform_nested() {
let wasm_bytes = load_rust_logger_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load logger package");
let imports = HostImports::new();
let mut instance = module
.instantiate_with_imports(imports)
.expect("failed to instantiate with imports");
let input = Value::List {
elem_type: ValueType::S64,
items: vec![Value::S64(10), Value::S64(20), Value::S64(30)],
};
let expected = Value::List {
elem_type: ValueType::S64,
items: vec![Value::S64(20), Value::S64(40), Value::S64(60)],
};
let output = instance
.call_with_value("process", &input)
.expect("failed to call process");
assert_eq!(output, expected);
let logs = instance.get_logs();
assert!(
logs.iter().any(|m| m.contains("got List")),
"Expected List type log"
);
}
#[test]
fn host_imports_clear_logs() {
let wasm_bytes = load_rust_logger_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load logger package");
let imports = HostImports::new();
let mut instance = module
.instantiate_with_imports(imports)
.expect("failed to instantiate with imports");
let input = Value::S64(1);
let _ = instance.call_with_value("process", &input).unwrap();
let logs1 = instance.get_logs();
assert!(!logs1.is_empty());
instance.clear_logs();
assert!(instance.get_logs().is_empty(), "Logs should be cleared");
let _ = instance.call_with_value("process", &input).unwrap();
let logs2 = instance.get_logs();
assert!(!logs2.is_empty());
assert!(
logs2.len() < logs1.len() * 2,
"Should only have logs from second call"
);
}
fn load_rust_sexpr_package() -> Vec<u8> {
let wasm_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("packages/sexpr/target/wasm32-unknown-unknown/release/sexpr_package.wasm");
std::fs::read(&wasm_path).unwrap_or_else(|e| {
panic!(
"Failed to read sexpr package at {}: {}. Run: cd packages/sexpr && cargo build --target wasm32-unknown-unknown --release",
wasm_path.display(),
e
)
})
}
#[allow(dead_code)]
mod sexpr {
use packr::abi::Value;
pub fn sym(s: &str) -> Value {
Value::Variant {
type_name: "expr".to_string(),
case_name: "sym".to_string(),
tag: 0,
payload: vec![Value::String(s.to_string())],
}
}
pub fn num(n: i64) -> Value {
Value::Variant {
type_name: "expr".to_string(),
case_name: "num".to_string(),
tag: 1,
payload: vec![Value::S64(n)],
}
}
pub fn float(f: f64) -> Value {
Value::Variant {
type_name: "expr".to_string(),
case_name: "float".to_string(),
tag: 2,
payload: vec![Value::F64(f)],
}
}
pub fn boolean(b: bool) -> Value {
Value::Variant {
type_name: "expr".to_string(),
case_name: "bool".to_string(),
tag: 4,
payload: vec![Value::Bool(b)],
}
}
pub fn nil() -> Value {
Value::Variant {
type_name: "expr".to_string(),
case_name: "nil".to_string(),
tag: 5,
payload: vec![],
}
}
pub fn cons(head: Value, tail: Value) -> Value {
Value::Variant {
type_name: "expr".to_string(),
case_name: "cons".to_string(),
tag: 6,
payload: vec![Value::Tuple(vec![head, tail])],
}
}
pub fn list(items: Vec<Value>) -> Value {
let mut result = nil();
for item in items.into_iter().rev() {
result = cons(item, result);
}
result
}
pub fn err(msg: &str) -> Value {
Value::Variant {
type_name: "expr".to_string(),
case_name: "err".to_string(),
tag: 7,
payload: vec![Value::String(msg.to_string())],
}
}
}
#[test]
fn sexpr_eval_simple_addition() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![
sexpr::sym("+"),
sexpr::num(1),
sexpr::num(2),
sexpr::num(3),
]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::num(6));
}
#[test]
fn sexpr_eval_nested_arithmetic() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![
sexpr::sym("*"),
sexpr::list(vec![sexpr::sym("+"), sexpr::num(2), sexpr::num(3)]),
sexpr::list(vec![sexpr::sym("-"), sexpr::num(10), sexpr::num(4)]),
]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::num(30));
}
#[test]
fn sexpr_eval_comparison() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![sexpr::sym("<"), sexpr::num(5), sexpr::num(10)]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::boolean(true));
}
#[test]
fn sexpr_eval_if_true() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![
sexpr::sym("if"),
sexpr::list(vec![sexpr::sym(">"), sexpr::num(10), sexpr::num(5)]),
sexpr::num(42),
sexpr::num(0),
]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::num(42));
}
#[test]
fn sexpr_eval_if_false() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![
sexpr::sym("if"),
sexpr::list(vec![sexpr::sym("<"), sexpr::num(10), sexpr::num(5)]),
sexpr::num(42),
sexpr::num(0),
]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::num(0));
}
#[test]
fn sexpr_eval_list_operations() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![
sexpr::sym("car"),
sexpr::list(vec![
sexpr::sym("list"),
sexpr::num(1),
sexpr::num(2),
sexpr::num(3),
]),
]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::num(1));
}
#[test]
fn sexpr_eval_length() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![
sexpr::sym("length"),
sexpr::list(vec![
sexpr::sym("list"),
sexpr::num(1),
sexpr::num(2),
sexpr::num(3),
sexpr::num(4),
sexpr::num(5),
]),
]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::num(5));
}
#[test]
fn sexpr_eval_complex_expression() {
let wasm_bytes = load_rust_sexpr_package();
let runtime = Runtime::new();
let module = runtime
.load_module(&wasm_bytes)
.expect("failed to load sexpr package");
let mut instance = module.instantiate().expect("failed to instantiate");
let input = sexpr::list(vec![
sexpr::sym("if"),
sexpr::list(vec![
sexpr::sym("and"),
sexpr::list(vec![sexpr::sym(">"), sexpr::num(10), sexpr::num(5)]),
sexpr::list(vec![sexpr::sym("<"), sexpr::num(3), sexpr::num(7)]),
]),
sexpr::list(vec![
sexpr::sym("+"),
sexpr::list(vec![sexpr::sym("*"), sexpr::num(2), sexpr::num(3)]),
sexpr::list(vec![sexpr::sym("/"), sexpr::num(10), sexpr::num(2)]),
]),
sexpr::num(0),
]);
let output = instance
.call_with_value("evaluate", &input)
.expect("failed to call evaluate");
assert_eq!(output, sexpr::num(11));
}