Skip to content

Commit 6b0bbee

Browse files
authored
test(dhcp,dhcp-server): table-ify the shared vendor_class predicate tests (#2742)
1 parent dc896e2 commit 6b0bbee

5 files changed

Lines changed: 330 additions & 126 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/dhcp-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ futures = { workspace = true }
6060

6161
[dev-dependencies]
6262
tempfile = { workspace = true }
63+
carbide-test-support = { path = "../test-support" }
6364

6465
[build-dependencies]
6566
tonic-prost-build = "0.14"

crates/dhcp-server/src/vendor_class.rs

Lines changed: 163 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ impl VendorClass {
153153

154154
#[cfg(test)]
155155
mod tests {
156+
use carbide_test_support::Outcome::*;
157+
use carbide_test_support::{Case, check_cases, value_scenarios};
158+
156159
use super::*;
157160

158161
impl VendorClass {
@@ -169,86 +172,183 @@ mod tests {
169172
}
170173
}
171174

172-
#[test]
173-
fn it_is_pxe_capable() {
174-
let vc: VendorClass = "PXEClient:Arch:00007:UNDI:003000".parse().unwrap();
175-
assert!(!vc.is_netboot());
176-
177-
let vc: VendorClass = "iDRAC".parse().unwrap();
178-
assert!(!vc.is_netboot());
179-
180-
let vc: VendorClass = "PXEClient".parse().unwrap();
181-
assert!(!vc.is_netboot());
182-
}
183-
184-
#[test]
185-
fn is_it_arm_non_uefi() {
186-
let vc: VendorClass = "nvidia-bluefield-dpu aarch64".parse().unwrap();
187-
assert!(vc.arm());
188-
189-
let vc: VendorClass = "BF2Client".parse().unwrap();
190-
assert!(vc.arm());
175+
/// The full boot-relevant classification a parsed vendor class resolves to:
176+
/// architecture (`arm`/`x64`), whether it asks for HTTP `netboot`, and whether
177+
/// it is a `modern` (netboot ARM-UEFI) client.
178+
#[derive(Debug, PartialEq)]
179+
struct Classification {
180+
arm: bool,
181+
x64: bool,
182+
netboot: bool,
183+
modern: bool,
191184
}
192185

193-
#[test]
194-
fn is_it_arm() {
195-
let vc: VendorClass = "PXEClient:Arch:00011:UNDI:003000".parse().unwrap();
196-
assert!(vc.arm());
197-
}
198-
199-
#[test]
200-
fn is_it_not_modern() {
201-
let vc: VendorClass = "PXEClient:Arch:00007:UNDI:003000".parse().unwrap();
202-
assert!(!vc.is_it_modern());
186+
impl Classification {
187+
fn of(vc: &VendorClass) -> Self {
188+
Self {
189+
arm: vc.arm(),
190+
x64: vc.x64(),
191+
netboot: vc.is_netboot(),
192+
modern: vc.is_it_modern(),
193+
}
194+
}
203195
}
204196

197+
/// Every vendor-class string we recognize, pinned to its full classification.
198+
/// One row per input -- a `PXEClient` x64 UEFI, an OS-form `aarch64` DPU, an
199+
/// `HTTPClient` netboot, a bare BMC id -- so a parse covers all four predicates
200+
/// at once instead of one assertion apiece.
205201
#[test]
206-
fn is_it_modern() {
207-
let vc: VendorClass = "HTTPClient:Arch:00011:UNDI:003000".parse().unwrap();
208-
assert!(vc.is_it_modern());
202+
fn classifies_each_vendor_class() {
203+
check_cases(
204+
[
205+
Case {
206+
scenario: "x64 UEFI PXEClient",
207+
input: "PXEClient:Arch:00007:UNDI:003000",
208+
expect: Yields(Classification {
209+
arm: false,
210+
x64: true,
211+
netboot: false,
212+
modern: false,
213+
}),
214+
},
215+
Case {
216+
scenario: "Dell iDRAC BMC (x64)",
217+
input: "iDRAC",
218+
expect: Yields(Classification {
219+
arm: false,
220+
x64: true,
221+
netboot: false,
222+
modern: false,
223+
}),
224+
},
225+
Case {
226+
scenario: "bare PXEClient iPXE response (arm)",
227+
input: "PXEClient",
228+
expect: Yields(Classification {
229+
arm: true,
230+
x64: false,
231+
netboot: false,
232+
modern: false,
233+
}),
234+
},
235+
Case {
236+
scenario: "OS-form aarch64 DPU",
237+
input: "nvidia-bluefield-dpu aarch64",
238+
expect: Yields(Classification {
239+
arm: true,
240+
x64: false,
241+
netboot: false,
242+
modern: false,
243+
}),
244+
},
245+
Case {
246+
scenario: "legacy BF2 card",
247+
input: "BF2Client",
248+
expect: Yields(Classification {
249+
arm: true,
250+
x64: false,
251+
netboot: false,
252+
modern: false,
253+
}),
254+
},
255+
Case {
256+
scenario: "ARM UEFI PXEClient (not netboot)",
257+
input: "PXEClient:Arch:00011:UNDI:003000",
258+
expect: Yields(Classification {
259+
arm: true,
260+
x64: false,
261+
netboot: false,
262+
modern: false,
263+
}),
264+
},
265+
Case {
266+
scenario: "ARM UEFI HTTPClient is modern netboot",
267+
input: "HTTPClient:Arch:00011:UNDI:003000",
268+
expect: Yields(Classification {
269+
arm: true,
270+
x64: false,
271+
netboot: true,
272+
modern: true,
273+
}),
274+
},
275+
Case {
276+
scenario: "x64 HTTPClient netboots but is not modern",
277+
input: "HTTPClient:Arch:00016:UNDI:003001",
278+
expect: Yields(Classification {
279+
arm: false,
280+
x64: true,
281+
netboot: true,
282+
modern: false,
283+
}),
284+
},
285+
],
286+
|input| {
287+
input
288+
.parse::<VendorClass>()
289+
.map(|vc| Classification::of(&vc))
290+
.map_err(|_| "parse failed")
291+
},
292+
);
209293
}
210294

295+
/// The human-readable `VendorClass` `Display`: `"<arch> (<netboot|basic>)"`.
211296
#[test]
212-
fn it_is_netboot_capable() {
213-
let vc: VendorClass = "HTTPClient:Arch:00016:UNDI:003001".parse().unwrap();
214-
assert!(vc.is_netboot());
215-
}
297+
fn formats_vendor_class_display() {
298+
value_scenarios!(run = |input: &str| input.parse::<VendorClass>().unwrap().to_string();
299+
"basic (non-netboot) clients" {
300+
"NothingClient:Arch:00011:UNDI:X" => "ARM 64-bit UEFI (basic)".to_string(),
301+
"NVIDIA/BF/OOB" => "ARM 64-bit UEFI (basic)".to_string(),
302+
"NVIDIA/BF/BMC" => "ARM 64-bit UEFI (basic)".to_string(),
303+
"PXEClient:Arch:00000:UNDI:003000" => "x86 BIOS (basic)".to_string(),
304+
}
216305

217-
#[test]
218-
fn it_is_netboot_and_not_arm() {
219-
let vc: VendorClass = "HTTPClient:Arch:00016:UNDI:003001".parse().unwrap();
220-
assert!(vc.is_netboot());
221-
assert!(vc.x64());
306+
"netboot clients" {
307+
"HTTPClient:Arch:00011:UNDI:003000" => "ARM 64-bit UEFI (netboot)".to_string(),
308+
}
309+
);
222310
}
223311

312+
/// `MachineArchitecture::from_str` maps DHCP option-93 architecture codes (base
313+
/// 10, as the long vendor class spells them) to an architecture. It never errors
314+
/// -- an unrecognized or non-numeric code resolves to `Unknown` so we still vend
315+
/// an address. Exercised directly here rather than only through `VendorClass`.
224316
#[test]
225-
fn it_handles_basic_for_all_clients() {
226-
let vc: Result<VendorClass, VendorClassParseError> =
227-
"NothingClient:Arch:00011:UNDI:X".parse();
228-
assert_eq!(vc.unwrap().to_string(), "ARM 64-bit UEFI (basic)");
229-
}
317+
fn maps_architecture_codes() {
318+
value_scenarios!(run = |input: &str| MachineArchitecture::from_str(input).unwrap();
319+
"x86 BIOS" {
320+
"00000" => MachineArchitecture::BiosX86,
321+
}
230322

231-
#[test]
232-
fn it_formats_the_parser_armuefi_netboot() {
233-
let vc: VendorClass = "HTTPClient:Arch:00011:UNDI:003000".parse().unwrap();
234-
assert_eq!(vc.to_string(), "ARM 64-bit UEFI (netboot)");
235-
}
323+
"x64 UEFI" {
324+
"00007" => MachineArchitecture::EfiX64,
325+
"00016" => MachineArchitecture::EfiX64,
326+
}
236327

237-
#[test]
238-
fn it_detects_nvidia_bf_oob_as_arm() {
239-
let vc: VendorClass = "NVIDIA/BF/OOB".parse().unwrap();
240-
assert_eq!(vc.to_string(), "ARM 64-bit UEFI (basic)");
241-
}
328+
"ARM 64-bit" {
329+
"00011" => MachineArchitecture::Arm64,
330+
"00019" => MachineArchitecture::Arm64,
331+
"aarch64" => MachineArchitecture::Arm64,
332+
}
242333

243-
#[test]
244-
fn it_detects_nvidia_bf_bmc_as_arm() {
245-
let vc: VendorClass = "NVIDIA/BF/BMC".parse().unwrap();
246-
assert_eq!(vc.to_string(), "ARM 64-bit UEFI (basic)");
334+
"unknown" {
335+
"00042" => MachineArchitecture::Unknown,
336+
"not-a-number" => MachineArchitecture::Unknown,
337+
}
338+
);
247339
}
248340

341+
/// The `MachineArchitecture` `Display` strings, which the `VendorClass` `Display`
342+
/// is built from.
249343
#[test]
250-
fn it_formats_the_parser_legacypxe() {
251-
let vc: VendorClass = "PXEClient:Arch:00000:UNDI:003000".parse().unwrap();
252-
assert_eq!(vc.to_string(), "x86 BIOS (basic)");
344+
fn formats_architecture_display() {
345+
value_scenarios!(run = |arch: MachineArchitecture| arch.to_string();
346+
"architecture labels" {
347+
MachineArchitecture::Arm64 => "ARM 64-bit UEFI".to_string(),
348+
MachineArchitecture::EfiX64 => "x64 UEFI".to_string(),
349+
MachineArchitecture::BiosX86 => "x86 BIOS".to_string(),
350+
MachineArchitecture::Unknown => "Unknown".to_string(),
351+
}
352+
);
253353
}
254354
}

crates/dhcp/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ serde_json = { workspace = true }
4141
test-cdylib = { workspace = true }
4242
tempfile = { workspace = true }
4343
carbide-rpc = { path = "../rpc" }
44+
carbide-test-support = { path = "../test-support" }
4445
prometheus = { workspace = true }
4546

4647
[build-dependencies]

0 commit comments

Comments
 (0)