use std::collections::HashSet;
use std::path::PathBuf;
use vyre_driver::backend::{validate_program, BackendError, VyreBackend};
use vyre_foundation::ir::{Node, OpId, Program};
use vyre_foundation::program_caps::{check_backend_capabilities, RequiredCapabilities};
#[test]
fn backend_errors_contain_fix_guidance() {
let errors: Vec<BackendError> = vec![
BackendError::DeviceOutOfMemory {
requested: 1,
available: 0,
},
BackendError::UnsupportedFeature {
name: "x".into(),
backend: "y".into(),
},
BackendError::PoisonedLock {
lock_error: "x".into(),
},
BackendError::KernelCompileFailed {
backend: "y".into(),
compiler_message: "z".into(),
},
BackendError::DispatchFailed {
code: None,
message: "m".into(),
},
BackendError::InvalidProgram {
fix: "Fix: do something".into(),
},
];
for err in &errors {
let msg = err.to_string();
assert!(
msg.contains("Fix:"),
"BackendError must carry actionable Fix: guidance; got: {msg}"
);
}
}
#[test]
fn backend_error_new_preserves_complete_message() {
let err = BackendError::new("something broke");
assert_eq!(err.to_string(), "something broke");
}
#[test]
fn empty_capability_set_rejects_any_program_with_nodes() {
struct NoOpBackend;
impl vyre_driver::backend::private::Sealed for NoOpBackend {}
impl VyreBackend for NoOpBackend {
fn id(&self) -> &'static str {
"noop"
}
fn supported_ops(&self) -> &HashSet<OpId> {
static EMPTY: std::sync::OnceLock<HashSet<OpId>> = std::sync::OnceLock::new();
EMPTY.get_or_init(HashSet::new)
}
fn dispatch(
&self,
_program: &Program,
_inputs: &[Vec<u8>],
_config: &vyre_driver::DispatchConfig,
) -> Result<Vec<Vec<u8>>, BackendError> {
Ok(vec![])
}
}
let program = Program::wrapped(vec![], [1, 1, 1], vec![Node::Return]);
let backend = NoOpBackend;
let err = validate_program(&program, &backend)
.expect_err("a backend with empty supported_ops must reject a program containing nodes");
let msg = err.to_string();
assert!(
msg.contains("Fix:") || msg.contains("unsupported") || msg.contains("supported"),
"empty-capability rejection must be actionable: {msg}"
);
}
#[test]
fn capability_negotiation_lists_all_missing_bits() {
let mut required = RequiredCapabilities::none();
required.subgroup_ops = true;
required.f16 = true;
required.bf16 = true;
required.indirect_dispatch = true;
required.trap = true;
let err = check_backend_capabilities(
"test",
false,
false,
false,
false,
false,
false,
[1, 1, 1],
&required,
)
.unwrap_err();
let missing = err.missing;
let expected: HashSet<&str> = [
"subgroup_ops",
"f16",
"bf16",
"indirect_dispatch",
"trap_propagation",
]
.iter()
.copied()
.collect();
let actual: HashSet<&str> = missing.iter().map(String::as_str).collect();
assert_eq!(
actual, expected,
"capability negotiation must report every missing capability explicitly"
);
}
#[test]
fn validation_rejects_unsupported_operation() {
struct UnsupportedOpsBackend;
impl vyre_driver::backend::private::Sealed for UnsupportedOpsBackend {}
impl VyreBackend for UnsupportedOpsBackend {
fn id(&self) -> &'static str {
"unsupported-ops-contract"
}
fn supported_ops(&self) -> &HashSet<OpId> {
static EMPTY: std::sync::OnceLock<HashSet<OpId>> = std::sync::OnceLock::new();
EMPTY.get_or_init(HashSet::new)
}
fn dispatch(
&self,
_program: &Program,
_inputs: &[Vec<u8>],
_config: &vyre_driver::DispatchConfig,
) -> Result<Vec<Vec<u8>>, BackendError> {
Ok(vec![])
}
}
let program = Program::wrapped(vec![], [1, 1, 1], vec![Node::Return]);
let err = validate_program(&program, &UnsupportedOpsBackend).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("Fix:"),
"validation rejection must carry actionable Fix: guidance; got: {msg}"
);
}
#[test]
fn external_fixture_is_loadable() {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let fixture = manifest.join("tests/fixtures/unsupported_op.txt");
assert!(
fixture.exists(),
"external fixture must exist at {fixture:?}"
);
let content = std::fs::read_to_string(&fixture).unwrap();
assert!(!content.trim().is_empty(), "fixture must not be empty");
}
#[test]
fn external_fixture_drives_validation_rejection() {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let fixture = manifest.join("tests/fixtures/unsupported_op.txt");
let op_id = std::fs::read_to_string(&fixture)
.unwrap()
.trim()
.to_string();
struct DenyAllBackend;
impl vyre_driver::backend::private::Sealed for DenyAllBackend {}
impl VyreBackend for DenyAllBackend {
fn id(&self) -> &'static str {
"deny_all"
}
fn supported_ops(&self) -> &HashSet<OpId> {
static EMPTY: std::sync::OnceLock<HashSet<OpId>> = std::sync::OnceLock::new();
EMPTY.get_or_init(HashSet::new)
}
fn dispatch(
&self,
_program: &Program,
_inputs: &[Vec<u8>],
_config: &vyre_driver::DispatchConfig,
) -> Result<Vec<Vec<u8>>, BackendError> {
Ok(vec![])
}
}
let program = Program::wrapped(vec![], [1, 1, 1], vec![Node::Return]);
let err = validate_program(&program, &DenyAllBackend)
.expect_err("validation must reject unsupported ops (fixture op={op_id})");
let msg = err.to_string();
assert!(
msg.contains("Fix:"),
"fixture-driven validation rejection must carry Fix: guidance; got: {msg}"
);
}