tiny-std 0.3.2

Tiny rust stdlib for linux
Documentation
use core::time::Duration;

use crate::io::{Read, Write};
use crate::net::{
    Ip, SocketAddress, TcpListener, TcpStream, TcpTryConnect, UnixListener, UnixStream,
};
use crate::time::MonotonicInstant;
use crate::unix::fd::AsRawFd;
use rusl::error::Errno;
use rusl::platform::{PollEvents, PollFd};
use rusl::string::unix_str::UnixStr;

#[test]
fn test_unix_ping_pong() {
    let sock_path = UnixStr::try_from_str("/tmp/test-sock/sock1\0").unwrap();
    let _ = crate::fs::remove_file(sock_path);
    crate::fs::create_dir_all(UnixStr::try_from_str("/tmp/test-sock/\0").unwrap()).unwrap();
    let mut listener = UnixListener::bind(sock_path).unwrap();
    assert!(matches!(listener.try_accept(), Ok(None)));
    let client = UnixStream::connect(sock_path).unwrap();
    let client_handle = listener.accept().unwrap();
    verify_communication(client, client_handle);
}

#[test]
fn test_unix_accept_timeout() {
    let sock_path = UnixStr::try_from_str("/tmp/test-sock/sock2\0").unwrap();
    let _ = crate::fs::remove_file(sock_path);
    crate::fs::create_dir_all(UnixStr::try_from_str("/tmp/test-sock/\0").unwrap()).unwrap();
    let mut listener = UnixListener::bind(sock_path).unwrap();
    assert!(
        matches!(
            listener.accept_with_timeout(core::time::Duration::from_millis(15)),
            Err(crate::Error::Timeout)
        ),
        "Expected timeout"
    );
}

#[test]
fn test_unix_try_accept() {
    let sock_path = UnixStr::try_from_str("/tmp/test-sock/sock3\0").unwrap();
    let _ = crate::fs::remove_file(sock_path);
    crate::fs::create_dir_all(UnixStr::try_from_str("/tmp/test-sock/\0").unwrap()).unwrap();
    let mut listener = UnixListener::bind(sock_path).unwrap();
    assert!(matches!(listener.try_accept(), Ok(None)));
    let client = UnixStream::connect(sock_path).unwrap();
    let client_handle = listener.try_accept().unwrap().unwrap();
    verify_communication(client, client_handle);
}

#[test]
fn test_unix_try_connect() {
    let sock_path = UnixStr::try_from_str("/tmp/test-sock/sock4\0").unwrap();
    let _ = crate::fs::remove_file(sock_path);
    crate::fs::create_dir_all(UnixStr::try_from_str("/tmp/test-sock/\0").unwrap()).unwrap();
    let mut listener = UnixListener::bind(sock_path).unwrap();
    assert!(matches!(listener.try_accept(), Ok(None)));
    let client = UnixStream::try_connect(sock_path).unwrap().unwrap();
    let client_handle = listener.accept().unwrap();
    verify_communication(client, client_handle);
}

fn verify_communication<C: Read + Write + AsRawFd, H: Read + Write + AsRawFd>(
    mut client: C,
    mut client_handle: H,
) {
    let write_in = &[8, 8, 8, 8];
    client_handle.write_all(write_in).unwrap();
    client_handle.flush().unwrap();
    rusl::select::ppoll(
        &mut [PollFd::new(client.as_raw_fd(), PollEvents::POLLOUT)],
        None,
        None,
    )
    .unwrap();
    let mut dest = [0u8; 4];
    client.read_exact(&mut dest).unwrap();
    assert_eq!(write_in, &dest);
}

#[test]
fn test_tcp_ping_pong() {
    let ip = Ip::V4([127, 0, 0, 1]);
    let mut listener = TcpListener::bind(&SocketAddress::new(ip, 0)).unwrap();
    let addr = listener.local_addr().unwrap();
    let client = TcpStream::connect(&addr).unwrap();
    let client_handle = listener.accept().unwrap();
    verify_communication(client, client_handle);
}

#[test]
fn test_tcp_accept_timeout() {
    let ip = Ip::V4([127, 0, 0, 1]);
    let mut listener = TcpListener::bind(&SocketAddress::new(ip, 0)).unwrap();
    assert!(
        matches!(
            listener.accept_with_timeout(core::time::Duration::from_millis(15)),
            Err(crate::Error::Timeout)
        ),
        "Expected timeout"
    );
}

#[test]
fn test_tcp_try_accept() {
    let ip = Ip::V4([127, 0, 0, 1]);
    let mut listener = TcpListener::bind(&SocketAddress::new(ip, 0)).unwrap();
    assert!(matches!(listener.try_accept(), Ok(None)));
    let addr = listener.local_addr().unwrap();
    let client = TcpStream::connect(&addr).unwrap();
    let client_handle = listener.try_accept().unwrap().unwrap();
    verify_communication(client, client_handle);
}

#[test]
fn test_tcp_try_connect() {
    let ip = Ip::V4([127, 0, 0, 1]);
    let TcpTryConnect::InProgress(bad_address_con) =
        TcpStream::try_connect(&SocketAddress::new(ip, u16::MAX - 1)).unwrap()
    else {
        panic!("Failed to start connection");
    };
    let Err(e) = bad_address_con.try_connect() else {
        panic!("Expected err on invalid port, some chance of spurious error due to port being used by someone else");
    };
    assert!(matches!(
        e,
        crate::error::Error::Os {
            msg: _,
            code: Errno::ECONNREFUSED
        }
    ));
    let mut listener = TcpListener::bind(&SocketAddress::new(ip, 0)).unwrap();
    assert!(matches!(listener.try_accept(), Ok(None)));
    let addr = listener.local_addr().unwrap();
    let TcpTryConnect::InProgress(con) = TcpStream::try_connect(&addr).unwrap() else {
        panic!("Failed to start connection");
    };
    let jh = std::thread::spawn(move || {
        let start = MonotonicInstant::now();
        let mut con = con;
        loop {
            match con.try_connect() {
                Ok(TcpTryConnect::Connected(c)) => break c,
                Ok(TcpTryConnect::InProgress(c)) => con = c,
                Err(e) => panic!("failed try_connect: {e}"),
            }
            assert!(
                (start.elapsed() <= Duration::from_millis(15)),
                "Failed to connect TCP in time"
            );
        }
    });
    let client_handle = listener
        .accept_with_timeout(Duration::from_millis(15))
        .unwrap();
    let client = jh.join().unwrap();
    verify_communication(client, client_handle);
}

#[test]
fn test_tcp_try_connect_then_block() {
    let ip = Ip::V4([127, 0, 0, 1]);
    let mut listener = TcpListener::bind(&SocketAddress::new(ip, 0)).unwrap();
    assert!(matches!(listener.try_accept(), Ok(None)));
    let addr = listener.local_addr().unwrap();
    let TcpTryConnect::InProgress(con) = TcpStream::try_connect(&addr).unwrap() else {
        panic!("Failed to start connection");
    };
    let jh = std::thread::spawn(move || con.connect_blocking().unwrap());
    let client_handle = listener
        .accept_with_timeout(Duration::from_millis(15))
        .unwrap();
    let client = jh.join().unwrap();
    verify_communication(client, client_handle);
}