use canlink_hal::{BackendConfig, CanBackend, CanError, CanId, CanMessage};
use canlink_mock::{MockBackend, MockConfig};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== CAN Automated Testing Suite ===\n");
let mut passed = 0;
let mut failed = 0;
println!("Running test suite...\n");
run_test(
"Basic Send/Receive",
test_basic_send_receive,
&mut passed,
&mut failed,
);
run_test(
"Error Handling",
test_error_handling,
&mut passed,
&mut failed,
);
run_test(
"Message Filtering",
test_message_filtering,
&mut passed,
&mut failed,
);
run_test(
"CAN-FD Support",
test_canfd_support,
&mut passed,
&mut failed,
);
run_test("Extended IDs", test_extended_ids, &mut passed, &mut failed);
run_test(
"Remote Frames",
test_remote_frames,
&mut passed,
&mut failed,
);
run_test(
"Channel Management",
test_channel_management,
&mut passed,
&mut failed,
);
run_test(
"State Transitions",
test_state_transitions,
&mut passed,
&mut failed,
);
run_test(
"Burst Traffic",
test_burst_traffic,
&mut passed,
&mut failed,
);
run_test(
"Error Recovery",
test_error_recovery,
&mut passed,
&mut failed,
);
println!("\n=== Test Summary ===");
println!("Passed: {}", passed);
println!("Failed: {}", failed);
println!("Total: {}", passed + failed);
if failed == 0 {
println!("\n✓ All tests passed!");
Ok(())
} else {
println!("\n✗ Some tests failed");
Err("Test failures detected".into())
}
}
fn run_test<F>(name: &str, test_fn: F, passed: &mut u32, failed: &mut u32)
where
F: FnOnce() -> Result<(), Box<dyn std::error::Error>>,
{
print!(" [TEST] {} ... ", name);
match test_fn() {
Ok(_) => {
println!("✓ PASS");
*passed += 1;
}
Err(e) => {
println!("✗ FAIL: {}", e);
*failed += 1;
}
}
}
fn test_basic_send_receive() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
let msg = CanMessage::new_standard(0x123, &[1, 2, 3])?;
backend.send_message(&msg)?;
assert_eq!(
backend.get_recorded_messages().len(),
1,
"Expected 1 message"
);
assert!(
backend.verify_message_sent(CanId::Standard(0x123)),
"Message not found"
);
backend.close()?;
Ok(())
}
fn test_error_handling() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
backend
.error_injector_mut()
.inject_send_error(CanError::SendFailed {
reason: "Test error".to_string(),
});
let msg = CanMessage::new_standard(0x123, &[1, 2, 3])?;
let result = backend.send_message(&msg);
assert!(result.is_err(), "Expected send to fail");
assert_eq!(
backend.get_recorded_messages().len(),
0,
"Failed message should not be recorded"
);
backend.close()?;
Ok(())
}
fn test_message_filtering() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
backend.send_message(&CanMessage::new_standard(0x100, &[1])?)?;
backend.send_message(&CanMessage::new_standard(0x200, &[2])?)?;
backend.send_message(&CanMessage::new_standard(0x100, &[3])?)?;
backend.send_message(&CanMessage::new_standard(0x300, &[4])?)?;
let messages_100 = backend.get_messages_by_id(CanId::Standard(0x100));
assert_eq!(messages_100.len(), 2, "Expected 2 messages with ID 0x100");
assert_eq!(messages_100[0].data()[0], 1, "Wrong data in first message");
assert_eq!(messages_100[1].data()[0], 3, "Wrong data in second message");
backend.close()?;
Ok(())
}
fn test_canfd_support() -> Result<(), Box<dyn std::error::Error>> {
let mut backend_fd = MockBackend::new();
let config = BackendConfig::new("mock");
backend_fd.initialize(&config)?;
backend_fd.open_channel(0)?;
let capability = backend_fd.get_capability()?;
assert!(capability.supports_canfd, "CAN-FD should be supported");
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let msg = CanMessage::new_fd(CanId::Standard(0x123), &data)?;
backend_fd.send_message(&msg)?;
let recorded = backend_fd.get_recorded_messages();
assert_eq!(recorded.len(), 1, "Expected 1 message");
assert!(recorded[0].is_fd(), "Message should be CAN-FD");
backend_fd.close()?;
let config_20 = MockConfig::can20_only();
let mut backend_20 = MockBackend::with_config(config_20);
backend_20.initialize(&config)?;
backend_20.open_channel(0)?;
let capability = backend_20.get_capability()?;
assert!(!capability.supports_canfd, "CAN-FD should not be supported");
let result = backend_20.send_message(&msg);
assert!(
result.is_err(),
"CAN-FD message should fail on CAN 2.0 backend"
);
backend_20.close()?;
Ok(())
}
fn test_extended_ids() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
backend.send_message(&CanMessage::new_extended(0x12345678, &[1, 2])?)?;
backend.send_message(&CanMessage::new_extended(0x1FFFFFFF, &[3, 4])?)?;
assert!(
backend.verify_message_sent(CanId::Extended(0x12345678)),
"Extended ID 0x12345678 not found"
);
assert!(
backend.verify_message_sent(CanId::Extended(0x1FFFFFFF)),
"Extended ID 0x1FFFFFFF not found"
);
assert!(
!backend.verify_message_sent(CanId::Standard(0x5678)),
"Standard ID should not match extended ID"
);
backend.close()?;
Ok(())
}
fn test_remote_frames() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
let remote = CanMessage::new_remote(CanId::Standard(0x123), 4)?;
backend.send_message(&remote)?;
let messages = backend.get_messages_by_id(CanId::Standard(0x123));
assert_eq!(messages.len(), 1, "Expected 1 message");
assert!(messages[0].is_remote(), "Message should be remote frame");
backend.close()?;
Ok(())
}
fn test_channel_management() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
backend.open_channel(1)?;
let result = backend.open_channel(0);
assert!(result.is_err(), "Should not be able to open channel twice");
backend.close_channel(0)?;
let result = backend.close_channel(0);
assert!(
result.is_err(),
"Should not be able to close closed channel"
);
let result = backend.open_channel(99);
assert!(
result.is_err(),
"Should not be able to open invalid channel"
);
backend.close()?;
Ok(())
}
fn test_state_transitions() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
let msg = CanMessage::new_standard(0x123, &[1, 2, 3])?;
let result = backend.send_message(&msg);
assert!(result.is_err(), "Should not be able to send before init");
backend.initialize(&config)?;
let result = backend.send_message(&msg);
assert!(
result.is_err(),
"Should not be able to send without channel"
);
backend.open_channel(0)?;
backend.send_message(&msg)?;
backend.close()?;
let result = backend.send_message(&msg);
assert!(result.is_err(), "Should not be able to send after close");
Ok(())
}
fn test_burst_traffic() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
let burst_size = 100;
for i in 0..burst_size {
let msg = CanMessage::new_standard((i % 10) as u16, &[i as u8])?;
backend.send_message(&msg)?;
}
assert!(
backend.verify_message_count(burst_size),
"Not all messages recorded"
);
for id in 0..10 {
let messages = backend.get_messages_by_id(CanId::Standard(id));
assert_eq!(messages.len(), 10, "Expected 10 messages for ID {}", id);
}
backend.close()?;
Ok(())
}
fn test_error_recovery() -> Result<(), Box<dyn std::error::Error>> {
let mut backend = MockBackend::new();
let config = BackendConfig::new("mock");
backend.initialize(&config)?;
backend.open_channel(0)?;
backend.error_injector_mut().inject_send_error_with_config(
CanError::SendFailed {
reason: "Intermittent".to_string(),
},
2, 0,
);
let msg = CanMessage::new_standard(0x123, &[1, 2, 3])?;
assert!(
backend.send_message(&msg).is_err(),
"First send should fail"
);
assert!(
backend.send_message(&msg).is_err(),
"Second send should fail"
);
assert!(
backend.send_message(&msg).is_ok(),
"Third send should succeed"
);
assert!(
backend.verify_message_count(1),
"Only successful message should be recorded"
);
backend.close()?;
Ok(())
}
#[allow(unused_macros)]
macro_rules! assert_msg {
($cond:expr, $msg:expr) => {
if !$cond {
return Err($msg.into());
}
};
}