Files
RedBear-OS/local/recipes/system/redbear-netctl/source/tests/cli_wifi.rs
T
2026-04-16 12:44:35 +01:00

382 lines
12 KiB
Rust

use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
use redbear_netctl_console::backend::{
ConsoleBackend, FsBackend, IpMode, Profile, RuntimePaths, SecurityKind,
};
fn temp_root(prefix: &str) -> PathBuf {
let stamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let path = std::env::temp_dir().join(format!("{prefix}-{stamp}"));
fs::create_dir_all(&path).unwrap();
path
}
fn run_netctl(
args: &[&str],
profile_dir: &PathBuf,
wifictl_root: &PathBuf,
netcfg_root: &PathBuf,
) -> String {
let output = Command::new(env!("CARGO_BIN_EXE_redbear-netctl"))
.args(args)
.env("REDBEAR_NETCTL_PROFILE_DIR", profile_dir)
.env("REDBEAR_WIFICTL_ROOT", wifictl_root)
.env("REDBEAR_NETCFG_ROOT", netcfg_root)
.env("REDBEAR_DHCPD_CMD", "/usr/bin/true")
.output()
.unwrap();
assert!(
output.status.success(),
"command {:?} failed: {}",
args,
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).unwrap()
}
fn console_runtime_paths(
profile_dir: &PathBuf,
wifictl_root: &PathBuf,
netcfg_root: &PathBuf,
dhcpd_command: &str,
) -> RuntimePaths {
RuntimePaths {
profile_dir: profile_dir.clone(),
active_profile_path: profile_dir.join("active"),
wifictl_root: wifictl_root.clone(),
netcfg_root: netcfg_root.clone(),
dhcpd_command: dhcpd_command.to_string(),
dhcp_wait_timeout: std::time::Duration::from_millis(500),
dhcp_poll_interval: std::time::Duration::from_millis(10),
}
}
#[test]
fn cli_start_wifi_profile_writes_connect_path() {
let profile_dir = temp_root("rbos-netctl-cli-profiles");
let wifictl_root = temp_root("rbos-netctl-cli-wifictl");
let netcfg_root = temp_root("rbos-netctl-cli-netcfg");
fs::create_dir_all(wifictl_root.join("ifaces/wlan0")).unwrap();
fs::create_dir_all(netcfg_root.join("ifaces/wlan0/addr")).unwrap();
fs::write(
netcfg_root.join("ifaces/wlan0/addr/list"),
"Not configured\n",
)
.unwrap();
fs::write(
profile_dir.join("wifi-dhcp"),
"Description='Wi-Fi'\nInterface=wlan0\nConnection=wifi\nSSID='demo'\nSecurity=wpa2-psk\nKey='secret'\nIP=dhcp\n",
)
.unwrap();
fs::write(wifictl_root.join("ifaces/wlan0/status"), "connected\n").unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/link-state"),
"link=connected\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/firmware-status"),
"firmware=present\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/transport-status"),
"transport=active\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/transport-init-status"),
"transport_init=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/activation-status"),
"activation=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/connect-result"),
"connect_result=bounded-associated ssid=demo security=wpa2-psk\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/disconnect-result"),
"disconnect_result=bounded-disconnected\n",
)
.unwrap();
let dhcp_log = profile_dir.join("dhcp.log");
let dhcp_script = profile_dir.join("fake-dhcpd.sh");
fs::write(
&dhcp_script,
format!(
"#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$1\" > '{}'\nprintf '10.0.0.44/24\\n' > '{}/ifaces/wlan0/addr/list'\n",
dhcp_log.display(),
netcfg_root.display()
),
)
.unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&dhcp_script).unwrap().permissions();
perms.set_mode(0o755);
fs::set_permissions(&dhcp_script, perms).unwrap();
}
let output = Command::new(env!("CARGO_BIN_EXE_redbear-netctl"))
.args(["start", "wifi-dhcp"])
.env("REDBEAR_NETCTL_PROFILE_DIR", &profile_dir)
.env("REDBEAR_WIFICTL_ROOT", &wifictl_root)
.env("REDBEAR_NETCFG_ROOT", &netcfg_root)
.env("REDBEAR_DHCPD_CMD", &dhcp_script)
.env("REDBEAR_DHCPD_WAIT_MS", "500")
.env("REDBEAR_DHCPD_POLL_MS", "10")
.output()
.unwrap();
assert!(
output.status.success(),
"command {:?} failed: {}",
["start", "wifi-dhcp"],
String::from_utf8_lossy(&output.stderr)
);
let started = String::from_utf8(output.stdout).unwrap();
assert!(started.contains("started wifi-dhcp"));
assert_eq!(fs::read_to_string(&dhcp_log).unwrap(), "wlan0\n");
assert_eq!(
fs::read_to_string(netcfg_root.join("ifaces/wlan0/addr/list")).unwrap(),
"10.0.0.44/24\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/ssid")).unwrap(),
"demo\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/security")).unwrap(),
"wpa2-psk\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/key")).unwrap(),
"secret\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/prepare")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/init-transport")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/activate-nic")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/connect")).unwrap(),
"1\n"
);
let status = run_netctl(
&["status", "wifi-dhcp"],
&profile_dir,
&wifictl_root,
&netcfg_root,
);
assert!(status.contains("address=10.0.0.44/24"));
assert!(status.contains("wifi_status=connected"));
assert!(status.contains("connect_result=bounded-associated ssid=demo security=wpa2-psk"));
assert!(status.contains("disconnect_result=bounded-disconnected"));
let stopped = run_netctl(
&["stop", "wifi-dhcp"],
&profile_dir,
&wifictl_root,
&netcfg_root,
);
assert!(stopped.contains("stopped wifi-dhcp"));
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/disconnect")).unwrap(),
"1\n"
);
}
#[test]
fn cli_status_reports_pending_wifi_link_honestly() {
let profile_dir = temp_root("rbos-netctl-cli-pending-profiles");
let wifictl_root = temp_root("rbos-netctl-cli-pending-wifictl");
let netcfg_root = temp_root("rbos-netctl-cli-pending-netcfg");
fs::create_dir_all(wifictl_root.join("ifaces/wlan0")).unwrap();
fs::create_dir_all(netcfg_root.join("ifaces/wlan0/addr")).unwrap();
fs::write(
profile_dir.join("wifi-open-bounded"),
"Description='Wi-Fi bounded'\nInterface=wlan0\nConnection=wifi\nSSID='demo'\nSecurity=wpa2-psk\nKey='secret'\nIP=bounded\n",
)
.unwrap();
fs::write(profile_dir.join("active"), "wifi-open-bounded\n").unwrap();
fs::write(wifictl_root.join("ifaces/wlan0/status"), "associating\n").unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/link-state"),
"link=associating\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/firmware-status"),
"firmware=present\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/transport-status"),
"transport=active\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/transport-init-status"),
"transport_init=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/activation-status"),
"activation=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/connect-result"),
"connect_result=host-bounded-pending ssid=demo security=wpa2-psk\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/disconnect-result"),
"disconnect_result=bounded-disconnected\n",
)
.unwrap();
fs::write(
netcfg_root.join("ifaces/wlan0/addr/list"),
"Not configured\n",
)
.unwrap();
let status = run_netctl(
&["status", "wifi-open-bounded"],
&profile_dir,
&wifictl_root,
&netcfg_root,
);
assert!(status.contains("wifi_status=associating"));
assert!(status.contains("link_state=link=associating"));
assert!(status.contains("connect_result=host-bounded-pending ssid=demo security=wpa2-psk"));
}
#[test]
fn cli_start_consumes_console_written_wifi_profile() {
let profile_dir = temp_root("rbos-netctl-cli-console-profile");
let wifictl_root = temp_root("rbos-netctl-cli-console-wifictl");
let netcfg_root = temp_root("rbos-netctl-cli-console-netcfg");
fs::create_dir_all(wifictl_root.join("ifaces/wlan0")).unwrap();
fs::create_dir_all(netcfg_root.join("ifaces/wlan0/addr")).unwrap();
fs::write(
netcfg_root.join("ifaces/wlan0/addr/list"),
"Not configured\n",
)
.unwrap();
fs::write(wifictl_root.join("ifaces/wlan0/status"), "associating\n").unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/link-state"),
"link=associating\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/transport-init-status"),
"transport_init=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/activation-status"),
"activation=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/connect-result"),
"connect_result=host-bounded-pending ssid=console-demo security=wpa2-psk\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/disconnect-result"),
"disconnect_result=not-run\n",
)
.unwrap();
let console_backend = FsBackend::new(console_runtime_paths(
&profile_dir,
&wifictl_root,
&netcfg_root,
"/usr/bin/true",
));
let profile = Profile {
name: "wifi-console-bounded".to_string(),
description: "Console written Wi-Fi profile".to_string(),
interface: "wlan0".to_string(),
ssid: "console-demo".to_string(),
security: SecurityKind::Wpa2Psk,
key: "secret".to_string(),
ip_mode: IpMode::Bounded,
address: String::new(),
gateway: String::new(),
dns: String::new(),
};
console_backend.save_profile(&profile).unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_redbear-netctl"))
.args(["start", "wifi-console-bounded"])
.env("REDBEAR_NETCTL_PROFILE_DIR", &profile_dir)
.env("REDBEAR_WIFICTL_ROOT", &wifictl_root)
.env("REDBEAR_NETCFG_ROOT", &netcfg_root)
.env("REDBEAR_DHCPD_CMD", "/usr/bin/true")
.output()
.unwrap();
assert!(
output.status.success(),
"command {:?} failed: {}",
["start", "wifi-console-bounded"],
String::from_utf8_lossy(&output.stderr)
);
let started = String::from_utf8(output.stdout).unwrap();
assert!(started.contains("started wifi-console-bounded"));
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/ssid")).unwrap(),
"console-demo\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/security")).unwrap(),
"wpa2-psk\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/key")).unwrap(),
"secret\n"
);
let status = run_netctl(
&["status", "wifi-console-bounded"],
&profile_dir,
&wifictl_root,
&netcfg_root,
);
assert!(status.contains("profile=wifi-console-bounded"));
assert!(status.contains("wifi_status=associating"));
assert!(status.contains("link_state=link=associating"));
assert!(
status.contains("connect_result=host-bounded-pending ssid=console-demo security=wpa2-psk")
);
}