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