#![cfg(target_os = "macos")]
use std::net::Ipv4Addr;
use arcbox_vmnet::{Vmnet, VmnetConfig, VmnetError, VmnetInterfaceInfo, VmnetMode};
#[test]
fn config_default_is_shared_nat() {
let config = VmnetConfig::default();
assert_eq!(config.mode, VmnetMode::Shared);
assert_eq!(config.mtu, 1500);
assert!(config.mac.is_none());
assert!(config.bridge_interface.is_none());
assert!(config.dhcp_start.is_none());
assert!(!config.isolated);
}
#[test]
fn shared_builder_chain_sets_all_fields() {
let mac = [0x02, 0x00, 0x00, 0x12, 0x34, 0x56];
let config = VmnetConfig::shared()
.with_mac(mac)
.with_mtu(9000)
.with_dhcp_range(Ipv4Addr::new(10, 0, 0, 2), Ipv4Addr::new(10, 0, 0, 254))
.with_subnet_mask(Ipv4Addr::new(255, 255, 255, 0));
assert_eq!(config.mode, VmnetMode::Shared);
assert_eq!(config.mac, Some(mac));
assert_eq!(config.mtu, 9000);
assert_eq!(config.dhcp_start, Some(Ipv4Addr::new(10, 0, 0, 2)));
assert_eq!(config.dhcp_end, Some(Ipv4Addr::new(10, 0, 0, 254)));
assert_eq!(config.subnet_mask, Some(Ipv4Addr::new(255, 255, 255, 0)));
}
#[test]
fn host_only_with_isolation() {
let config = VmnetConfig::host_only().with_isolation(true);
assert_eq!(config.mode, VmnetMode::HostOnly);
assert!(config.isolated);
}
#[test]
fn bridged_carries_interface_name() {
let config = VmnetConfig::bridged("en0");
assert_eq!(config.mode, VmnetMode::Bridged);
assert_eq!(config.bridge_interface.as_deref(), Some("en0"));
}
#[test]
fn vmnet_mode_default_is_shared() {
assert_eq!(VmnetMode::default(), VmnetMode::Shared);
}
#[test]
fn vmnet_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Vmnet>();
assert_send_sync::<VmnetConfig>();
assert_send_sync::<VmnetInterfaceInfo>();
}
#[test]
fn config_error_carries_message() {
let err = VmnetError::config("bad input");
let s = err.to_string();
assert!(s.contains("bad input"), "display missing message: {s}");
assert!(s.contains("vmnet"), "display missing crate prefix: {s}");
assert!(matches!(err, VmnetError::Config(_)));
}
#[test]
fn io_error_converts_into_vmnet_error() {
let io = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
let err: VmnetError = io.into();
assert!(matches!(err, VmnetError::Io(_)));
}
#[test]
fn interface_info_fields_round_trip() {
let info = VmnetInterfaceInfo {
mac: [0x02, 0x00, 0x00, 0x00, 0x00, 0x01],
mtu: 1500,
max_packet_size: 1518,
};
let cloned = info.clone();
assert_eq!(cloned.mac, info.mac);
assert_eq!(cloned.mtu, info.mtu);
assert_eq!(cloned.max_packet_size, info.max_packet_size);
}
#[test]
fn bridged_without_interface_fails_with_config_error() {
let config = VmnetConfig {
mode: VmnetMode::Bridged,
bridge_interface: None,
..Default::default()
};
match Vmnet::new(config) {
Err(VmnetError::Config(msg)) => {
assert!(msg.contains("bridge mode"), "unexpected message: {msg}");
}
Ok(_) => panic!("bridged mode without interface should fail"),
Err(other) => panic!("expected config error, got {other:?}"),
}
}
#[test]
fn bridged_with_nul_in_interface_name_fails_cleanly() {
let config = VmnetConfig::bridged("en0\0evil");
match Vmnet::new(config) {
Err(VmnetError::Config(msg)) => {
assert!(msg.contains("NUL"), "unexpected message: {msg}");
}
Ok(_) => panic!("interface name with NUL should fail"),
Err(other) => panic!("expected config error, got {other:?}"),
}
}
#[test]
#[ignore = "requires com.apple.vm.networking entitlement or root"]
fn shared_interface_starts_and_stops() {
let vmnet = Vmnet::new_shared().expect("vmnet start");
assert!(vmnet.is_running());
assert!(vmnet.max_packet_size() >= 1500);
vmnet.stop();
assert!(!vmnet.is_running());
}
#[test]
#[ignore = "requires com.apple.vm.networking entitlement or root"]
fn host_only_isolated_interface_starts() {
let config = VmnetConfig::host_only().with_isolation(true);
let vmnet = Vmnet::new(config).expect("vmnet start");
assert!(vmnet.is_running());
}
#[test]
#[ignore = "requires com.apple.vm.networking entitlement or root"]
fn read_with_no_traffic_returns_zero() {
let vmnet = Vmnet::new_shared().expect("vmnet start");
let mut buf = [0u8; 1500];
let n = vmnet.read_packet(&mut buf).expect("read");
assert_eq!(n, 0);
}