#[cfg(feature = "vcan_tests")]
use socketcan::{
CanErrorFrame, CanFrame, CanSocket, EmbeddedFrame, ErrorCause, ShouldRetry, Socket,
SocketOptions, StandardId,
errors::{
CAN_ERR_ACK, CAN_ERR_BUSERROR, CAN_ERR_CNT, CAN_ERR_CRTL, CAN_ERR_PROT, ControllerProblems,
Location, ViolationTypes,
},
id::{ERR_MASK_ALL, ERR_MASK_NONE},
timestamp::{
SOF_TIMESTAMPING_OPT_CMSG, SOF_TIMESTAMPING_RX_SOFTWARE, SOF_TIMESTAMPING_SOFTWARE,
},
};
#[cfg(feature = "vcan_tests")]
use serial_test::serial;
#[cfg(feature = "vcan_tests")]
use std::time::{self, SystemTime};
#[cfg(feature = "vcan_tests")]
const VCAN: &str = "vcan0";
#[cfg(feature = "vcan_tests")]
#[test]
fn test_nonexistent_device() {
assert!(CanSocket::open("invalid").is_err());
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_timeout() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_filter_drop_all().unwrap();
sock.set_read_timeout(time::Duration::from_millis(100))
.unwrap();
assert!(sock.read_frame().should_retry());
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_set_error_mask() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_error_mask(ERR_MASK_ALL).unwrap();
sock.set_error_mask(ERR_MASK_NONE).unwrap();
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_other_protocols_bind_with_our_addr() {
use socket2::{Domain, Protocol, Socket as Sock2, Type};
use socketcan::{
CanAddr,
addr::{AF_CAN, J1939_NO_ADDR, J1939_NO_NAME, J1939_NO_PGN},
socket::{CAN_ISOTP, CAN_J1939},
};
let j1939 =
CanAddr::from_iface_j1939(VCAN, J1939_NO_NAME, J1939_NO_PGN, J1939_NO_ADDR).unwrap();
let isotp = CanAddr::from_iface_isotp(
VCAN,
StandardId::new(0x123).unwrap(),
StandardId::new(0x321).unwrap(),
)
.unwrap();
for (proto, addr) in [(CAN_J1939, j1939), (CAN_ISOTP, isotp)] {
let sock = Sock2::new_raw(
Domain::from(AF_CAN),
Type::DGRAM,
Some(Protocol::from(proto)),
)
.unwrap();
sock.bind(&addr.into_sock_addr()).unwrap();
}
assert_eq!(j1939.j1939_pgn(), J1939_NO_PGN);
assert_eq!(j1939.j1939_addr(), J1939_NO_ADDR);
assert_eq!(isotp.tp_rx_id(), 0x123);
}
#[test]
#[cfg(feature = "netlink")]
fn hw_can_param_bytes() {
use socketcan::CanInterface;
let Ok(name) = std::env::var("SOCKETCAN_FD_IFACE") else {
eprintln!("skipped: set SOCKETCAN_FD_IFACE to an FD-capable interface");
return;
};
let iface = CanInterface::open(&name).expect("interface should exist");
let bytes = iface
.can_param_bytes(libc::IFLA_CAN_CLOCK as u16)
.expect("query should succeed")
.expect("a real controller reports its clock");
assert_eq!(bytes.len(), 4, "IFLA_CAN_CLOCK is a u32");
let freq = u32::from_ne_bytes(bytes[..4].try_into().unwrap());
assert_eq!(Some(freq), iface.clock().unwrap());
let nest = iface
.can_param_bytes(libc::IFLA_CAN_CTRLMODE_EXT as u16)
.expect("query should succeed")
.expect("kernel 6.0+ reports supported modes");
assert!(nest.len() >= 8, "nest is {nest:02X?}");
let supported = u32::from_ne_bytes(nest[4..8].try_into().unwrap());
assert_eq!(Some(supported), iface.supported_ctrlmodes().unwrap());
let tdc = iface.can_param_bytes(libc::IFLA_CAN_TDC as u16).unwrap();
eprintln!("{name}: clock {freq} Hz, supported {supported:#06X}, TDC {tdc:?}");
}
#[test]
#[cfg(feature = "netlink")]
fn hw_supported_ctrlmodes() {
use socketcan::{CanCtrlMode, CanInterface};
let Ok(name) = std::env::var("SOCKETCAN_FD_IFACE") else {
eprintln!("skipped: set SOCKETCAN_FD_IFACE to an FD-capable interface");
return;
};
let iface = CanInterface::open(&name).expect("interface should exist");
let supported = iface
.supported_ctrlmodes()
.expect("query should succeed")
.expect("an FD controller reports IFLA_CAN_CTRLMODE_EXT");
let named = [
(CanCtrlMode::Loopback, "LOOPBACK"),
(CanCtrlMode::ListenOnly, "LISTENONLY"),
(CanCtrlMode::TripleSampling, "3_SAMPLES"),
(CanCtrlMode::OneShot, "ONE_SHOT"),
(CanCtrlMode::BerrReporting, "BERR_REPORTING"),
(CanCtrlMode::Fd, "FD"),
(CanCtrlMode::PresumeAck, "PRESUME_ACK"),
(CanCtrlMode::NonIso, "FD_NON_ISO"),
(CanCtrlMode::CcLen8Dlc, "CC_LEN8_DLC"),
];
let list: Vec<&str> = named
.iter()
.filter(|(m, _)| supported & m.mask() != 0)
.map(|(_, n)| *n)
.collect();
eprintln!("{name}: supported = {supported:#06X} {list:?}");
assert!(
supported & CanCtrlMode::Fd.mask() != 0,
"{name} reports {supported:#06X}, without CAN_CTRLMODE_FD"
);
assert_eq!(
iface.can_params().unwrap().ctrl_mode_supported,
Some(supported)
);
assert_eq!(
iface.details().unwrap().can.ctrl_mode_supported,
Some(supported)
);
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_option_getters_mirror_setters() {
let sock = CanSocket::open(VCAN).unwrap();
for on in [true, false] {
sock.set_loopback(on).unwrap();
assert_eq!(sock.loopback().unwrap(), on, "loopback {on}");
sock.set_recv_own_msgs(on).unwrap();
assert_eq!(sock.recv_own_msgs().unwrap(), on, "recv_own_msgs {on}");
sock.set_join_filters(on).unwrap();
assert_eq!(sock.join_filters().unwrap(), on, "join_filters {on}");
}
for mask in [ERR_MASK_ALL, ERR_MASK_NONE, 0x0000_0004] {
sock.set_error_filter(mask).unwrap();
assert_eq!(sock.error_filter().unwrap(), mask, "mask {mask:#x}");
assert_eq!(sock.error_mask().unwrap(), mask);
}
let fresh = CanSocket::open(VCAN).unwrap();
assert!(fresh.loopback().unwrap());
assert!(!fresh.recv_own_msgs().unwrap());
assert_eq!(fresh.error_filter().unwrap(), ERR_MASK_NONE);
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_socket_option_setters_agree() {
use socketcan::socket::{CAN_RAW_ERR_FILTER, CAN_RAW_FILTER, CAN_RAW_LOOPBACK, SOL_CAN_RAW};
let sock = CanSocket::open(VCAN).unwrap();
unsafe { sock.set_socket_option(SOL_CAN_RAW, CAN_RAW_LOOPBACK, &0i32) }.unwrap();
assert_eq!(
sock.get_socket_option_int(SOL_CAN_RAW, CAN_RAW_LOOPBACK)
.unwrap(),
0
);
sock.set_socket_option_bytes(SOL_CAN_RAW, CAN_RAW_LOOPBACK, &1i32.to_ne_bytes())
.unwrap();
assert_eq!(
sock.get_socket_option_int(SOL_CAN_RAW, CAN_RAW_LOOPBACK)
.unwrap(),
1
);
sock.set_socket_option_int(SOL_CAN_RAW, CAN_RAW_LOOPBACK, 0)
.unwrap();
assert_eq!(
sock.get_socket_option_int(SOL_CAN_RAW, CAN_RAW_LOOPBACK)
.unwrap(),
0
);
sock.set_error_mask(ERR_MASK_ALL).unwrap();
let mut buf = [0u8; 4];
sock.get_socket_option_bytes(SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &mut buf)
.unwrap();
assert_eq!(u32::from_ne_bytes(buf), ERR_MASK_ALL);
sock.set_filter_drop_all().unwrap();
sock.set_socket_option_bytes(SOL_CAN_RAW, CAN_RAW_FILTER, &[])
.unwrap();
sock.set_filter_accept_all().unwrap();
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_socket_option_round_trip() {
use socketcan::socket::{CAN_RAW_LOOPBACK, CAN_RAW_RECV_OWN_MSGS, SOL_CAN_RAW};
let sock = CanSocket::open(VCAN).unwrap();
for (name, opt) in [
("CAN_RAW_LOOPBACK", CAN_RAW_LOOPBACK),
("CAN_RAW_RECV_OWN_MSGS", CAN_RAW_RECV_OWN_MSGS),
] {
for on in [true, false] {
match opt {
CAN_RAW_LOOPBACK => sock.set_loopback(on).unwrap(),
_ => sock.set_recv_own_msgs(on).unwrap(),
}
let got = sock.get_socket_option_int(SOL_CAN_RAW, opt).unwrap();
assert_eq!(got, i32::from(on), "{name} set to {on}");
}
}
sock.set_error_mask(ERR_MASK_ALL).unwrap();
let mut buf = [0u8; 4];
let n = sock
.get_socket_option_bytes(SOL_CAN_RAW, socketcan::socket::CAN_RAW_ERR_FILTER, &mut buf)
.unwrap();
assert_eq!(n, 4);
assert_eq!(u32::from_ne_bytes(buf), ERR_MASK_ALL);
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_multi_bit_error_frame_round_trip() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_loopback(true).unwrap();
sock.set_recv_own_msgs(true).unwrap();
sock.set_error_mask(ERR_MASK_ALL).unwrap();
let bits = CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_ACK;
let data = [0u8, 0, 0x9E, 0x08, 0, 0, 0, 0];
let frame = CanErrorFrame::new_error(bits, &data).unwrap();
sock.write_frame(&frame).unwrap();
let echoed = sock.read_frame().unwrap();
let echoed = match echoed {
CanFrame::Error(f) => f,
other => panic!("expected an error frame, got {:?}", other),
};
assert_eq!(echoed.error_bits(), bits);
let err = echoed.into_error();
assert_eq!(err.len(), 3, "decoded: {}", err);
let (types, location) = err.protocol().expect("a protocol violation");
assert_eq!(location, Location::CrcSequence);
assert_eq!(
types,
ViolationTypes::FORM
| ViolationTypes::STUFF
| ViolationTypes::BIT0
| ViolationTypes::BIT1
| ViolationTypes::TX,
);
assert!(err.is_no_ack());
assert!(err.is_bus_error());
assert_eq!(CanErrorFrame::from(err), echoed);
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_controller_state_change_error_frame() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_loopback(true).unwrap();
sock.set_recv_own_msgs(true).unwrap();
sock.set_error_mask(ERR_MASK_ALL).unwrap();
let bits = CAN_ERR_CRTL | CAN_ERR_CNT;
let data = [0u8, 0x0C, 0, 0, 0, 0, 112, 96];
sock.write_frame(&CanErrorFrame::new_error(bits, &data).unwrap())
.unwrap();
let echoed = match sock.read_frame().unwrap() {
CanFrame::Error(f) => f,
other => panic!("expected an error frame, got {:?}", other),
};
let err = echoed.into_error();
let all: Vec<ErrorCause> = err.causes().copied().collect();
assert_eq!(
all,
vec![
ErrorCause::Controller(ControllerProblems::RX_WARNING | ControllerProblems::TX_WARNING),
ErrorCause::Counters { tx: 112, rx: 96 },
],
"decoded: {}",
err
);
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_enable_own_loopback() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_loopback(true).unwrap();
sock.set_recv_own_msgs(true).unwrap();
let id = StandardId::new(0x123).unwrap();
let frame = CanFrame::new_remote(id, 0).unwrap();
sock.write_frame(&frame).unwrap();
sock.read_frame().unwrap();
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_test_nonblocking() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_filter_drop_all().unwrap();
sock.set_nonblocking(true).unwrap();
assert!(sock.read_frame().should_retry());
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_has_hw_timestamps_returns_false() {
let sock = CanSocket::open(VCAN).unwrap();
assert!(!sock.has_hw_timestamps());
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_read_frame_with_timestamp() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_loopback(true).unwrap();
sock.set_recv_own_msgs(true).unwrap();
sock.set_recv_timestamp(true).unwrap();
let id = StandardId::new(0x321).unwrap();
let frame = CanFrame::new(id, &[0xAA, 0xBB]).unwrap();
let sent_at = SystemTime::now();
sock.write_frame(&frame).unwrap();
let (rx, ts) = sock.read_frame_with_timestamp().unwrap();
assert_eq!(rx.data(), frame.data());
let delta = ts
.duration_since(sent_at)
.or_else(|e| Ok::<_, std::time::SystemTimeError>(e.duration()))
.unwrap();
assert!(
delta < time::Duration::from_secs(2),
"timestamp out of expected range: {delta:?}"
);
}
#[test]
#[cfg(feature = "vcan_tests")]
#[serial]
fn vcan_read_frame_with_timestamps_populates_sw() {
let sock = CanSocket::open(VCAN).unwrap();
sock.set_loopback(true).unwrap();
sock.set_recv_own_msgs(true).unwrap();
sock.set_recv_timestamp(true).unwrap();
sock.set_timestamping(
SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_OPT_CMSG,
)
.unwrap();
let id = StandardId::new(0x456).unwrap();
let frame = CanFrame::new(id, &[0x11, 0x22, 0x33]).unwrap();
sock.write_frame(&frame).unwrap();
let (_rx, ts) = sock.read_frame_with_timestamps().unwrap();
assert!(ts.socket.is_some(), "SO_TIMESTAMPNS not delivered");
assert!(ts.sw.is_some(), "RX_SOFTWARE not delivered");
assert!(ts.hw.is_none(), "vcan should not report a hw timestamp");
}