diff --git a/.github/buildomat/packet-test-common.sh b/.github/buildomat/packet-test-common.sh index 6df876f4..33f4b0fa 100755 --- a/.github/buildomat/packet-test-common.sh +++ b/.github/buildomat/packet-test-common.sh @@ -97,22 +97,6 @@ banner "Links" ./target/debug/swadm --host '[::1]' link ls || echo "failed to list links" -banner "swadm Checks" - -pushd swadm - -DENDRITE_TEST_HOST='[::1]' \ - DENDRITE_TEST_VERBOSITY=3 \ - cargo test \ - --no-fail-fast \ - $SWADM_FEATURES \ - --test \ - counters \ - -- \ - --ignored - -popd - banner "Packet Tests" set +o errexit @@ -132,3 +116,19 @@ DENDRITE_TEST_HOST='[::1]' \ -- \ --ignored \ --skip succeeds_when_table_fragmented + +popd + +banner "swadm checks" + +pushd swadm + +DENDRITE_TEST_HOST='[::1]' \ + DENDRITE_TEST_VERBOSITY=3 \ + cargo test \ + --no-fail-fast \ + $SWADM_FEATURES \ + -- \ + --ignored + +popd diff --git a/Cargo.lock b/Cargo.lock index e4f7141f..7b4912e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -946,7 +946,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.0", ] [[package]] @@ -2136,7 +2136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] @@ -3067,7 +3067,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-layer", @@ -3497,7 +3497,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3565,7 +3565,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5718,7 +5718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.118", @@ -5784,7 +5784,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.40", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -5821,9 +5821,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -6189,7 +6189,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6202,7 +6202,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] @@ -6706,6 +6706,31 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "serial_test" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "699f4197115b8a7e7ff19c9a315a4bd6fffec26cc4626ef45ecaea389e081c6d" +dependencies = [ + "futures-executor", + "futures-util", + "log", + "once_cell", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "sha1" version = "0.10.6" @@ -7070,7 +7095,7 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.118", @@ -7282,6 +7307,7 @@ dependencies = [ "oxnet", "regex", "reqwest 0.13.2", + "serial_test", "slog", "tabwriter", "tokio", @@ -7438,7 +7464,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] @@ -8986,7 +9012,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.0", ] [[package]] diff --git a/swadm/Cargo.toml b/swadm/Cargo.toml index 2c0bc18b..61857068 100644 --- a/swadm/Cargo.toml +++ b/swadm/Cargo.toml @@ -24,3 +24,7 @@ reqwest.workspace = true slog.workspace = true tabwriter.workspace = true tokio.workspace = true + +[dev-dependencies] +serial_test = "3.5.0" + diff --git a/swadm/README.md b/swadm/README.md new file mode 100644 index 00000000..df0ce963 --- /dev/null +++ b/swadm/README.md @@ -0,0 +1,17 @@ +# SW(itch) ADM(in) + +This is the management CLI for Oxide's rack switch. + +## Testing + +swadm is widely used across scripts and documentation. Changes +should generally be backward compatible. + +This is definitionally a string-typed interface, so regressions are +easily missed. The integration tests module has infra for testing +commands, and adding a test before making swadm changes might +help prevent drift. + +These tests are run in Linux CI but currently ignored in Illumos CI. +Illumos CI is blocked by tofino simulator support: https://github.com/oxidecomputer/tofino-sde/issues/21 + diff --git a/swadm/src/link.rs b/swadm/src/link.rs index 47e7e34a..66d9c610 100644 --- a/swadm/src/link.rs +++ b/swadm/src/link.rs @@ -436,7 +436,7 @@ pub enum Link { /// Whether the link is configured to autonegotiate with its peer during /// link training. /// - /// This is generally only true for backplane links, and defaults to + /// This is generally only true for backplane links and defaults to false. #[clap(long)] autoneg: bool, /// Whether the link is configured in KR mode, an electrical specification diff --git a/swadm/tests/cli/cmd.rs b/swadm/tests/cli/cmd.rs new file mode 100644 index 00000000..c27b2af1 --- /dev/null +++ b/swadm/tests/cli/cmd.rs @@ -0,0 +1,251 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/ +// +// Copyright 2026 Oxide Computer Company + +//! # Cmd +//! +//! This module defines helpers for executing swadm CLI +//! commands and validating their output. +//! +//! They assume a test environment in which swadm can +//! reach dpd and control it without interference. + +use std::borrow::Cow; +use std::process::Command; + +use anyhow::Context; +use anyhow::bail; +use regex::Regex; + +const SWADM: &str = env!("CARGO_BIN_EXE_swadm"); + +// Integration test libraries don't support cargo doc tests. +// See test modules for examples. + +/// This macro simplifies working with the [`Pattern`] type +/// when validating the output of [`swadm`]. +/// +/// Regex consts from [`re`], literals, and numbers are generally +/// accepted. +#[macro_export] +macro_rules! pat { + [$($p:expr),*] => { + [$($crate::cmd::Pattern::from($p)),*] + }; +} + +/// Runs a `swadm` CLI command with the given args. +pub fn swadm(input: impl AsRef) -> anyhow::Result { + let mut args = vec!["--host", "[::1]"]; + args.extend(input.as_ref().trim().split_ascii_whitespace()); + + let output = + Command::new(self::SWADM).args(&args).output().with_context(|| { + format!("swadm CLI command failed: args = {args:#?}") + })?; + + if !output.status.success() { + bail!("cmd failed: {args:?}: {:?}", String::from_utf8(output.stderr)); + } + + let stdout = std::str::from_utf8(&output.stdout) + .context("failed to decode stdout as utf-8")? + .into(); + + Ok(Out { stdout }) +} + +/// Common regex patterns for searching swadm output. +pub mod re { + use super::Pattern; + + /// Anything up until the next match. + pub const ANY: Pattern = Pattern::regex(r".*?"); + + /// One or more consecutive non whitespace chars. + pub const WORD: Pattern = Pattern::regex(r"\S+"); + + /// One or more whitespace chars. + pub const SPACE: Pattern = Pattern::regex(r"\s+"); + + /// A block of parentheses with characters inside. Doesn't + /// support nested parentheses. + pub const PARENS: Pattern = Pattern::regex(r"\([^)]*\)"); +} + +/// A wrapper type for regex inputs that can be +/// chained to validate swadm CLI output. +pub struct Pattern(Cow<'static, str>); + +impl Pattern { + /// Creates a regex pattern. + pub const fn regex(re: &'static str) -> Self { + Self(Cow::Borrowed(re)) + } + + /// Creates a text literal that will not engage regex semantics. + pub fn literal(lit: impl AsRef) -> Self { + Self(regex::escape(lit.as_ref()).into()) + } +} + +impl AsRef for Pattern { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl From<&str> for Pattern { + fn from(value: &str) -> Self { + Self::literal(value) + } +} + +impl From for Pattern { + fn from(value: String) -> Self { + Self::literal(value) + } +} + +// The number implementations cause allocations. But this is +// for tests, and it's convenient. + +impl From for Pattern { + fn from(value: i8) -> Self { + Self::literal(format!("{value}")) + } +} + +impl From for Pattern { + fn from(value: i32) -> Self { + Self::literal(format!("{value}")) + } +} + +impl From for Pattern { + fn from(value: usize) -> Self { + Self::literal(format!("{value}")) + } +} + +/// This contains the stdout of a successful [`swadm`] command +/// and can be used for parsing CLI output. +#[derive(Debug)] +pub struct Out { + stdout: Box, +} + +impl Out { + /// Searches for a single line in the output matching the + /// given pattern. + /// + /// Patterns expect whitespace between members and can be + /// easily constructed using the [`pat`] macro and [`Pattern`] + /// type. + /// + /// This function may fail if the merged patterns form an + /// invalid regex. + /// + /// Given a valid regex, this succeeds IFF there is a + /// single matching line in the output. + pub fn expect_line( + &self, + pattern: impl IntoIterator>, + ) -> anyhow::Result<()> { + let reg = Self::make_regex(pattern)?; + + let count = self + .stdout + .lines() + .filter(|line| reg.is_match(line.trim())) + .count(); + + if count != 1 { + bail!( + " +Expected exactly one match, found {count:?}. + Regex: {reg:?} + Stdout: {}", + self.stdout + ); + } + + Ok(()) + } + + /// Returns swadm's raw stdout. + pub fn stdout(&self) -> &str { + &self.stdout + } + + /// Merges a user-friendly input iterator of patterns into a + /// line matching regex. Returns err if the regex fails to compile. + fn make_regex( + pattern: impl IntoIterator>, + ) -> anyhow::Result { + let mut line_match = String::new(); + + for (idx, pat) in pattern.into_iter().enumerate() { + if idx != 0 { + line_match.push_str(re::SPACE.as_ref()); + } + line_match.push_str(pat.as_ref()); + } + + Regex::new(&line_match) + .with_context(|| format!("Regex failed to compile: {line_match:?}")) + } +} + +#[cfg(test)] +mod test { + use crate::cmd::{ + Out, + re::{ANY, PARENS, WORD}, + }; + + /// Validates output parsing against txeq output. + #[test] + fn output_txeq() -> anyhow::Result<()> { + // Tap values are nonsensical. We could proptest this, but idk + // if that's warranted complexity in a test for a test for a CLI tool. + + const TXEQ_STDOUT: &str = " + lane 0 lane 1 lane 2 lane 3 +pre2 0 (111) 1 ( 11) 2 ( 1) 3 ( 11) +pre1 -1 ( 11) -2 ( 11) -3 ( 11) -4 ( 11) +main 19 ( 11) 20 ( 11) 21 ( 11) 22 ( 11) +post1 -2 ( 1) -13 ( -2) -9 (-11) -22 (-123) +post2 -123 ( 11) 456 ( 11) 0 ( 11) 0 ( 11) +"; + + let out = Out { stdout: TXEQ_STDOUT.into() }; + + // Verify header + out.expect_line(pat!["lane 0", "lane 1", "lane 2", "lane 3"])?; + + // Post2 across all four lanes. + out.expect_line(pat![ + "post2", "-123", PARENS, "456", PARENS, "0", PARENS, "0", PARENS + ])?; + + // Lane 2 across all five parameters. + for (spot, value) in [ + ("pre2", "1"), + ("pre1", "-2"), + ("main", "20"), + ("post1", "-13"), + ("post2", "456"), + ] { + out.expect_line(pat![ + spot, WORD, PARENS, value, PARENS, WORD, PARENS, WORD, PARENS + ])?; + + out.expect_line(pat![spot, WORD, PARENS, value, ANY])?; + } + + Ok(()) + } +} diff --git a/swadm/tests/counters.rs b/swadm/tests/cli/counters.rs similarity index 72% rename from swadm/tests/counters.rs rename to swadm/tests/cli/counters.rs index f01f7e15..6055f41c 100644 --- a/swadm/tests/counters.rs +++ b/swadm/tests/cli/counters.rs @@ -6,33 +6,18 @@ //! Integration test for swadm P4 counter functionality. -use std::process::Command; +use serial_test::serial; -// Path to `swadm` executable. -const SWADM: &str = env!("CARGO_BIN_EXE_swadm"); - -fn swadm() -> Command { - Command::new(SWADM) -} +use crate::cmd; #[test] #[ignore] -fn test_p4_counter_list() { - let output = swadm() - .arg("--host") - .arg("[::1]") - .arg("counters") - .arg("list") - .output() +#[serial] +fn counters_list() { + let output = cmd::swadm("counters list") .expect("Failed to execute swadm counters list"); - assert!( - output.status.success(), - "swadm counters list failed with stderr: {}", - String::from_utf8_lossy(&output.stderr) - ); - - let stdout = String::from_utf8_lossy(&output.stdout); + let stdout = output.stdout(); // Verify output is not empty and contains expected counter information assert!(!stdout.is_empty(), "Counter list output should not be empty"); diff --git a/swadm/tests/cli/link_apply.rs b/swadm/tests/cli/link_apply.rs new file mode 100644 index 00000000..8a360bbd --- /dev/null +++ b/swadm/tests/cli/link_apply.rs @@ -0,0 +1,92 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/ +// +// Copyright 2026 Oxide Computer Company + +use serial_test::serial; + +use crate::cmd; +use crate::cmd::re::PARENS; +use crate::pat; + +const LINK: &str = "rear0/0"; + +/// Tests the `tx-eq` flag in link settings apply. Verifies that +/// every tap of every lane on the resulting 100g link has +/// the same value. +#[test] +#[serial] +#[ignore] +fn apply_tx_eq_all() -> anyhow::Result<()> { + let val = -1; + + cmd::swadm(format!( + "link apply + --link {LINK} + --tag test + --fec rs + --speed 100g + --lane 0 + --tx-eq={val}" + ))?; + + let tx_eq = cmd::swadm(format!("link serdes get txeq {LINK}"))?; + for label in ["pre2", "pre1", "main", "post1", "post2"] { + tx_eq.expect_line(pat![ + label, val, PARENS, val, PARENS, val, PARENS, val, PARENS + ])?; + } + + Ok(()) +} + +/// Tests the individual tx eq flags in link settings apply. +/// If one or more taps is explicitly declared, the other taps +/// should default to zero. +#[test] +#[serial] +#[ignore] +fn apply_tx_eq_custom() -> anyhow::Result<()> { + cmd::swadm(format!( + "link apply + --link {LINK} + --tag test + --fec rs + --speed 100g + --lane 0 + --main=-22 + --post1 5" + ))?; + + let tx_eq = cmd::swadm(format!("link serdes get txeq {LINK}"))?; + for (label, val) in + [("pre2", 0), ("pre1", 0), ("main", -22), ("post1", 5), ("post2", 0)] + { + tx_eq.expect_line(pat![ + label, val, PARENS, val, PARENS, val, PARENS, val, PARENS + ])?; + } + + Ok(()) +} + +/// Verifies that the `tx-eq` shorthand and explicit +/// tap flags are mutually exclusive. +#[test] +#[serial] +#[ignore] +fn tx_eq_exclusive() { + cmd::swadm(format!( + "link apply + --link {LINK} + --tag test + --fec rs + --speed 100g + --lane 0 + --main=-22 + --post1 5 + --tx-eq 1" + )) + .expect_err("Flags are exclusive"); +} diff --git a/swadm/tests/cli/main.rs b/swadm/tests/cli/main.rs new file mode 100644 index 00000000..722a07a6 --- /dev/null +++ b/swadm/tests/cli/main.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/ +// +// Copyright 2026 Oxide Computer Company + +mod cmd; +mod counters; +mod link_apply; +mod tx_eq; diff --git a/swadm/tests/cli/tx_eq.rs b/swadm/tests/cli/tx_eq.rs new file mode 100644 index 00000000..c2668b84 --- /dev/null +++ b/swadm/tests/cli/tx_eq.rs @@ -0,0 +1,93 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/ +// +// Copyright 2026 Oxide Computer Company + +//! This module tests CLI commands that CRUD +//! tx equalization settings on a link. + +use anyhow::Context; +use serial_test::serial; + +use crate::cmd; +use crate::cmd::re::{ANY, PARENS}; +use crate::pat; + +/// Sets some but not all tx eq taps on a port. +/// Unspecified taps default to zero. +// +// Aside: I would prefer different semantics, but +// docs/scripts in other repos expect this behavior. +#[test] +#[serial] +#[ignore] +fn set_partial_taps() -> anyhow::Result<()> { + let port = "rear0"; + let link = "rear0/0"; + + self::create_link(port, link).context("link setup failed")?; + + cmd::swadm(format!("link serdes set txeq {link} --main=-20"))?; + + let tx_eq = cmd::swadm(format!("link serdes get txeq {link}"))?; + for (label, val) in + [("pre2", 0), ("pre1", 0), ("main", -20), ("post1", 0), ("post2", 0)] + { + tx_eq.expect_line(pat![ + label, val, PARENS, val, PARENS, val, PARENS, val, PARENS + ])?; + } + + Ok(()) +} + +/// Sets tx eq when all taps on a link are declared. +#[test] +#[serial] +#[ignore] +fn set_all_taps() -> anyhow::Result<()> { + let port = "rear0"; + let link = "rear0/0"; + + self::create_link(port, link).context("link setup failed")?; + + cmd::swadm(format!( + "link serdes set tx-eq {link} --pre2=-1 --pre1 0 --main 10 --post1=5 --post2 2" + ))?; + + let tx_eq = cmd::swadm(format!("link serdes get txeq {link}"))?; + for (label, val) in + [("pre2", -1), ("pre1", 0), ("main", 10), ("post1", 5), ("post2", 2)] + { + tx_eq.expect_line(pat![ + label, val, PARENS, val, PARENS, val, PARENS, val, PARENS + ])?; + } + + Ok(()) +} + +/// Creates the new link and runs a few validations on it. +fn create_link(port: &str, link: &str) -> anyhow::Result<()> { + cmd::swadm("link ls")? + .expect_line(pat!["Port/Link", "Media", ANY]) + .context("Is swadm usable right now?")?; + + let delete_cmd = format!("link del {link}"); + if let Err(e) = cmd::swadm(&delete_cmd) { + println!( + "Delete failed. This can occur when the link doesn't exist: {e:?}" + ); + } + + cmd::swadm(format!("link create {port} -s 100g --fec rs"))? + .expect_line(pat![format!("Created link {link}")])?; + + cmd::swadm(format!("link enable {link}"))?; + + cmd::swadm(format!("link get {link} -v"))? + .expect_line(pat!["Speed", "100G"])?; + + Ok(()) +} diff --git a/swadm/tests/port-link.rs b/swadm/tests/port-link.rs deleted file mode 100644 index a9b3bb80..00000000 --- a/swadm/tests/port-link.rs +++ /dev/null @@ -1,254 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/ -// -// Copyright 2026 Oxide Computer Company - -//! Small integration test to verify that getting / setting properties either -//! via the port-based or link-based `swadm` APIs work as expected. -//! -//! This is a one-off test, and should be deleted once the port-v-link -//! conversion is merged. Note that the Dendrite server needs to be in a fresh -//! state for most of these tests to be valid. - -use std::net::IpAddr; -use std::process::Command; - -// Path to `swadm` executable. -const SWADM: &str = env!("CARGO_BIN_EXE_swadm"); - -// The name of the link we're operating on, in the new and old naming schemes -// respectively. -const LINK: &str = "rear0/0"; -const PORT: &str = "1:0"; - -fn swadm() -> Command { - Command::new(SWADM) -} - -#[derive(Debug)] -struct PropertyValue<'a> { - name: &'a str, - value: &'a str, -} - -#[derive(Debug)] -struct SetTest<'a> { - port: PropertyValue<'a>, - link: PropertyValue<'a>, -} - -impl SetTest<'_> { - fn run(self) { - // Check that properties fetched through link and port are the same, to - // start. - let port_val = get_port_prop(self.port.name); - let link_val = get_link_prop(self.link.name); - assert_eq!( - port_val, link_val, - "Property '{}'/'{}' differs between port and link schemes", - self.port.name, self.link.name, - ); - - // Check that we set the property via port, and fetch it via link. - set_port_prop(self.port.name, self.port.value); - let link_val = get_link_prop(self.link.name); - assert_eq!(self.port.value, link_val.trim()); - - // Check that we set the property via link, and fetch it via port. - set_link_prop(self.link.name, self.link.value); - let port_val = get_port_prop(self.port.name); - assert_eq!(self.link.value, port_val.trim()); - } -} - -fn get_link_prop(name: &str) -> String { - let link_val = swadm() - .arg("link") - .arg("get-prop") - .arg(LINK) - .arg(name) - .output() - .unwrap() - .stdout; - String::from_utf8(link_val).unwrap() -} - -fn get_port_prop(name: &str) -> String { - let port_val = swadm() - .arg("port") - .arg("get") - .arg(PORT) - .arg(name) - .output() - .unwrap() - .stdout; - String::from_utf8(port_val).unwrap() -} - -fn set_link_prop(name: &str, value: &str) { - swadm() - .arg("link") - .arg("set-prop") - .arg(LINK) - .arg(name) - .arg(value) - .output() - .unwrap(); -} - -fn set_port_prop(name: &str, value: &str) { - swadm() - .arg("port") - .arg("set") - .arg(PORT) - .arg(name) - .arg(value) - .output() - .unwrap(); -} - -#[test] -#[ignore] -fn test_mac() { - let test = SetTest { - port: PropertyValue { name: "mac", value: "a8:40:25:ff:ff:01" }, - link: PropertyValue { name: "mac", value: "a8:40:25:ff:ff:02" }, - }; - test.run(); -} - -#[test] -#[ignore] -fn test_an() { - let test = SetTest { - port: PropertyValue { name: "an", value: "true" }, - link: PropertyValue { name: "an", value: "false" }, - }; - test.run(); -} - -#[test] -#[ignore] -fn test_kr() { - let test = SetTest { - port: PropertyValue { name: "kr", value: "true" }, - link: PropertyValue { name: "kr", value: "false" }, - }; - test.run(); -} - -#[test] -#[ignore] -fn test_enable() { - let test = SetTest { - port: PropertyValue { name: "ena", value: "true" }, - link: PropertyValue { name: "ena", value: "false" }, - }; - test.run(); -} - -// Test getting/setting IP addresses on a port/link works correctly. -// -// This is a bit different, since there are multiple IP addresses on each link. -// Also, the port-based swadm API doesn't support operating on addresses; that's -// only available through `swadm addr`. -#[test] -#[ignore] -fn test_ip_addresses() { - let added_port_addrs: &[IpAddr] = - &["192.168.1.1".parse().unwrap(), "fd00::1".parse().unwrap()]; - let added_link_addrs: &[IpAddr] = - &["192.168.1.2".parse().unwrap(), "fd00::2".parse().unwrap()]; - - // Check that both schemes have the same addresses. - let port_addrs = String::from_utf8( - swadm().arg("addr").arg("list").arg(PORT).output().unwrap().stdout, - ) - .unwrap(); - let link_addrs = String::from_utf8( - swadm() - .arg("link") - .arg("get-prop") - .arg(LINK) - .arg("ip") - .output() - .unwrap() - .stdout, - ) - .unwrap(); - assert_eq!(port_addrs, link_addrs); - - // Add the IP addresses via the port scheme. Verify we get them back, and - // that they're also listed in the link scheme. - for addr in added_port_addrs.iter() { - swadm() - .arg("addr") - .arg("add") - .arg(PORT) - .arg(addr.to_string()) - .output() - .unwrap(); - } - let port_addrs: Vec = String::from_utf8( - swadm().arg("addr").arg("list").arg(PORT).output().unwrap().stdout, - ) - .unwrap() - .lines() - .map(|line| line.parse().unwrap()) - .collect(); - let link_addrs: Vec = String::from_utf8( - swadm() - .arg("link") - .arg("get-prop") - .arg(LINK) - .arg("ip") - .output() - .unwrap() - .stdout, - ) - .unwrap() - .lines() - .map(|line| line.parse().unwrap()) - .collect(); - assert_eq!(port_addrs, link_addrs); - assert_eq!(port_addrs, added_port_addrs); - - // Add the IP addresses via the link scheme. Verify we get them back, and - // that they're also listed in the port scheme. - for addr in added_link_addrs.iter() { - swadm() - .arg("link") - .arg("set-prop") - .arg(LINK) - .arg("ip") - .arg(addr.to_string()) - .output() - .unwrap(); - } - let port_addrs: Vec = String::from_utf8( - swadm().arg("addr").arg("list").arg(PORT).output().unwrap().stdout, - ) - .unwrap() - .lines() - .map(|line| line.parse().unwrap()) - .collect(); - let link_addrs: Vec = String::from_utf8( - swadm() - .arg("link") - .arg("get-prop") - .arg(LINK) - .arg("ip") - .output() - .unwrap() - .stdout, - ) - .unwrap() - .lines() - .map(|line| line.parse().unwrap()) - .collect(); - assert_eq!(port_addrs, link_addrs); - let mut all_addrs = [added_port_addrs, added_link_addrs].concat(); - all_addrs.sort(); - assert_eq!(port_addrs, all_addrs); -}