1addr_ty!(
2 #[doc(alias = "Eui48Addr")]
4 MacAddr[6]
5);
6
7#[cfg(test)]
8mod tests {
9 use super::*;
10 use crate::{ParseError, TestCase};
11
12 use std::{string::ToString, vec, vec::Vec};
13
14 const MAC_ADDRESS_SIZE: usize = 6;
15
16 fn test_cases() -> Vec<TestCase<MAC_ADDRESS_SIZE>> {
17 vec![
18 TestCase {
20 input: "00:00:5e:00:53:01",
21 output: Some(vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x01]),
22 err: None,
23 },
24 TestCase {
25 input: "00-00-5e-00-53-01",
26 output: Some(vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x01]),
27 err: None,
28 },
29 TestCase {
30 input: "0000.5e00.5301",
31 output: Some(vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x01]),
32 err: None,
33 },
34 TestCase {
35 input: "ab:cd:ef:AB:CD:EF",
36 output: Some(vec![0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef]),
37 err: None,
38 },
39 TestCase {
41 input: "01.02.03.04.05.06",
42 output: None,
43 err: Some(ParseError::InvalidSeparator(b'.')),
44 },
45 TestCase {
46 input: "01:02:03:04:05:06:",
47 output: None,
48 err: Some(ParseError::InvalidLength(18)),
49 },
50 TestCase {
51 input: "x1:02:03:04:05:06",
52 output: None,
53 err: Some(ParseError::InvalidHexDigit([b'x', b'1'])),
54 },
55 TestCase {
56 input: "01-02:03:04:05:06",
57 output: None,
58 err: Some(ParseError::UnexpectedSeparator {
59 expected: b'-',
60 actual: b':',
61 }),
62 },
63 ]
64 }
65
66 #[test]
67 fn parse() {
68 let cases = test_cases();
69 for (i, test) in cases.iter().enumerate() {
70 let result = MacAddr::try_from(test.input);
71
72 match (result, &test.output) {
73 (Ok(out), Some(expected)) => {
74 assert_eq!(
75 expected.as_slice(),
76 out,
77 "Test case {}: MacAddr::parse({}) output mismatch",
78 i,
79 test.input
80 );
81
82 if test.err.is_none() {
84 let formatted = out.to_string();
85 let round_trip = MacAddr::try_from(formatted.as_str());
86 assert!(
87 round_trip.is_ok(),
88 "Test case {}: Round-trip parse failed for {}",
89 i,
90 formatted
91 );
92 assert_eq!(
93 round_trip.unwrap(),
94 out,
95 "Test case {}: Round-trip value mismatch",
96 i
97 );
98 }
99 }
100 (Err(err), None) => {
101 assert_eq!(
102 Some(&err),
103 test.err.as_ref(),
104 "Test case {}: Expected error containing '{:?}', got '{:?}'",
105 i,
106 test.err,
107 err
108 );
109 }
110 (Ok(out), None) => {
111 panic!(
112 "Test case {}: Expected error '{:?}', got success: {:?}",
113 i, test.err, out
114 );
115 }
116 (Err(err), Some(expected)) => {
117 panic!(
118 "Test case {}: Expected {:?}, got error: {:?}",
119 i, expected, err
120 );
121 }
122 }
123 }
124 }
125
126 #[test]
127 fn test_default() {
128 let addr = MacAddr::default();
129 assert_eq!(addr.octets(), [0, 0, 0, 0, 0, 0]);
130 }
131
132 #[test]
133 fn formatted() {
134 let addr = MacAddr::try_from("00:00:5e:00:53:01").unwrap();
135 assert_eq!(addr.to_string(), "00:00:5e:00:53:01");
136 assert_eq!(addr.to_colon_separated(), "00:00:5e:00:53:01");
137
138 let dot = addr.to_dot_separated_array();
139 let dot_str = core::str::from_utf8(&dot).unwrap();
140 assert_eq!(dot_str, "0000.5e00.5301");
141 assert_eq!(addr.to_dot_separated(), "0000.5e00.5301");
142
143 let dashed = addr.to_hyphen_separated_array();
144 let dashed_str = core::str::from_utf8(&dashed).unwrap();
145 assert_eq!(dashed_str, "00-00-5e-00-53-01");
146 assert_eq!(addr.to_hyphen_separated(), "00-00-5e-00-53-01");
147 }
148
149 #[cfg(feature = "serde")]
150 #[test]
151 fn serde_human_readable() {
152 let addr = MacAddr::try_from("00:00:5e:00:53:01").unwrap();
153 let json = serde_json::to_string(&addr).unwrap();
154 assert_eq!(json, "\"00:00:5e:00:53:01\"");
155
156 let addr2: MacAddr = serde_json::from_str(&json).unwrap();
157 assert_eq!(addr, addr2);
158 }
159
160 #[cfg(feature = "serde")]
161 #[test]
162 fn serde_human_unreadable() {
163 let addr = MacAddr::try_from("00:00:5e:00:53:01").unwrap();
164 let json = bincode::serde::encode_to_vec(addr, bincode::config::standard()).unwrap();
165 assert_eq!(json, [0, 0, 94, 0, 83, 1]);
166 assert_eq!(addr.octets(), [0, 0, 94, 0, 83, 1]);
167
168 let addr2: MacAddr = bincode::serde::decode_from_slice(&json, bincode::config::standard())
169 .unwrap()
170 .0;
171 assert_eq!(addr, addr2);
172
173 let addr3 = MacAddr::from_raw([0, 0, 94, 0, 83, 1]);
174 assert_eq!(addr, addr3);
175
176 println!("{:?}", addr);
177 }
178
179 #[cfg(feature = "arbitrary")]
184 #[test]
185 fn arbitrary_is_deterministic() {
186 use arbitrary::{Arbitrary, Unstructured};
187
188 let data = [0xAA; 32];
191 let a = MacAddr::arbitrary(&mut Unstructured::new(&data)).expect("arbitrary should succeed");
192 let b = MacAddr::arbitrary(&mut Unstructured::new(&data)).expect("arbitrary should succeed");
193 assert_eq!(a, b, "arbitrary should be deterministic for a fixed input");
194 }
195
196 #[cfg(feature = "arbitrary")]
197 #[test]
198 fn arbitrary_size_hint_matches_byte_array() {
199 use arbitrary::Arbitrary;
200
201 let hint = MacAddr::size_hint(0);
204 let expected = <[u8; MAC_ADDRESS_SIZE] as Arbitrary>::size_hint(0);
205 assert_eq!(hint, expected);
206 }
207
208 #[cfg(feature = "arbitrary")]
209 #[test]
210 fn arbitrary_consumes_expected_bytes() {
211 use arbitrary::{Arbitrary, Unstructured};
212
213 let data = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
216 let mut u = Unstructured::new(&data);
217 let _first = MacAddr::arbitrary(&mut u).unwrap();
218 let _second = MacAddr::arbitrary(&mut u).unwrap();
219 }
223
224 #[cfg(feature = "quickcheck")]
225 #[test]
226 fn quickcheck_arbitrary_roundtrips_through_string() {
227 use quickcheck::{Arbitrary, Gen};
228
229 let mut g = Gen::new(32);
230 for _ in 0..128 {
231 let addr = MacAddr::arbitrary(&mut g);
232 let parsed =
233 MacAddr::try_from(addr.to_string().as_str()).expect("to_string() output must parse back");
234 assert_eq!(addr, parsed);
235 }
236 }
237
238 #[cfg(feature = "quickcheck")]
239 #[test]
240 fn quickcheck_shrink_terminates_and_preserves_length() {
241 use quickcheck::Arbitrary;
242
243 let addr = MacAddr::from_raw([0xFF; MAC_ADDRESS_SIZE]);
244 let shrinks: Vec<_> = addr.shrink().take(4096).collect();
247 assert!(!shrinks.is_empty(), "non-zero address should yield shrinks");
248 for s in &shrinks {
252 assert_eq!(s.octets().len(), MAC_ADDRESS_SIZE);
253 }
254 }
255
256 #[cfg(feature = "quickcheck")]
257 #[test]
258 fn quickcheck_shrink_zero_is_empty() {
259 use quickcheck::Arbitrary;
260
261 let zero = MacAddr::from_raw([0; MAC_ADDRESS_SIZE]);
265 let shrinks: Vec<_> = zero.shrink().collect();
266 assert!(
267 shrinks.is_empty(),
268 "zero address should yield no shrinks, got {:?}",
269 shrinks
270 );
271 }
272
273 #[cfg(feature = "quickcheck")]
274 #[test]
275 fn quickcheck_roundtrip_property() {
276 fn prop(addr: MacAddr) -> bool {
282 MacAddr::try_from(addr.to_string().as_str())
283 .map(|p| p == addr)
284 .unwrap_or(false)
285 }
286 quickcheck::quickcheck(prop as fn(MacAddr) -> bool);
287 }
288}