#[cfg(loom)]
use loom::sync::atomic::{AtomicUsize, Ordering};
#[cfg(not(loom))]
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::pipeline::backend::{ConformDispatchConfig, ExecutionModel, VyreBackend};
#[cfg(test)]
mod tests {
use super::*;
struct MockBackend {
name: &'static str,
output: Vec<u8>,
}
impl VyreBackend for MockBackend {
fn name(&self) -> &str {
self.name
}
fn dispatch(
&self,
_wgsl: &str,
_input: &[u8],
_output_size: usize,
_config: ConformDispatchConfig,
) -> Result<Vec<u8>, String> {
Ok(self.output.clone())
}
fn dispatch_program(
&self,
_program: &[u8],
_input: &[u8],
_output_size: usize,
_config: ConformDispatchConfig,
) -> Result<Vec<u8>, String> {
Ok(self.output.clone())
}
}
#[test]
fn swap_backend_uses_a_before_threshold() {
let a = MockBackend {
name: "a",
output: vec![0xAA],
};
let b = MockBackend {
name: "b",
output: vec![0xBB],
};
let swap = SwapBackend::new(&a, &b, 2);
assert_eq!(
swap.dispatch("", &[], 1, ConformDispatchConfig::default())
.unwrap(),
vec![0xAA]
);
assert_eq!(
swap.dispatch("", &[], 1, ConformDispatchConfig::default())
.unwrap(),
vec![0xAA]
);
}
#[test]
fn swap_backend_uses_b_after_threshold() {
let a = MockBackend {
name: "a",
output: vec![0xAA],
};
let b = MockBackend {
name: "b",
output: vec![0xBB],
};
let swap = SwapBackend::new(&a, &b, 1);
assert_eq!(
swap.dispatch("", &[], 1, ConformDispatchConfig::default())
.unwrap(),
vec![0xAA]
);
assert_eq!(
swap.dispatch("", &[], 1, ConformDispatchConfig::default())
.unwrap(),
vec![0xBB]
);
}
#[test]
fn with_backend_swap_detects_divergence() {
let a = MockBackend {
name: "a",
output: vec![0x00; 4],
};
let b = MockBackend {
name: "b",
output: vec![0xFF; 4],
};
let program = vyre::ir::Program::new(vec![], [1, 1, 1], vec![vyre::ir::Node::Return]);
let result = with_backend_swap(&a, &b, &program, &[], 4, ConformDispatchConfig::default(), 0);
assert!(
result.is_err(),
"mixed run must be rejected when pure baselines diverge"
);
let result = with_backend_swap(&a, &b, &program, &[], 4, ConformDispatchConfig::default(), 1);
assert!(
result.is_err(),
"mixed run must be rejected when pure baselines diverge"
);
}
#[test]
fn with_backend_swap_propagates_backend_b_error() {
struct FailingBackend;
impl VyreBackend for FailingBackend {
fn name(&self) -> &str {
"failing"
}
fn dispatch(
&self,
_wgsl: &str,
_input: &[u8],
_output_size: usize,
_config: ConformDispatchConfig,
) -> Result<Vec<u8>, String> {
Err("backend b error".to_string())
}
fn dispatch_program(
&self,
_program: &[u8],
_input: &[u8],
_output_size: usize,
_config: ConformDispatchConfig,
) -> Result<Vec<u8>, String> {
Err("backend b error".to_string())
}
}
let a = MockBackend {
name: "a",
output: vec![0x00; 4],
};
let program = vyre::ir::Program::new(vec![], [1, 1, 1], vec![vyre::ir::Node::Return]);
let result = with_backend_swap(
&a,
&FailingBackend,
&program,
&[],
4,
ConformDispatchConfig::default(),
0,
);
assert!(
result.is_err(),
"expected error from backend B, got: {:?}",
result
);
let msg = result.unwrap_err();
assert!(
msg.contains("backend b error"),
"error must propagate from backend B, got: {msg}"
);
}
}