socketcan-alt 0.4.0

Rust binding for SocketCAN
Documentation
use super::Socket;
use crate::socket::tests::{ifname, random_data_standard, random_fd_data_standard, LOCK};
use crate::{Cmsg, Frame, Timestamping};
use std::ffi::CString;
use std::io::ErrorKind;
use std::io::Result;
use std::time::Duration;
use tokio::time::{sleep, timeout};

macro_rules! lock {
    (shared) => {
        let _lock = LOCK.read();
    };
    (exclusive) => {
        let _lock = LOCK.write();
    };
}

async fn recv(socket: Socket, query: Option<Frame>) -> Option<Result<()>> {
    timeout(Duration::from_millis(100), async {
        loop {
            let frame = socket.recv().await?;
            if query.as_ref().map(|query| &frame == query).unwrap_or(true) {
                return Ok(());
            }
        }
    })
    .await
    .ok()
}

async fn recv_msg(socket: Socket, query: Option<Frame>) -> Option<Result<Option<libc::timespec>>> {
    timeout(Duration::from_millis(100), async {
        let mut cmsg_buf = vec![0; Cmsg::space()];
        loop {
            let (frame, cmsgs) = socket.recv_msg(&mut cmsg_buf).await?;
            if query.as_ref().map(|query| &frame == query).unwrap_or(true) {
                let timestamp = cmsgs.into_iter().flatten().find_map(|cmsg| match cmsg {
                    Cmsg::Timestamping(ts) => Some(ts[0]),
                    _ => None,
                });
                return Ok(timestamp);
            }
        }
    })
    .await
    .ok()
}

#[tokio::test]
#[ignore]
async fn test_bind() {
    Socket::bind(ifname()).unwrap();
}

#[tokio::test]
async fn test_bind_no_device() {
    let ifname = CString::new("NO DEVICE").unwrap();
    assert!(Socket::bind(ifname).is_err());
}

#[tokio::test]
#[ignore]
async fn test_nonblocking_on() {
    lock!(exclusive);
    let socket = Socket::bind(ifname()).unwrap();

    assert!(recv(socket, None).await.is_none());
}

#[tokio::test]
#[ignore]
async fn test_default_timestamping_off() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx = Socket::bind(ifname()).unwrap();

    let frame = random_data_standard();
    socket_tx.send(&frame).await.unwrap();
    assert!(recv_msg(socket_rx, Some(frame))
        .await
        .unwrap()
        .unwrap()
        .is_none());
}

#[tokio::test]
#[ignore]
async fn test_set_timestamping_on() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx0 = Socket::bind(ifname()).unwrap();
    let socket_rx1 = Socket::bind(ifname()).unwrap();
    socket_rx0
        .set_timestamping(Timestamping::RX_SOFTWARE | Timestamping::SOFTWARE)
        .unwrap();
    socket_rx1
        .set_timestamping(Timestamping::RX_SOFTWARE | Timestamping::SOFTWARE)
        .unwrap();

    let frame = random_data_standard();
    socket_tx.send(&frame).await.unwrap();
    let timestamp0 = recv_msg(socket_rx0, Some(frame))
        .await
        .unwrap()
        .unwrap()
        .unwrap();
    sleep(Duration::from_millis(100)).await;
    let timestamp1 = recv_msg(socket_rx1, Some(frame))
        .await
        .unwrap()
        .unwrap()
        .unwrap();
    assert!(timestamp0.tv_sec != 0);
    assert!(timestamp1.tv_sec != 0);
    assert_eq!(
        (timestamp0.tv_sec, timestamp0.tv_nsec),
        (timestamp1.tv_sec, timestamp1.tv_nsec)
    );
}

#[tokio::test]
#[ignore]
async fn test_default_loopback_on() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx = Socket::bind(ifname()).unwrap();

    let frame = random_data_standard();
    socket_tx.send(&frame).await.unwrap();
    recv(socket_rx, Some(frame)).await.unwrap().unwrap();
}

#[tokio::test]
#[ignore]
async fn test_default_recv_own_msgs_off() {
    lock!(shared);
    let socket = Socket::bind(ifname()).unwrap();

    let frame = random_data_standard();
    socket.send(&frame).await.unwrap();
    assert!(recv(socket, Some(frame)).await.is_none());
}

#[tokio::test]
#[ignore]
async fn test_set_recv_own_msgs_on() {
    lock!(shared);
    let socket = Socket::bind(ifname()).unwrap();
    socket.set_recv_own_msgs(true).unwrap();

    let frame = random_data_standard();
    socket.send(&frame).await.unwrap();
    recv(socket, Some(frame)).await.unwrap().unwrap();
}

#[tokio::test]
#[ignore]
async fn test_default_fd_frames_off() {
    lock!(shared);
    let socket = Socket::bind(ifname()).unwrap();

    let frame = random_fd_data_standard();
    assert_eq!(
        socket.send(&frame).await.unwrap_err().kind(),
        ErrorKind::InvalidInput
    );
}

#[tokio::test]
#[ignore]
async fn test_set_fd_frames_on() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx = Socket::bind(ifname()).unwrap();
    socket_tx.set_fd_frames(true).unwrap();
    socket_rx.set_fd_frames(true).unwrap();

    let frame = random_fd_data_standard();
    socket_tx.send(&frame).await.unwrap();
    recv(socket_rx, Some(frame)).await.unwrap().unwrap();
}

#[test]
fn test_marker_traits() {
    fn check<F>(_: F)
    where
        F: Send,
    {
    }

    check(async {
        let ifname = CString::new("NO DEVICE").unwrap();
        let socket = Socket::bind(ifname).unwrap();

        socket.recv().await.unwrap();

        let mut cmsg_buf = Vec::new();
        socket.recv_msg(&mut cmsg_buf).await.unwrap();

        let frame = random_data_standard();
        socket.send(&frame).await.unwrap();
    })
}