use vyre::ir::{BufferDecl, DataType as IrDataType, Expr, Node, Program};
use vyre::Error;
use vyre_conform::enforce::category::Category;
use vyre_conform::oracles::cpu_ref::CpuReferenceOracle;
use vyre_conform::oracles::spec_table::SpecTableOracle;
use vyre_conform::oracles::{Oracle, Verdict};
use vyre_conform::reference::interp;
use vyre_conform::spec::Value;
use vyre_conform::spec::{OracleKind, SpecRow, SpecSource};
use vyre_conform::types::{DataType, OpSignature, OpSpec, Strictness};
static ROW_INPUT: &[u8] = &[1, 0, 0, 0];
static ROW_EXPECTED: &[u8] = &[2, 0, 0, 0];
static ROW_INPUTS: &[&[u8]] = &[ROW_INPUT];
static SPEC_ROWS: &[SpecRow] = &[SpecRow::new(
ROW_INPUTS,
ROW_EXPECTED,
"panic isolation regression row",
SpecSource::HandWritten,
)];
fn output_bytes(value: &Value) -> &[u8] {
let Value::Bytes(bytes) = value else {
panic!("reference output must be raw bytes");
};
bytes
}
fn panic_cpu(_input: &[u8]) -> Vec<u8> {
panic!("synthetic cpu panic")
}
fn dummy_wgsl() -> String {
"fn vyre_op(a: u32) -> u32 { return a; }".to_string()
}
fn panic_spec() -> OpSpec {
OpSpec::builder("adversarial.cpu_panic")
.signature(OpSignature {
inputs: vec![DataType::U32],
output: DataType::U32,
})
.cpu_fn(panic_cpu)
.wgsl_fn(dummy_wgsl)
.category(Category::unclassified())
.laws(vec![])
.strictness(Strictness::Strict)
.version(1)
.spec_table(SPEC_ROWS)
.oracle_override(Some(OracleKind::CpuReference))
.build()
.expect("synthetic spec should build")
}
#[test]
fn bytes_buffer_load_and_store_are_byte_addressed_not_noops() {
let program = Program::new(
vec![
BufferDecl::read("input", 0, IrDataType::Bytes),
BufferDecl::read_write("out", 1, IrDataType::Bytes),
],
[1, 1, 1],
vec![Node::store(
"out",
Expr::u32(1),
Expr::load("input", Expr::u32(2)),
)],
);
let outputs = interp::run(
&program,
&[
Value::Bytes(b"abcdef".to_vec()),
Value::Bytes(b"XYZ".to_vec()),
],
)
.expect("bytes load/store should execute");
assert_eq!(output_bytes(&outputs[0]), b"Xcdef");
}
#[test]
fn cast_negative_i32_to_u32_returns_invalid_program_instead_of_panicking() {
let program = Program::new(
vec![BufferDecl::read_write("out", 0, IrDataType::U32)],
[1, 1, 1],
vec![Node::store(
"out",
Expr::u32(0),
Expr::Cast {
target: IrDataType::U32,
value: Box::new(Expr::i32(-1)),
},
)],
);
assert!(matches!(
interp::run(&program, &[Value::Bytes(vec![0; 4])]),
Err(Error::Interp { .. })
));
}
#[test]
fn negative_loop_bounds_return_invalid_program_instead_of_panicking() {
let program = Program::new(
vec![BufferDecl::read_write("out", 0, IrDataType::U32)],
[1, 1, 1],
vec![Node::loop_for(
"i",
Expr::i32(-1),
Expr::u32(1),
vec![Node::store("out", Expr::u32(0), Expr::u32(1))],
)],
);
assert!(matches!(
interp::run(&program, &[Value::Bytes(vec![0; 4])]),
Err(Error::Interp { .. })
));
}
#[test]
fn cpu_reference_oracle_maps_cpu_panic_to_fail_verdict() {
let verdict = CpuReferenceOracle.verify(&panic_spec(), &[1], &[0]);
assert!(matches!(verdict, Verdict::Fail { .. }));
}
#[test]
fn spec_table_oracle_maps_cpu_panic_to_fail_verdict() {
let verdict = SpecTableOracle.verify(
&panic_spec(),
&[1],
&[u32::from_le_bytes([
ROW_EXPECTED[0],
ROW_EXPECTED[1],
ROW_EXPECTED[2],
ROW_EXPECTED[3],
])],
);
assert!(matches!(verdict, Verdict::Fail { .. }));
}