ax-net 0.13.1

Unified network stack for TGOSKits (ArceOS, StarryOS, Axvisor)
extern crate alloc;

use alloc::boxed::Box;
use core::{cell::Cell, time::Duration};

use ax_io::IoError;
use ax_net::{
    InterfaceId, NetError, NetResult,
    options::{Configurable, GetSocketOption, SetSocketOption, TcpInfo, TcpInfoOptions, TcpState},
};
use ax_runtime as _;

#[test]
fn ax_net_interface_ids_validate_linux_ifindices() {
    let id = InterfaceId::new(7);
    assert_eq!(id.to_linux_ifindex(), 7);
    assert_eq!(InterfaceId::from_linux_ifindex(7), Some(id));
    assert_eq!(InterfaceId::from_linux_ifindex(0), None);
    assert_eq!(InterfaceId::from_linux_ifindex(-1), None);
}

struct MockConfigurable {
    supported: bool,
    set_calls: Cell<usize>,
}

impl Configurable for MockConfigurable {
    fn get_option_inner(&self, opt: &mut GetSocketOption) -> NetResult<bool> {
        match opt {
            GetSocketOption::ReuseAddress(value) => **value = true,
            GetSocketOption::SendTimeout(value) => **value = Duration::from_millis(7),
            GetSocketOption::TcpInfo(value) => {
                **value = TcpInfo {
                    state: TcpState::Established,
                    options: TcpInfoOptions::SACK | TcpInfoOptions::TIMESTAMPS,
                    snd_mss: 1460,
                    rcv_mss: 1460,
                    ..TcpInfo::default()
                };
            }
            _ => {}
        }
        Ok(self.supported)
    }

    fn set_option_inner(&self, opt: SetSocketOption) -> NetResult<bool> {
        if matches!(
            opt,
            SetSocketOption::NoDelay(true) | SetSocketOption::KeepAlive(true)
        ) {
            self.set_calls.set(self.set_calls.get() + 1);
        }
        Ok(self.supported)
    }
}

#[test]
fn ax_net_socket_options_dispatch_supported_and_unsupported_results() {
    let configurable = MockConfigurable {
        supported: true,
        set_calls: Cell::new(0),
    };

    let mut reuse = false;
    configurable
        .get_option(GetSocketOption::ReuseAddress(&mut reuse))
        .unwrap();
    assert!(reuse);

    let mut timeout = Duration::ZERO;
    configurable
        .get_option(GetSocketOption::SendTimeout(&mut timeout))
        .unwrap();
    assert_eq!(timeout, Duration::from_millis(7));

    let mut tcp_info = TcpInfo::default();
    configurable
        .get_option(GetSocketOption::TcpInfo(&mut tcp_info))
        .unwrap();
    assert_eq!(tcp_info.state, TcpState::Established);
    assert!(tcp_info.options.contains(TcpInfoOptions::SACK));
    assert_eq!(tcp_info.snd_mss, 1460);

    configurable
        .set_option(SetSocketOption::NoDelay(&true))
        .unwrap();
    configurable
        .set_option(SetSocketOption::KeepAlive(&true))
        .unwrap();
    assert_eq!(configurable.set_calls.get(), 2);

    let boxed: Box<dyn Configurable> = Box::new(MockConfigurable {
        supported: true,
        set_calls: Cell::new(0),
    });
    boxed.set_option(SetSocketOption::NoDelay(&true)).unwrap();

    let unsupported = MockConfigurable {
        supported: false,
        set_calls: Cell::new(0),
    };
    assert_eq!(
        unsupported
            .set_option(SetSocketOption::NoDelay(&true))
            .unwrap_err(),
        NetError::ProtocolOptionUnsupported
    );
    let mut reuse = false;
    assert_eq!(
        unsupported
            .get_option(GetSocketOption::ReuseAddress(&mut reuse))
            .unwrap_err(),
        NetError::ProtocolOptionUnsupported
    );
}

#[test]
fn ax_net_interrupted_error_preserves_io_semantics() {
    assert_eq!(IoError::from(NetError::Interrupted), IoError::Interrupted);
}