#![cfg(feature = "network-discovery")]
#![allow(clippy::unwrap_used, clippy::expect_used)]
use ant_quic::candidate_discovery::NetworkInterfaceDiscovery;
fn is_skip_worthy_platform_monitoring_error(error: &str) -> bool {
let error = error.to_ascii_lowercase();
error.contains("permission denied")
|| error.contains("access is denied")
|| error.contains("operation not permitted")
|| error.contains("privilege is not held")
|| error.contains("cap_net_admin")
|| error.contains("dynamic store access failed: failed to create scdynamicstore")
|| (error.contains("network change notification failed")
&& (error.contains("code 5") || error.contains("code 1314")))
}
#[test]
fn test_monitoring_error_skip_classification_is_restrictive() {
assert!(is_skip_worthy_platform_monitoring_error(
"Failed to create netlink socket: Operation not permitted (os error 1)"
));
assert!(is_skip_worthy_platform_monitoring_error(
"Network change notification failed with code 5"
));
assert!(is_skip_worthy_platform_monitoring_error(
"Dynamic store access failed: Failed to create SCDynamicStore"
));
assert!(!is_skip_worthy_platform_monitoring_error(
"Dynamic store configuration failed"
));
assert!(!is_skip_worthy_platform_monitoring_error(
"Message receive failed: invalid netlink frame"
));
}
#[cfg(target_os = "windows")]
mod windows_tests {
use super::*;
use ant_quic::candidate_discovery::windows::WindowsInterfaceDiscovery;
use std::time::Duration;
#[test]
fn test_windows_ip_helper_api_functionality() {
let mut discovery = WindowsInterfaceDiscovery::new();
match discovery.start_scan() {
Ok(_) => {
std::thread::sleep(Duration::from_millis(100));
if let Some(interfaces) = discovery.check_scan_complete() {
println!("Found {} network interfaces on Windows", interfaces.len());
assert!(
!interfaces.is_empty(),
"Windows should have at least one network interface"
);
for interface in interfaces {
assert!(
!interface.name.is_empty(),
"Interface name should not be empty"
);
assert!(
!interface.addresses.is_empty(),
"Interface should have at least one address"
);
println!(
"Windows interface: {} with {} addresses",
interface.name,
interface.addresses.len()
);
}
} else {
panic!("Windows network scan did not complete");
}
}
Err(e) => {
if e.contains("Access is denied") || e.contains("permission") {
println!("Skipping test due to permission issues on CI: {}", e);
} else {
panic!("Failed to start Windows network scan: {}", e);
}
}
}
}
#[test]
fn test_windows_network_change_monitoring() {
let mut discovery = WindowsInterfaceDiscovery::new();
match discovery.enable_change_monitoring() {
Ok(()) => {
let changed = discovery.check_network_changes();
println!("Windows network changes detected: {changed}");
}
Err(e) => {
let error = e.to_string();
assert!(
is_skip_worthy_platform_monitoring_error(&error),
"Windows network monitoring setup failed unexpectedly: {error}"
);
println!("Skipping monitoring test due to environment error: {error}");
}
}
}
#[test]
#[ignore] fn test_windows_adapter_enumeration_stress() {
for i in 0..10 {
let mut discovery = WindowsInterfaceDiscovery::new();
match discovery.start_scan() {
Ok(_) => {
std::thread::sleep(Duration::from_millis(50));
if let Some(interfaces) = discovery.check_scan_complete() {
println!("Iteration {}: Found {} interfaces", i, interfaces.len());
}
}
Err(e) => println!("Iteration {} failed: {}", i, e),
}
}
}
}
#[cfg(target_os = "linux")]
mod linux_tests {
use super::*;
use ant_quic::candidate_discovery::linux::LinuxInterfaceDiscovery;
use std::time::Duration;
#[test]
fn test_linux_netlink_socket_functionality() {
let mut discovery = LinuxInterfaceDiscovery::new();
match discovery.start_scan() {
Ok(_) => {
std::thread::sleep(Duration::from_millis(100));
if let Some(interfaces) = discovery.check_scan_complete() {
println!("Found {} network interfaces on Linux", interfaces.len());
assert!(
!interfaces.is_empty(),
"Linux should have at least one network interface"
);
let has_loopback = interfaces.iter().any(|i| i.name == "lo");
if !has_loopback {
println!("Warning: No loopback interface found (may be normal in CI)");
}
for interface in interfaces {
assert!(
!interface.name.is_empty(),
"Interface name should not be empty"
);
println!(
"Linux interface: {} with {} addresses, up: {}",
interface.name,
interface.addresses.len(),
interface.is_up
);
}
} else {
panic!("Linux network scan did not complete");
}
}
Err(e) => {
panic!("Failed to start Linux network scan: {}", e);
}
}
}
#[test]
fn test_linux_proc_filesystem_access() {
assert!(
std::path::Path::new("/proc/net/dev").exists(),
"/proc/net/dev should exist on Linux"
);
match std::fs::read_to_string("/proc/net/dev") {
Ok(content) => {
assert!(
content.contains("lo:"),
"/proc/net/dev should contain loopback interface"
);
}
Err(e) => panic!("Cannot read /proc/net/dev: {}", e),
}
if std::path::Path::new("/proc/net/if_inet6").exists() {
println!("IPv6 support detected via /proc/net/if_inet6");
}
}
#[test]
fn test_linux_netlink_monitoring() {
let mut discovery = LinuxInterfaceDiscovery::new();
match discovery.initialize_netlink_socket() {
Ok(_) => {
println!("Linux netlink socket initialized successfully");
match discovery.check_network_changes() {
Ok(changes) => {
println!("Network changes detected: {}", changes);
}
Err(e) => {
let error = e.to_string();
assert!(
is_skip_worthy_platform_monitoring_error(&error),
"Linux netlink monitoring check failed unexpectedly: {error}"
);
println!("Skipping monitoring check due to environment error: {error}");
}
}
}
Err(e) => {
let error = e.to_string();
assert!(
is_skip_worthy_platform_monitoring_error(&error),
"Linux netlink initialization failed unexpectedly: {error}"
);
println!("Skipping netlink monitoring due to environment error: {error}");
}
}
}
#[test]
#[ignore] fn test_linux_netlink_namespace() {
println!("Network namespace test would run with appropriate privileges");
}
#[test]
fn test_linux_interface_enumeration_stress() {
for i in 0..10 {
let mut discovery = LinuxInterfaceDiscovery::new();
match discovery.start_scan() {
Ok(_) => {
std::thread::sleep(Duration::from_millis(50));
if let Some(interfaces) = discovery.check_scan_complete() {
println!("Iteration {}: Found {} interfaces", i, interfaces.len());
}
}
Err(e) => panic!("Iteration {} failed: {}", i, e),
}
}
}
}
#[cfg(target_os = "macos")]
mod macos_tests {
use super::*;
use ant_quic::candidate_discovery::macos::MacOSInterfaceDiscovery;
use std::time::Duration;
#[test]
fn test_macos_system_configuration_functionality() {
let mut discovery = MacOSInterfaceDiscovery::new();
match discovery.start_scan() {
Ok(_) => {
std::thread::sleep(Duration::from_millis(100));
if let Some(interfaces) = discovery.check_scan_complete() {
println!("Found {} network interfaces on macOS", interfaces.len());
assert!(
!interfaces.is_empty(),
"macOS should have at least one network interface"
);
let has_loopback = interfaces.iter().any(|i| i.name == "lo0");
if !has_loopback {
println!("Warning: No lo0 interface found (may be normal in CI)");
}
for interface in interfaces {
assert!(
!interface.name.is_empty(),
"Interface name should not be empty"
);
println!(
"macOS interface: {} with {} addresses, wireless: {}",
interface.name,
interface.addresses.len(),
interface.is_wireless
);
}
} else {
panic!("macOS network scan did not complete");
}
}
Err(e) => {
panic!("Failed to start macOS network scan: {}", e);
}
}
}
#[test]
fn test_macos_scf_dynamic_store() {
let mut discovery = MacOSInterfaceDiscovery::new();
match discovery.initialize_dynamic_store() {
Ok(_) => {
println!("macOS SCDynamicStore created successfully");
assert!(
discovery.sc_store.is_some(),
"Dynamic store should be initialized"
);
}
Err(e) => {
println!(
"Dynamic store creation failed (may be normal on CI): {:?}",
e
);
}
}
}
#[test]
fn test_macos_framework_availability() {
let frameworks = [
"/System/Library/Frameworks/SystemConfiguration.framework",
"/System/Library/Frameworks/CoreFoundation.framework",
];
for framework in &frameworks {
assert!(
std::path::Path::new(framework).exists(),
"Required framework {} should exist",
framework
);
}
}
#[test]
fn test_macos_network_change_monitoring() {
let mut discovery = MacOSInterfaceDiscovery::new();
match discovery.enable_change_monitoring() {
Ok(_) => {
println!("macOS network monitoring initialized");
assert!(
discovery.sc_store.is_some(),
"Dynamic store should be initialized for monitoring"
);
let changed = discovery.check_network_changes();
println!("Network changes detected: {}", changed);
}
Err(e) => {
let error = e.to_string();
assert!(
is_skip_worthy_platform_monitoring_error(&error),
"macOS network monitoring setup failed unexpectedly: {error}"
);
println!("Skipping monitoring test due to environment error: {error}");
}
}
}
#[test]
#[ignore] fn test_macos_interface_enumeration_stress() {
for i in 0..10 {
let mut discovery = MacOSInterfaceDiscovery::new();
match discovery.start_scan() {
Ok(_) => {
std::thread::sleep(Duration::from_millis(50));
if let Some(interfaces) = discovery.check_scan_complete() {
println!("Iteration {}: Found {} interfaces", i, interfaces.len());
}
}
Err(e) => panic!("Iteration {} failed: {}", i, e),
}
}
}
}
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
#[test]
fn test_platform_interface_consistency() {
#[cfg(target_os = "windows")]
let mut discovery = ant_quic::candidate_discovery::windows::WindowsInterfaceDiscovery::new();
#[cfg(target_os = "linux")]
let mut discovery = ant_quic::candidate_discovery::linux::LinuxInterfaceDiscovery::new();
#[cfg(target_os = "macos")]
let mut discovery = ant_quic::candidate_discovery::macos::MacOSInterfaceDiscovery::new();
match discovery.start_scan() {
Ok(_) => {
std::thread::sleep(std::time::Duration::from_millis(100));
if let Some(interfaces) = discovery.check_scan_complete() {
for interface in interfaces {
assert!(!interface.name.is_empty());
assert!(interface.mtu.is_none() || interface.mtu.unwrap() >= 576);
for addr in &interface.addresses {
assert!(addr.port() == 0, "Interface addresses should have port 0");
}
}
}
}
Err(e) => {
println!("Platform consistency test skipped due to: {}", e);
}
}
}