Source file
src/net/netip/netip_test.go
1
2
3
4
5 package netip_test
6
7 import (
8 "bytes"
9 "encoding/json"
10 "flag"
11 "fmt"
12 "internal/testenv"
13 "net"
14 . "net/netip"
15 "reflect"
16 "slices"
17 "strings"
18 "testing"
19 "unique"
20 )
21
22 var long = flag.Bool("long", false, "run long tests")
23
24 type uint128 = Uint128
25
26 var (
27 mustPrefix = MustParsePrefix
28 mustIP = MustParseAddr
29 mustIPPort = MustParseAddrPort
30 )
31
32 func TestParseAddr(t *testing.T) {
33 var validIPs = []struct {
34 in string
35 ip Addr
36 str string
37 wantErr string
38 }{
39
40 {
41 in: "0.0.0.0",
42 ip: MkAddr(Mk128(0, 0xffff00000000), Z4),
43 },
44
45 {
46 in: "192.168.140.255",
47 ip: MkAddr(Mk128(0, 0xffffc0a88cff), Z4),
48 },
49
50 {
51 in: "010.000.015.001",
52 wantErr: `ParseAddr("010.000.015.001"): IPv4 field has octet with leading zero`,
53 },
54
55 {
56 in: "000001.00000002.00000003.000000004",
57 wantErr: `ParseAddr("000001.00000002.00000003.000000004"): IPv4 field has octet with leading zero`,
58 },
59
60 {
61 in: "::ffff:1.2.03.4",
62 wantErr: `ParseAddr("::ffff:1.2.03.4"): IPv4 field has octet with leading zero`,
63 },
64
65 {
66 in: "::ffff:1.2.3.z",
67 wantErr: `ParseAddr("::ffff:1.2.3.z"): unexpected character (at "z")`,
68 },
69
70 {
71 in: "::",
72 ip: MkAddr(Mk128(0, 0), Z6noz),
73 },
74
75 {
76 in: "::1",
77 ip: MkAddr(Mk128(0, 1), Z6noz),
78 },
79
80 {
81 in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b",
82 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), Z6noz),
83 },
84
85 {
86 in: "fd7a:115c::626b:430b",
87 ip: MkAddr(Mk128(0xfd7a115c00000000, 0x00000000626b430b), Z6noz),
88 },
89
90 {
91 in: "fd7a:115c:a1e0:ab12:4843:cd96::",
92 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd9600000000), Z6noz),
93 },
94
95 {
96 in: "fd7a:115c:a1e0:ab12:4843:cd96:626b::",
97 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b0000), Z6noz),
98 str: "fd7a:115c:a1e0:ab12:4843:cd96:626b:0",
99 },
100
101 {
102 in: "fd7a:115c:a1e0::4843:cd96:626b:430b",
103 ip: MkAddr(Mk128(0xfd7a115ca1e00000, 0x4843cd96626b430b), Z6noz),
104 str: "fd7a:115c:a1e0:0:4843:cd96:626b:430b",
105 },
106
107 {
108 in: "::ffff:192.168.140.255",
109 ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), Z6noz),
110 str: "::ffff:192.168.140.255",
111 },
112
113 {
114 in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0",
115 ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), unique.Make(MakeAddrDetail(true, "eth0"))),
116 },
117
118 {
119 in: "1:2::ffff:192.168.140.255%eth1",
120 ip: MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
121 str: "1:2::ffff:c0a8:8cff%eth1",
122 },
123
124 {
125 in: "::ffff:192.168.140.255%eth1",
126 ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
127 str: "::ffff:192.168.140.255%eth1",
128 },
129
130 {
131 in: "FD9E:1A04:F01D::1",
132 ip: MkAddr(Mk128(0xfd9e1a04f01d0000, 0x1), Z6noz),
133 str: "fd9e:1a04:f01d::1",
134 },
135 }
136
137 for _, test := range validIPs {
138 t.Run(test.in, func(t *testing.T) {
139 got, err := ParseAddr(test.in)
140 if err != nil {
141 if err.Error() == test.wantErr {
142 return
143 }
144 t.Fatal(err)
145 }
146 if test.wantErr != "" {
147 t.Fatalf("wanted error %q; got none", test.wantErr)
148 }
149 if got != test.ip {
150 t.Errorf("got %#v, want %#v", got, test.ip)
151 }
152
153
154 got2, err := ParseAddr(test.in)
155 if err != nil {
156 t.Fatal(err)
157 }
158 if got != got2 {
159 t.Errorf("ParseAddr(%q) got 2 different results: %#v, %#v", test.in, got, got2)
160 }
161
162
163 s := got.String()
164 got3, err := ParseAddr(s)
165 if err != nil {
166 t.Fatal(err)
167 }
168 if got != got3 {
169 t.Errorf("ParseAddr(%q) != ParseAddr(ParseIP(%q).String()). Got %#v, want %#v", test.in, test.in, got3, got)
170 }
171
172
173 slow, err := parseIPSlow(test.in)
174 if err != nil {
175 t.Fatal(err)
176 }
177 if got != slow {
178 t.Errorf("ParseAddr(%q) = %#v, parseIPSlow(%q) = %#v", test.in, got, test.in, slow)
179 }
180
181
182 s = got.String()
183 wants := test.str
184 if wants == "" {
185 wants = test.in
186 }
187 if s != wants {
188 t.Errorf("ParseAddr(%q).String() got %q, want %q", test.in, s, wants)
189 }
190
191
192 TestAppendToMarshal(t, got)
193
194
195
196
197
198 js := `"` + test.in + `"`
199 var jsgot Addr
200 if err := json.Unmarshal([]byte(js), &jsgot); err != nil {
201 t.Fatal(err)
202 }
203 if jsgot != got {
204 t.Errorf("json.Unmarshal(%q) = %#v, want %#v", test.in, jsgot, got)
205 }
206 jsb, err := json.Marshal(jsgot)
207 if err != nil {
208 t.Fatal(err)
209 }
210 jswant := `"` + wants + `"`
211 jsback := string(jsb)
212 if jsback != jswant {
213 t.Errorf("Marshal(Unmarshal(%q)) = %s, want %s", test.in, jsback, jswant)
214 }
215 })
216 }
217
218 var invalidIPs = []string{
219
220 "",
221
222 "bad",
223
224
225 "1234",
226
227 "1.2.3.4%eth0",
228
229 ".1.2.3",
230 "1.2.3.",
231 "1..2.3",
232
233 "1.2.3.4.5",
234
235 "0300.0250.0214.0377",
236
237 "0xc0.0xa8.0x8c.0xff",
238
239 "192.168.12345",
240
241
242 "127.0.1",
243
244 "192.1234567",
245
246
247 "127.1",
248
249 "192.168.300.1",
250
251 "192.168.0.1.5.6",
252
253 "1:2:3:4:5:6:7",
254
255 "1:2:3:4:5:6:7:8:9",
256
257 "1:2:3:4::5:6:7:8",
258
259 "fe801::1",
260
261 "fe80:tail:scal:e::",
262
263 "fe80::1%",
264
265 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
266
267 "ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
268
269 "::ffff:192.168.140.bad",
270
271 "fe80::1::1",
272
273 "fe80:1?:1",
274
275 "fe80:",
276
277 "0:0:0:0:0:ffff:0:00000",
278
279 "0:0:0:0:00000:ffff:127.1.2.3",
280 }
281
282 for _, s := range invalidIPs {
283 t.Run(s, func(t *testing.T) {
284 got, err := ParseAddr(s)
285 if err == nil {
286 t.Errorf("ParseAddr(%q) = %#v, want error", s, got)
287 }
288
289 slow, err := parseIPSlow(s)
290 if err == nil {
291 t.Errorf("parseIPSlow(%q) = %#v, want error", s, slow)
292 }
293
294 std := net.ParseIP(s)
295 if std != nil {
296 t.Errorf("net.ParseIP(%q) = %#v, want error", s, std)
297 }
298
299 if s == "" {
300
301
302 return
303 }
304 var jsgot Addr
305 js := []byte(`"` + s + `"`)
306 if err := json.Unmarshal(js, &jsgot); err == nil {
307 t.Errorf("json.Unmarshal(%q) = %#v, want error", s, jsgot)
308 }
309 })
310 }
311 }
312
313 func TestAddrFromSlice(t *testing.T) {
314 tests := []struct {
315 ip []byte
316 wantAddr Addr
317 wantOK bool
318 }{
319 {
320 ip: []byte{10, 0, 0, 1},
321 wantAddr: mustIP("10.0.0.1"),
322 wantOK: true,
323 },
324 {
325 ip: []byte{0xfe, 0x80, 15: 0x01},
326 wantAddr: mustIP("fe80::01"),
327 wantOK: true,
328 },
329 {
330 ip: []byte{0, 1, 2},
331 wantAddr: Addr{},
332 wantOK: false,
333 },
334 {
335 ip: nil,
336 wantAddr: Addr{},
337 wantOK: false,
338 },
339 }
340 for _, tt := range tests {
341 addr, ok := AddrFromSlice(tt.ip)
342 if ok != tt.wantOK || addr != tt.wantAddr {
343 t.Errorf("AddrFromSlice(%#v) = %#v, %v, want %#v, %v", tt.ip, addr, ok, tt.wantAddr, tt.wantOK)
344 }
345 }
346 }
347
348 func TestIPv4Constructors(t *testing.T) {
349 if AddrFrom4([4]byte{1, 2, 3, 4}) != MustParseAddr("1.2.3.4") {
350 t.Errorf("don't match")
351 }
352 }
353
354 func TestAddrMarshalUnmarshalBinary(t *testing.T) {
355 tests := []struct {
356 ip string
357 wantSize int
358 }{
359 {"", 0},
360 {"1.2.3.4", 4},
361 {"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", 16},
362 {"::ffff:c000:0280", 16},
363 {"::ffff:c000:0280%eth0", 20},
364 }
365 for _, tc := range tests {
366 var ip Addr
367 if len(tc.ip) > 0 {
368 ip = mustIP(tc.ip)
369 }
370 b, err := ip.MarshalBinary()
371 if err != nil {
372 t.Fatal(err)
373 }
374 if len(b) != tc.wantSize {
375 t.Fatalf("%q encoded to size %d; want %d", tc.ip, len(b), tc.wantSize)
376 }
377 var ip2 Addr
378 if err := ip2.UnmarshalBinary(b); err != nil {
379 t.Fatal(err)
380 }
381 if ip != ip2 {
382 t.Fatalf("got %v; want %v", ip2, ip)
383 }
384 }
385
386
387 for _, n := range []int{3, 5} {
388 var ip2 Addr
389 if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
390 t.Fatalf("unmarshaled from unexpected IP length %d", n)
391 }
392 }
393 }
394
395 func TestAddrPortMarshalTextString(t *testing.T) {
396 tests := []struct {
397 in AddrPort
398 want string
399 }{
400 {mustIPPort("1.2.3.4:80"), "1.2.3.4:80"},
401 {mustIPPort("[::]:80"), "[::]:80"},
402 {mustIPPort("[1::CAFE]:80"), "[1::cafe]:80"},
403 {mustIPPort("[1::CAFE%en0]:80"), "[1::cafe%en0]:80"},
404 {mustIPPort("[::FFFF:192.168.140.255]:80"), "[::ffff:192.168.140.255]:80"},
405 {mustIPPort("[::FFFF:192.168.140.255%en0]:80"), "[::ffff:192.168.140.255%en0]:80"},
406 }
407 for i, tt := range tests {
408 if got := tt.in.String(); got != tt.want {
409 t.Errorf("%d. for (%v, %v) String = %q; want %q", i, tt.in.Addr(), tt.in.Port(), got, tt.want)
410 }
411 mt, err := tt.in.MarshalText()
412 if err != nil {
413 t.Errorf("%d. for (%v, %v) MarshalText error: %v", i, tt.in.Addr(), tt.in.Port(), err)
414 continue
415 }
416 if string(mt) != tt.want {
417 t.Errorf("%d. for (%v, %v) MarshalText = %q; want %q", i, tt.in.Addr(), tt.in.Port(), mt, tt.want)
418 }
419 }
420 }
421
422 func TestAddrPortMarshalUnmarshalBinary(t *testing.T) {
423 tests := []struct {
424 ipport string
425 wantSize int
426 }{
427 {"1.2.3.4:51820", 4 + 2},
428 {"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", 16 + 2},
429 {"[::ffff:c000:0280]:65535", 16 + 2},
430 {"[::ffff:c000:0280%eth0]:1", 20 + 2},
431 }
432 for _, tc := range tests {
433 var ipport AddrPort
434 if len(tc.ipport) > 0 {
435 ipport = mustIPPort(tc.ipport)
436 }
437 b, err := ipport.MarshalBinary()
438 if err != nil {
439 t.Fatal(err)
440 }
441 if len(b) != tc.wantSize {
442 t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(b), tc.wantSize)
443 }
444 var ipport2 AddrPort
445 if err := ipport2.UnmarshalBinary(b); err != nil {
446 t.Fatal(err)
447 }
448 if ipport != ipport2 {
449 t.Fatalf("got %v; want %v", ipport2, ipport)
450 }
451 }
452
453
454 for _, n := range []int{3, 7} {
455 var ipport2 AddrPort
456 if err := ipport2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
457 t.Fatalf("unmarshaled from unexpected length %d", n)
458 }
459 }
460 }
461
462 func TestPrefixMarshalTextString(t *testing.T) {
463 tests := []struct {
464 in Prefix
465 want string
466 }{
467 {mustPrefix("1.2.3.4/24"), "1.2.3.4/24"},
468 {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"},
469 {mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"},
470 {mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"},
471 {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"},
472 }
473 for i, tt := range tests {
474 if got := tt.in.String(); got != tt.want {
475 t.Errorf("%d. for %v String = %q; want %q", i, tt.in, got, tt.want)
476 }
477 mt, err := tt.in.MarshalText()
478 if err != nil {
479 t.Errorf("%d. for %v MarshalText error: %v", i, tt.in, err)
480 continue
481 }
482 if string(mt) != tt.want {
483 t.Errorf("%d. for %v MarshalText = %q; want %q", i, tt.in, mt, tt.want)
484 }
485 }
486 }
487
488 func TestPrefixMarshalUnmarshalBinary(t *testing.T) {
489 type testCase struct {
490 prefix Prefix
491 wantSize int
492 }
493 tests := []testCase{
494 {mustPrefix("1.2.3.4/24"), 4 + 1},
495 {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1},
496 {mustPrefix("::ffff:c000:0280/96"), 16 + 1},
497 {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1},
498 }
499 tests = append(tests,
500 testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize},
501 testCase{PrefixFrom(tests[1].prefix.Addr(), 129), tests[1].wantSize})
502 for _, tc := range tests {
503 prefix := tc.prefix
504 b, err := prefix.MarshalBinary()
505 if err != nil {
506 t.Fatal(err)
507 }
508 if len(b) != tc.wantSize {
509 t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(b), tc.wantSize)
510 }
511 var prefix2 Prefix
512 if err := prefix2.UnmarshalBinary(b); err != nil {
513 t.Fatal(err)
514 }
515 if prefix != prefix2 {
516 t.Fatalf("got %v; want %v", prefix2, prefix)
517 }
518 }
519
520
521 for _, n := range []int{3, 6} {
522 var prefix2 Prefix
523 if err := prefix2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
524 t.Fatalf("unmarshaled from unexpected length %d", n)
525 }
526 }
527 }
528
529 func TestAddrMarshalUnmarshal(t *testing.T) {
530
531
532
533 orig := `""`
534 var ip Addr
535 if err := json.Unmarshal([]byte(orig), &ip); err != nil {
536 t.Fatalf("Unmarshal(%q) got error %v", orig, err)
537 }
538 if ip != (Addr{}) {
539 t.Errorf("Unmarshal(%q) is not the zero Addr", orig)
540 }
541
542 jsb, err := json.Marshal(ip)
543 if err != nil {
544 t.Fatalf("Marshal(%v) got error %v", ip, err)
545 }
546 back := string(jsb)
547 if back != orig {
548 t.Errorf("Marshal(Unmarshal(%q)) got %q, want %q", orig, back, orig)
549 }
550 }
551
552 func TestAddrFrom16(t *testing.T) {
553 tests := []struct {
554 name string
555 in [16]byte
556 want Addr
557 }{
558 {
559 name: "v6-raw",
560 in: [...]byte{15: 1},
561 want: MkAddr(Mk128(0, 1), Z6noz),
562 },
563 {
564 name: "v4-raw",
565 in: [...]byte{10: 0xff, 11: 0xff, 12: 1, 13: 2, 14: 3, 15: 4},
566 want: MkAddr(Mk128(0, 0xffff01020304), Z6noz),
567 },
568 }
569 for _, tt := range tests {
570 t.Run(tt.name, func(t *testing.T) {
571 got := AddrFrom16(tt.in)
572 if got != tt.want {
573 t.Errorf("got %#v; want %#v", got, tt.want)
574 }
575 })
576 }
577 }
578
579 func TestIPProperties(t *testing.T) {
580 var (
581 nilIP Addr
582
583 unicast4 = mustIP("192.0.2.1")
584 unicast6 = mustIP("2001:db8::1")
585 unicastZone6 = mustIP("2001:db8::1%eth0")
586 unicast6Unassigned = mustIP("4000::1")
587
588 multicast4 = mustIP("224.0.0.1")
589 multicast6 = mustIP("ff02::1")
590 multicastZone6 = mustIP("ff02::1%eth0")
591
592 llu4 = mustIP("169.254.0.1")
593 llu6 = mustIP("fe80::1")
594 llu6Last = mustIP("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
595 lluZone6 = mustIP("fe80::1%eth0")
596
597 loopback4 = mustIP("127.0.0.1")
598
599 ilm6 = mustIP("ff01::1")
600 ilmZone6 = mustIP("ff01::1%eth0")
601
602 private4a = mustIP("10.0.0.1")
603 private4b = mustIP("172.16.0.1")
604 private4c = mustIP("192.168.1.1")
605 private6 = mustIP("fd00::1")
606 private6mapped4a = mustIP("::ffff:10.0.0.1")
607 private6mapped4b = mustIP("::ffff:172.16.0.1")
608 private6mapped4c = mustIP("::ffff:192.168.1.1")
609 )
610
611 tests := []struct {
612 name string
613 ip Addr
614 globalUnicast bool
615 interfaceLocalMulticast bool
616 linkLocalMulticast bool
617 linkLocalUnicast bool
618 loopback bool
619 multicast bool
620 private bool
621 unspecified bool
622 }{
623 {
624 name: "nil",
625 ip: nilIP,
626 },
627 {
628 name: "unicast v4Addr",
629 ip: unicast4,
630 globalUnicast: true,
631 },
632 {
633 name: "unicast v6 mapped v4Addr",
634 ip: AddrFrom16(unicast4.As16()),
635 globalUnicast: true,
636 },
637 {
638 name: "unicast v6Addr",
639 ip: unicast6,
640 globalUnicast: true,
641 },
642 {
643 name: "unicast v6AddrZone",
644 ip: unicastZone6,
645 globalUnicast: true,
646 },
647 {
648 name: "unicast v6Addr unassigned",
649 ip: unicast6Unassigned,
650 globalUnicast: true,
651 },
652 {
653 name: "multicast v4Addr",
654 ip: multicast4,
655 linkLocalMulticast: true,
656 multicast: true,
657 },
658 {
659 name: "multicast v6 mapped v4Addr",
660 ip: AddrFrom16(multicast4.As16()),
661 linkLocalMulticast: true,
662 multicast: true,
663 },
664 {
665 name: "multicast v6Addr",
666 ip: multicast6,
667 linkLocalMulticast: true,
668 multicast: true,
669 },
670 {
671 name: "multicast v6AddrZone",
672 ip: multicastZone6,
673 linkLocalMulticast: true,
674 multicast: true,
675 },
676 {
677 name: "link-local unicast v4Addr",
678 ip: llu4,
679 linkLocalUnicast: true,
680 },
681 {
682 name: "link-local unicast v6 mapped v4Addr",
683 ip: AddrFrom16(llu4.As16()),
684 linkLocalUnicast: true,
685 },
686 {
687 name: "link-local unicast v6Addr",
688 ip: llu6,
689 linkLocalUnicast: true,
690 },
691 {
692 name: "link-local unicast v6Addr upper bound",
693 ip: llu6Last,
694 linkLocalUnicast: true,
695 },
696 {
697 name: "link-local unicast v6AddrZone",
698 ip: lluZone6,
699 linkLocalUnicast: true,
700 },
701 {
702 name: "loopback v4Addr",
703 ip: loopback4,
704 loopback: true,
705 },
706 {
707 name: "loopback v6Addr",
708 ip: IPv6Loopback(),
709 loopback: true,
710 },
711 {
712 name: "loopback v6 mapped v4Addr",
713 ip: AddrFrom16(IPv6Loopback().As16()),
714 loopback: true,
715 },
716 {
717 name: "interface-local multicast v6Addr",
718 ip: ilm6,
719 interfaceLocalMulticast: true,
720 multicast: true,
721 },
722 {
723 name: "interface-local multicast v6AddrZone",
724 ip: ilmZone6,
725 interfaceLocalMulticast: true,
726 multicast: true,
727 },
728 {
729 name: "private v4Addr 10/8",
730 ip: private4a,
731 globalUnicast: true,
732 private: true,
733 },
734 {
735 name: "private v4Addr 172.16/12",
736 ip: private4b,
737 globalUnicast: true,
738 private: true,
739 },
740 {
741 name: "private v4Addr 192.168/16",
742 ip: private4c,
743 globalUnicast: true,
744 private: true,
745 },
746 {
747 name: "private v6Addr",
748 ip: private6,
749 globalUnicast: true,
750 private: true,
751 },
752 {
753 name: "private v6 mapped v4Addr 10/8",
754 ip: private6mapped4a,
755 globalUnicast: true,
756 private: true,
757 },
758 {
759 name: "private v6 mapped v4Addr 172.16/12",
760 ip: private6mapped4b,
761 globalUnicast: true,
762 private: true,
763 },
764 {
765 name: "private v6 mapped v4Addr 192.168/16",
766 ip: private6mapped4c,
767 globalUnicast: true,
768 private: true,
769 },
770 {
771 name: "unspecified v4Addr",
772 ip: IPv4Unspecified(),
773 unspecified: true,
774 },
775 {
776 name: "unspecified v6Addr",
777 ip: IPv6Unspecified(),
778 unspecified: true,
779 },
780 }
781
782 for _, tt := range tests {
783 t.Run(tt.name, func(t *testing.T) {
784 gu := tt.ip.IsGlobalUnicast()
785 if gu != tt.globalUnicast {
786 t.Errorf("IsGlobalUnicast(%v) = %v; want %v", tt.ip, gu, tt.globalUnicast)
787 }
788
789 ilm := tt.ip.IsInterfaceLocalMulticast()
790 if ilm != tt.interfaceLocalMulticast {
791 t.Errorf("IsInterfaceLocalMulticast(%v) = %v; want %v", tt.ip, ilm, tt.interfaceLocalMulticast)
792 }
793
794 llu := tt.ip.IsLinkLocalUnicast()
795 if llu != tt.linkLocalUnicast {
796 t.Errorf("IsLinkLocalUnicast(%v) = %v; want %v", tt.ip, llu, tt.linkLocalUnicast)
797 }
798
799 llm := tt.ip.IsLinkLocalMulticast()
800 if llm != tt.linkLocalMulticast {
801 t.Errorf("IsLinkLocalMulticast(%v) = %v; want %v", tt.ip, llm, tt.linkLocalMulticast)
802 }
803
804 lo := tt.ip.IsLoopback()
805 if lo != tt.loopback {
806 t.Errorf("IsLoopback(%v) = %v; want %v", tt.ip, lo, tt.loopback)
807 }
808
809 multicast := tt.ip.IsMulticast()
810 if multicast != tt.multicast {
811 t.Errorf("IsMulticast(%v) = %v; want %v", tt.ip, multicast, tt.multicast)
812 }
813
814 private := tt.ip.IsPrivate()
815 if private != tt.private {
816 t.Errorf("IsPrivate(%v) = %v; want %v", tt.ip, private, tt.private)
817 }
818
819 unspecified := tt.ip.IsUnspecified()
820 if unspecified != tt.unspecified {
821 t.Errorf("IsUnspecified(%v) = %v; want %v", tt.ip, unspecified, tt.unspecified)
822 }
823 })
824 }
825 }
826
827 func TestAddrWellKnown(t *testing.T) {
828 tests := []struct {
829 name string
830 ip Addr
831 std net.IP
832 }{
833 {
834 name: "IPv4 unspecified",
835 ip: IPv4Unspecified(),
836 std: net.IPv4zero,
837 },
838 {
839 name: "IPv6 link-local all nodes",
840 ip: IPv6LinkLocalAllNodes(),
841 std: net.IPv6linklocalallnodes,
842 },
843 {
844 name: "IPv6 link-local all routers",
845 ip: IPv6LinkLocalAllRouters(),
846 std: net.IPv6linklocalallrouters,
847 },
848 {
849 name: "IPv6 loopback",
850 ip: IPv6Loopback(),
851 std: net.IPv6loopback,
852 },
853 {
854 name: "IPv6 unspecified",
855 ip: IPv6Unspecified(),
856 std: net.IPv6unspecified,
857 },
858 }
859
860 for _, tt := range tests {
861 t.Run(tt.name, func(t *testing.T) {
862 want := tt.std.String()
863 got := tt.ip.String()
864
865 if got != want {
866 t.Fatalf("got %s, want %s", got, want)
867 }
868 })
869 }
870 }
871
872 func TestAddrLessCompare(t *testing.T) {
873 tests := []struct {
874 a, b Addr
875 want bool
876 }{
877 {Addr{}, Addr{}, false},
878 {Addr{}, mustIP("1.2.3.4"), true},
879 {mustIP("1.2.3.4"), Addr{}, false},
880
881 {mustIP("1.2.3.4"), mustIP("0102:0304::0"), true},
882 {mustIP("0102:0304::0"), mustIP("1.2.3.4"), false},
883 {mustIP("1.2.3.4"), mustIP("1.2.3.4"), false},
884
885 {mustIP("::1"), mustIP("::2"), true},
886 {mustIP("::1"), mustIP("::1%foo"), true},
887 {mustIP("::1%foo"), mustIP("::2"), true},
888 {mustIP("::2"), mustIP("::3"), true},
889
890 {mustIP("::"), mustIP("0.0.0.0"), false},
891 {mustIP("0.0.0.0"), mustIP("::"), true},
892
893 {mustIP("::1%a"), mustIP("::1%b"), true},
894 {mustIP("::1%a"), mustIP("::1%a"), false},
895 {mustIP("::1%b"), mustIP("::1%a"), false},
896
897
898
899
900
901
902
903
904 {mustIP("::ffff:11.1.1.12"), mustIP("11.1.1.12"), false},
905 }
906 for _, tt := range tests {
907 got := tt.a.Less(tt.b)
908 if got != tt.want {
909 t.Errorf("Less(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
910 }
911 cmp := tt.a.Compare(tt.b)
912 if got && cmp != -1 {
913 t.Errorf("Less(%q, %q) = true, but Compare = %v (not -1)", tt.a, tt.b, cmp)
914 }
915 if cmp < -1 || cmp > 1 {
916 t.Errorf("bogus Compare return value %v", cmp)
917 }
918 if cmp == 0 && tt.a != tt.b {
919 t.Errorf("Compare(%q, %q) = 0; but not equal", tt.a, tt.b)
920 }
921 if cmp == 1 && !tt.b.Less(tt.a) {
922 t.Errorf("Compare(%q, %q) = 1; but b.Less(a) isn't true", tt.a, tt.b)
923 }
924
925
926 if got == tt.want && got {
927 got2 := tt.b.Less(tt.a)
928 if got2 {
929 t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a)
930 }
931 }
932
933
934 deepEq := reflect.DeepEqual(tt.a, tt.b)
935 if (cmp == 0) != deepEq {
936 t.Errorf("%q and %q differ in == (%v) vs reflect.DeepEqual (%v)", tt.a, tt.b, cmp == 0, deepEq)
937 }
938 }
939
940
941 values := []Addr{
942 mustIP("::1"),
943 mustIP("::2"),
944 Addr{},
945 mustIP("1.2.3.4"),
946 mustIP("8.8.8.8"),
947 mustIP("::1%foo"),
948 }
949 slices.SortFunc(values, Addr.Compare)
950 got := fmt.Sprintf("%s", values)
951 want := `[invalid IP 1.2.3.4 8.8.8.8 ::1 ::1%foo ::2]`
952 if got != want {
953 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
954 }
955 }
956
957 func TestAddrPortCompare(t *testing.T) {
958 tests := []struct {
959 a, b AddrPort
960 want int
961 }{
962 {AddrPort{}, AddrPort{}, 0},
963 {AddrPort{}, mustIPPort("1.2.3.4:80"), -1},
964
965 {mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:80"), 0},
966 {mustIPPort("[::1]:80"), mustIPPort("[::1]:80"), 0},
967
968 {mustIPPort("1.2.3.4:80"), mustIPPort("2.3.4.5:22"), -1},
969 {mustIPPort("[::1]:80"), mustIPPort("[::2]:22"), -1},
970
971 {mustIPPort("1.2.3.4:80"), mustIPPort("1.2.3.4:443"), -1},
972 {mustIPPort("[::1]:80"), mustIPPort("[::1]:443"), -1},
973
974 {mustIPPort("1.2.3.4:80"), mustIPPort("[0102:0304::0]:80"), -1},
975 }
976 for _, tt := range tests {
977 got := tt.a.Compare(tt.b)
978 if got != tt.want {
979 t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
980 }
981
982
983 if got == tt.want {
984 got2 := tt.b.Compare(tt.a)
985 if want2 := -1 * tt.want; got2 != want2 {
986 t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
987 }
988 }
989 }
990
991
992 values := []AddrPort{
993 mustIPPort("[::1]:80"),
994 mustIPPort("[::2]:80"),
995 AddrPort{},
996 mustIPPort("1.2.3.4:443"),
997 mustIPPort("8.8.8.8:8080"),
998 mustIPPort("[::1%foo]:1024"),
999 }
1000 slices.SortFunc(values, AddrPort.Compare)
1001 got := fmt.Sprintf("%s", values)
1002 want := `[invalid AddrPort 1.2.3.4:443 8.8.8.8:8080 [::1]:80 [::1%foo]:1024 [::2]:80]`
1003 if got != want {
1004 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
1005 }
1006 }
1007
1008 func TestPrefixCompare(t *testing.T) {
1009 tests := []struct {
1010 a, b Prefix
1011 want int
1012 }{
1013 {Prefix{}, Prefix{}, 0},
1014 {Prefix{}, mustPrefix("1.2.3.0/24"), -1},
1015
1016 {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.3.0/24"), 0},
1017 {mustPrefix("fe80::/64"), mustPrefix("fe80::/64"), 0},
1018
1019 {mustPrefix("1.2.3.0/24"), mustPrefix("1.2.4.0/24"), -1},
1020 {mustPrefix("fe80::/64"), mustPrefix("fe90::/64"), -1},
1021
1022 {mustPrefix("1.2.0.0/16"), mustPrefix("1.2.0.0/24"), -1},
1023 {mustPrefix("fe80::/48"), mustPrefix("fe80::/64"), -1},
1024
1025 {mustPrefix("1.2.3.0/24"), mustPrefix("fe80::/8"), -1},
1026 }
1027 for _, tt := range tests {
1028 got := tt.a.Compare(tt.b)
1029 if got != tt.want {
1030 t.Errorf("Compare(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want)
1031 }
1032
1033
1034 if got == tt.want {
1035 got2 := tt.b.Compare(tt.a)
1036 if want2 := -1 * tt.want; got2 != want2 {
1037 t.Errorf("Compare(%q, %q) was correctly %v, but Compare(%q, %q) was %v", tt.a, tt.b, got, tt.b, tt.a, got2)
1038 }
1039 }
1040 }
1041
1042
1043 values := []Prefix{
1044 mustPrefix("1.2.3.0/24"),
1045 mustPrefix("fe90::/64"),
1046 mustPrefix("fe80::/64"),
1047 mustPrefix("1.2.0.0/16"),
1048 Prefix{},
1049 mustPrefix("fe80::/48"),
1050 mustPrefix("1.2.0.0/24"),
1051 }
1052 slices.SortFunc(values, Prefix.Compare)
1053 got := fmt.Sprintf("%s", values)
1054 want := `[invalid Prefix 1.2.0.0/16 1.2.0.0/24 1.2.3.0/24 fe80::/48 fe80::/64 fe90::/64]`
1055 if got != want {
1056 t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want)
1057 }
1058 }
1059
1060 func TestIPStringExpanded(t *testing.T) {
1061 tests := []struct {
1062 ip Addr
1063 s string
1064 }{
1065 {
1066 ip: Addr{},
1067 s: "invalid IP",
1068 },
1069 {
1070 ip: mustIP("192.0.2.1"),
1071 s: "192.0.2.1",
1072 },
1073 {
1074 ip: mustIP("::ffff:192.0.2.1"),
1075 s: "0000:0000:0000:0000:0000:ffff:c000:0201",
1076 },
1077 {
1078 ip: mustIP("2001:db8::1"),
1079 s: "2001:0db8:0000:0000:0000:0000:0000:0001",
1080 },
1081 {
1082 ip: mustIP("2001:db8::1%eth0"),
1083 s: "2001:0db8:0000:0000:0000:0000:0000:0001%eth0",
1084 },
1085 }
1086
1087 for _, tt := range tests {
1088 t.Run(tt.ip.String(), func(t *testing.T) {
1089 want := tt.s
1090 got := tt.ip.StringExpanded()
1091
1092 if got != want {
1093 t.Fatalf("got %s, want %s", got, want)
1094 }
1095 })
1096 }
1097 }
1098
1099 func TestPrefixMasking(t *testing.T) {
1100 type subtest struct {
1101 ip Addr
1102 bits uint8
1103 p Prefix
1104 ok bool
1105 }
1106
1107
1108 makeIPv6 := func(zone string) []subtest {
1109 if zone != "" {
1110 zone = "%" + zone
1111 }
1112
1113 return []subtest{
1114 {
1115 ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1116 bits: 255,
1117 },
1118 {
1119 ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
1120 bits: 32,
1121 p: mustPrefix("2001:db8::/32"),
1122 ok: true,
1123 },
1124 {
1125 ip: mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
1126 bits: 96,
1127 p: mustPrefix("fe80::dead:beef:0:0/96"),
1128 ok: true,
1129 },
1130 {
1131 ip: mustIP(fmt.Sprintf("aaaa::%s", zone)),
1132 bits: 4,
1133 p: mustPrefix("a000::/4"),
1134 ok: true,
1135 },
1136 {
1137 ip: mustIP(fmt.Sprintf("::%s", zone)),
1138 bits: 63,
1139 p: mustPrefix("::/63"),
1140 ok: true,
1141 },
1142 }
1143 }
1144
1145 tests := []struct {
1146 family string
1147 subtests []subtest
1148 }{
1149 {
1150 family: "nil",
1151 subtests: []subtest{
1152 {
1153 bits: 255,
1154 ok: true,
1155 },
1156 {
1157 bits: 16,
1158 ok: true,
1159 },
1160 },
1161 },
1162 {
1163 family: "IPv4",
1164 subtests: []subtest{
1165 {
1166 ip: mustIP("192.0.2.0"),
1167 bits: 255,
1168 },
1169 {
1170 ip: mustIP("192.0.2.0"),
1171 bits: 16,
1172 p: mustPrefix("192.0.0.0/16"),
1173 ok: true,
1174 },
1175 {
1176 ip: mustIP("255.255.255.255"),
1177 bits: 20,
1178 p: mustPrefix("255.255.240.0/20"),
1179 ok: true,
1180 },
1181 {
1182
1183
1184 ip: mustIP("100.98.156.66"),
1185 bits: 10,
1186 p: mustPrefix("100.64.0.0/10"),
1187 ok: true,
1188 },
1189 },
1190 },
1191 {
1192 family: "IPv6",
1193 subtests: makeIPv6(""),
1194 },
1195 {
1196 family: "IPv6 zone",
1197 subtests: makeIPv6("eth0"),
1198 },
1199 }
1200
1201 for _, tt := range tests {
1202 t.Run(tt.family, func(t *testing.T) {
1203 for _, st := range tt.subtests {
1204 t.Run(st.p.String(), func(t *testing.T) {
1205
1206 orig := st.ip.String()
1207
1208 p, err := st.ip.Prefix(int(st.bits))
1209 if st.ok && err != nil {
1210 t.Fatalf("failed to produce prefix: %v", err)
1211 }
1212 if !st.ok && err == nil {
1213 t.Fatal("expected an error, but none occurred")
1214 }
1215 if err != nil {
1216 t.Logf("err: %v", err)
1217 return
1218 }
1219
1220 if !reflect.DeepEqual(p, st.p) {
1221 t.Errorf("prefix = %q, want %q", p, st.p)
1222 }
1223
1224 if got := st.ip.String(); got != orig {
1225 t.Errorf("IP was mutated: %q, want %q", got, orig)
1226 }
1227 })
1228 }
1229 })
1230 }
1231 }
1232
1233 func TestPrefixMarshalUnmarshal(t *testing.T) {
1234 tests := []string{
1235 "",
1236 "1.2.3.4/32",
1237 "0.0.0.0/0",
1238 "::/0",
1239 "::1/128",
1240 "2001:db8::/32",
1241 }
1242
1243 for _, s := range tests {
1244 t.Run(s, func(t *testing.T) {
1245
1246
1247 orig := `"` + s + `"`
1248
1249 var p Prefix
1250 if err := json.Unmarshal([]byte(orig), &p); err != nil {
1251 t.Fatalf("failed to unmarshal: %v", err)
1252 }
1253
1254 pb, err := json.Marshal(p)
1255 if err != nil {
1256 t.Fatalf("failed to marshal: %v", err)
1257 }
1258
1259 back := string(pb)
1260 if orig != back {
1261 t.Errorf("Marshal = %q; want %q", back, orig)
1262 }
1263 })
1264 }
1265 }
1266
1267 func TestPrefixUnmarshalTextNonZero(t *testing.T) {
1268 ip := mustPrefix("fe80::/64")
1269 if err := ip.UnmarshalText([]byte("xxx")); err == nil {
1270 t.Fatal("unmarshaled into non-empty Prefix")
1271 }
1272 }
1273
1274 func TestIs4AndIs6(t *testing.T) {
1275 tests := []struct {
1276 ip Addr
1277 is4 bool
1278 is6 bool
1279 }{
1280 {Addr{}, false, false},
1281 {mustIP("1.2.3.4"), true, false},
1282 {mustIP("127.0.0.2"), true, false},
1283 {mustIP("::1"), false, true},
1284 {mustIP("::ffff:192.0.2.128"), false, true},
1285 {mustIP("::fffe:c000:0280"), false, true},
1286 {mustIP("::1%eth0"), false, true},
1287 }
1288 for _, tt := range tests {
1289 got4 := tt.ip.Is4()
1290 if got4 != tt.is4 {
1291 t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4)
1292 }
1293
1294 got6 := tt.ip.Is6()
1295 if got6 != tt.is6 {
1296 t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6)
1297 }
1298 }
1299 }
1300
1301 func TestIs4In6(t *testing.T) {
1302 tests := []struct {
1303 ip Addr
1304 want bool
1305 wantUnmap Addr
1306 }{
1307 {Addr{}, false, Addr{}},
1308 {mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")},
1309 {mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")},
1310 {mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")},
1311 {mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")},
1312 {mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1313 {mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")},
1314 {mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1315 {mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")},
1316 {mustIP("::1"), false, mustIP("::1")},
1317 {mustIP("1.2.3.4"), false, mustIP("1.2.3.4")},
1318 }
1319 for _, tt := range tests {
1320 got := tt.ip.Is4In6()
1321 if got != tt.want {
1322 t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want)
1323 }
1324 u := tt.ip.Unmap()
1325 if u != tt.wantUnmap {
1326 t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap)
1327 }
1328 }
1329 }
1330
1331 func TestPrefixMasked(t *testing.T) {
1332 tests := []struct {
1333 prefix Prefix
1334 masked Prefix
1335 }{
1336 {
1337 prefix: mustPrefix("192.168.0.255/24"),
1338 masked: mustPrefix("192.168.0.0/24"),
1339 },
1340 {
1341 prefix: mustPrefix("2100::/3"),
1342 masked: mustPrefix("2000::/3"),
1343 },
1344 {
1345 prefix: PrefixFrom(mustIP("2000::"), 129),
1346 masked: Prefix{},
1347 },
1348 {
1349 prefix: PrefixFrom(mustIP("1.2.3.4"), 33),
1350 masked: Prefix{},
1351 },
1352 }
1353 for _, test := range tests {
1354 t.Run(test.prefix.String(), func(t *testing.T) {
1355 got := test.prefix.Masked()
1356 if got != test.masked {
1357 t.Errorf("Masked=%s, want %s", got, test.masked)
1358 }
1359 })
1360 }
1361 }
1362
1363 func TestPrefix(t *testing.T) {
1364 tests := []struct {
1365 prefix string
1366 ip Addr
1367 bits int
1368 str string
1369 contains []Addr
1370 notContains []Addr
1371 }{
1372 {
1373 prefix: "192.168.0.0/24",
1374 ip: mustIP("192.168.0.0"),
1375 bits: 24,
1376 contains: mustIPs("192.168.0.1", "192.168.0.55"),
1377 notContains: mustIPs("192.168.1.1", "1.1.1.1"),
1378 },
1379 {
1380 prefix: "192.168.1.1/32",
1381 ip: mustIP("192.168.1.1"),
1382 bits: 32,
1383 contains: mustIPs("192.168.1.1"),
1384 notContains: mustIPs("192.168.1.2"),
1385 },
1386 {
1387 prefix: "100.64.0.0/10",
1388 ip: mustIP("100.64.0.0"),
1389 bits: 10,
1390 contains: mustIPs("100.64.0.0", "100.64.0.1", "100.81.251.94", "100.100.100.100", "100.127.255.254", "100.127.255.255"),
1391 notContains: mustIPs("100.63.255.255", "100.128.0.0"),
1392 },
1393 {
1394 prefix: "2001:db8::/96",
1395 ip: mustIP("2001:db8::"),
1396 bits: 96,
1397 contains: mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"),
1398 notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"),
1399 },
1400 {
1401 prefix: "0.0.0.0/0",
1402 ip: mustIP("0.0.0.0"),
1403 bits: 0,
1404 contains: mustIPs("192.168.0.1", "1.1.1.1"),
1405 notContains: append(mustIPs("2001:db8::1"), Addr{}),
1406 },
1407 {
1408 prefix: "::/0",
1409 ip: mustIP("::"),
1410 bits: 0,
1411 contains: mustIPs("::1", "2001:db8::1"),
1412 notContains: mustIPs("192.0.2.1"),
1413 },
1414 {
1415 prefix: "2000::/3",
1416 ip: mustIP("2000::"),
1417 bits: 3,
1418 contains: mustIPs("2001:db8::1"),
1419 notContains: mustIPs("fe80::1"),
1420 },
1421 }
1422 for _, test := range tests {
1423 t.Run(test.prefix, func(t *testing.T) {
1424 prefix, err := ParsePrefix(test.prefix)
1425 if err != nil {
1426 t.Fatal(err)
1427 }
1428 if prefix.Addr() != test.ip {
1429 t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip)
1430 }
1431 if prefix.Bits() != test.bits {
1432 t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits)
1433 }
1434 for _, ip := range test.contains {
1435 if !prefix.Contains(ip) {
1436 t.Errorf("does not contain %s", ip)
1437 }
1438 }
1439 for _, ip := range test.notContains {
1440 if prefix.Contains(ip) {
1441 t.Errorf("contains %s", ip)
1442 }
1443 }
1444 want := test.str
1445 if want == "" {
1446 want = test.prefix
1447 }
1448 if got := prefix.String(); got != want {
1449 t.Errorf("prefix.String()=%q, want %q", got, want)
1450 }
1451
1452 TestAppendToMarshal(t, prefix)
1453 })
1454 }
1455 }
1456
1457 func TestPrefixFromInvalidBits(t *testing.T) {
1458 v4 := MustParseAddr("1.2.3.4")
1459 v6 := MustParseAddr("66::66")
1460 tests := []struct {
1461 ip Addr
1462 in, want int
1463 }{
1464 {v4, 0, 0},
1465 {v6, 0, 0},
1466 {v4, 1, 1},
1467 {v4, 33, -1},
1468 {v6, 33, 33},
1469 {v6, 127, 127},
1470 {v6, 128, 128},
1471 {v4, 254, -1},
1472 {v4, 255, -1},
1473 {v4, -1, -1},
1474 {v6, -1, -1},
1475 {v4, -5, -1},
1476 {v6, -5, -1},
1477 }
1478 for _, tt := range tests {
1479 p := PrefixFrom(tt.ip, tt.in)
1480 if got := p.Bits(); got != tt.want {
1481 t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want)
1482 }
1483 }
1484 }
1485
1486 func TestParsePrefixAllocs(t *testing.T) {
1487 tests := []struct {
1488 ip string
1489 slash string
1490 }{
1491 {"192.168.1.0", "/24"},
1492 {"aaaa:bbbb:cccc::", "/24"},
1493 }
1494 for _, test := range tests {
1495 prefix := test.ip + test.slash
1496 t.Run(prefix, func(t *testing.T) {
1497 ipAllocs := int(testing.AllocsPerRun(5, func() {
1498 ParseAddr(test.ip)
1499 }))
1500 prefixAllocs := int(testing.AllocsPerRun(5, func() {
1501 ParsePrefix(prefix)
1502 }))
1503 if got := prefixAllocs - ipAllocs; got != 0 {
1504 t.Errorf("allocs=%d, want 0", got)
1505 }
1506 })
1507 }
1508 }
1509
1510 func TestParsePrefixError(t *testing.T) {
1511 tests := []struct {
1512 prefix string
1513 errstr string
1514 }{
1515 {
1516 prefix: "192.168.0.0",
1517 errstr: "no '/'",
1518 },
1519 {
1520 prefix: "1.257.1.1/24",
1521 errstr: "value >255",
1522 },
1523 {
1524 prefix: "1.1.1.0/q",
1525 errstr: "bad bits",
1526 },
1527 {
1528 prefix: "1.1.1.0/-1",
1529 errstr: "bad bits",
1530 },
1531 {
1532 prefix: "1.1.1.0/33",
1533 errstr: "out of range",
1534 },
1535 {
1536 prefix: "2001::/129",
1537 errstr: "out of range",
1538 },
1539
1540 {
1541 prefix: "1.1.1.0%a/24",
1542 errstr: "unexpected character",
1543 },
1544 {
1545 prefix: "2001:db8::%a/32",
1546 errstr: "zones cannot be present",
1547 },
1548 {
1549 prefix: "1.1.1.0/+32",
1550 errstr: "bad bits",
1551 },
1552 {
1553 prefix: "1.1.1.0/-32",
1554 errstr: "bad bits",
1555 },
1556 {
1557 prefix: "1.1.1.0/032",
1558 errstr: "bad bits",
1559 },
1560 {
1561 prefix: "1.1.1.0/0032",
1562 errstr: "bad bits",
1563 },
1564 }
1565 for _, test := range tests {
1566 t.Run(test.prefix, func(t *testing.T) {
1567 _, err := ParsePrefix(test.prefix)
1568 if err == nil {
1569 t.Fatal("no error")
1570 }
1571 if got := err.Error(); !strings.Contains(got, test.errstr) {
1572 t.Errorf("error is missing substring %q: %s", test.errstr, got)
1573 }
1574 })
1575 }
1576 }
1577
1578 func TestPrefixIsSingleIP(t *testing.T) {
1579 tests := []struct {
1580 ipp Prefix
1581 want bool
1582 }{
1583 {ipp: mustPrefix("127.0.0.1/32"), want: true},
1584 {ipp: mustPrefix("127.0.0.1/31"), want: false},
1585 {ipp: mustPrefix("127.0.0.1/0"), want: false},
1586 {ipp: mustPrefix("::1/128"), want: true},
1587 {ipp: mustPrefix("::1/127"), want: false},
1588 {ipp: mustPrefix("::1/0"), want: false},
1589 {ipp: Prefix{}, want: false},
1590 }
1591 for _, tt := range tests {
1592 got := tt.ipp.IsSingleIP()
1593 if got != tt.want {
1594 t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want)
1595 }
1596 }
1597 }
1598
1599 func mustIPs(strs ...string) []Addr {
1600 var res []Addr
1601 for _, s := range strs {
1602 res = append(res, mustIP(s))
1603 }
1604 return res
1605 }
1606
1607 func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
1608 b.ReportAllocs()
1609 tests := []struct {
1610 name string
1611 ip string
1612 }{
1613 {"ipv4", "1.2.3.4"},
1614 {"ipv6", "2001:db8::1"},
1615 {"ipv6+zone", "2001:db8::1%eth0"},
1616 }
1617 for _, tc := range tests {
1618 b.Run(tc.name, func(b *testing.B) {
1619 ip := mustIP(tc.ip)
1620 for i := 0; i < b.N; i++ {
1621 bt, err := ip.MarshalBinary()
1622 if err != nil {
1623 b.Fatal(err)
1624 }
1625 var ip2 Addr
1626 if err := ip2.UnmarshalBinary(bt); err != nil {
1627 b.Fatal(err)
1628 }
1629 }
1630 })
1631 }
1632 }
1633
1634 func BenchmarkStdIPv4(b *testing.B) {
1635 b.ReportAllocs()
1636 ips := []net.IP{}
1637 for i := 0; i < b.N; i++ {
1638 ip := net.IPv4(8, 8, 8, 8)
1639 ips = ips[:0]
1640 for i := 0; i < 100; i++ {
1641 ips = append(ips, ip)
1642 }
1643 }
1644 }
1645
1646 func BenchmarkIPv4(b *testing.B) {
1647 b.ReportAllocs()
1648 ips := []Addr{}
1649 for i := 0; i < b.N; i++ {
1650 ip := IPv4(8, 8, 8, 8)
1651 ips = ips[:0]
1652 for i := 0; i < 100; i++ {
1653 ips = append(ips, ip)
1654 }
1655 }
1656 }
1657
1658
1659
1660
1661 type ip4i struct {
1662 ip4 [4]byte
1663 flags1 byte
1664 flags2 byte
1665 flags3 byte
1666 flags4 byte
1667 ipv6 any
1668 }
1669
1670 func newip4i_v4(a, b, c, d byte) ip4i {
1671 return ip4i{ip4: [4]byte{a, b, c, d}}
1672 }
1673
1674
1675 func BenchmarkIPv4_inline(b *testing.B) {
1676 b.ReportAllocs()
1677 ips := []ip4i{}
1678 for i := 0; i < b.N; i++ {
1679 ip := newip4i_v4(8, 8, 8, 8)
1680 ips = ips[:0]
1681 for i := 0; i < 100; i++ {
1682 ips = append(ips, ip)
1683 }
1684 }
1685 }
1686
1687 func BenchmarkStdIPv6(b *testing.B) {
1688 b.ReportAllocs()
1689 ips := []net.IP{}
1690 for i := 0; i < b.N; i++ {
1691 ip := net.ParseIP("2001:db8::1")
1692 ips = ips[:0]
1693 for i := 0; i < 100; i++ {
1694 ips = append(ips, ip)
1695 }
1696 }
1697 }
1698
1699 func BenchmarkIPv6(b *testing.B) {
1700 b.ReportAllocs()
1701 ips := []Addr{}
1702 for i := 0; i < b.N; i++ {
1703 ip := mustIP("2001:db8::1")
1704 ips = ips[:0]
1705 for i := 0; i < 100; i++ {
1706 ips = append(ips, ip)
1707 }
1708 }
1709 }
1710
1711 func BenchmarkIPv4Contains(b *testing.B) {
1712 b.ReportAllocs()
1713 prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24)
1714 ip := IPv4(192, 168, 1, 1)
1715 for i := 0; i < b.N; i++ {
1716 prefix.Contains(ip)
1717 }
1718 }
1719
1720 func BenchmarkIPv6Contains(b *testing.B) {
1721 b.ReportAllocs()
1722 prefix := MustParsePrefix("::1/128")
1723 ip := MustParseAddr("::1")
1724 for i := 0; i < b.N; i++ {
1725 prefix.Contains(ip)
1726 }
1727 }
1728
1729 var parseBenchInputs = []struct {
1730 name string
1731 ip string
1732 }{
1733 {"v4", "192.168.1.1"},
1734 {"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
1735 {"v6_ellipsis", "fd7a:115c::626b:430b"},
1736 {"v6_v4", "::ffff:192.168.140.255"},
1737 {"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
1738 }
1739
1740 func BenchmarkParseAddr(b *testing.B) {
1741 sinkInternValue = unique.Make(MakeAddrDetail(true, "eth1"))
1742 for _, test := range parseBenchInputs {
1743 b.Run(test.name, func(b *testing.B) {
1744 b.ReportAllocs()
1745 for i := 0; i < b.N; i++ {
1746 sinkIP, _ = ParseAddr(test.ip)
1747 }
1748 })
1749 }
1750 }
1751
1752 func BenchmarkStdParseIP(b *testing.B) {
1753 for _, test := range parseBenchInputs {
1754 b.Run(test.name, func(b *testing.B) {
1755 b.ReportAllocs()
1756 for i := 0; i < b.N; i++ {
1757 sinkStdIP = net.ParseIP(test.ip)
1758 }
1759 })
1760 }
1761 }
1762
1763 func BenchmarkAddrString(b *testing.B) {
1764 for _, test := range parseBenchInputs {
1765 ip := MustParseAddr(test.ip)
1766 b.Run(test.name, func(b *testing.B) {
1767 b.ReportAllocs()
1768 for i := 0; i < b.N; i++ {
1769 sinkString = ip.String()
1770 }
1771 })
1772 }
1773 }
1774
1775 func BenchmarkIPStringExpanded(b *testing.B) {
1776 for _, test := range parseBenchInputs {
1777 ip := MustParseAddr(test.ip)
1778 b.Run(test.name, func(b *testing.B) {
1779 b.ReportAllocs()
1780 for i := 0; i < b.N; i++ {
1781 sinkString = ip.StringExpanded()
1782 }
1783 })
1784 }
1785 }
1786
1787 func BenchmarkAddrMarshalText(b *testing.B) {
1788 for _, test := range parseBenchInputs {
1789 ip := MustParseAddr(test.ip)
1790 b.Run(test.name, func(b *testing.B) {
1791 b.ReportAllocs()
1792 for i := 0; i < b.N; i++ {
1793 sinkBytes, _ = ip.MarshalText()
1794 }
1795 })
1796 }
1797 }
1798
1799 func BenchmarkAddrPortString(b *testing.B) {
1800 for _, test := range parseBenchInputs {
1801 ip := MustParseAddr(test.ip)
1802 ipp := AddrPortFrom(ip, 60000)
1803 b.Run(test.name, func(b *testing.B) {
1804 b.ReportAllocs()
1805 for i := 0; i < b.N; i++ {
1806 sinkString = ipp.String()
1807 }
1808 })
1809 }
1810 }
1811
1812 func BenchmarkAddrPortMarshalText(b *testing.B) {
1813 for _, test := range parseBenchInputs {
1814 ip := MustParseAddr(test.ip)
1815 ipp := AddrPortFrom(ip, 60000)
1816 b.Run(test.name, func(b *testing.B) {
1817 b.ReportAllocs()
1818 for i := 0; i < b.N; i++ {
1819 sinkBytes, _ = ipp.MarshalText()
1820 }
1821 })
1822 }
1823 }
1824
1825 func BenchmarkPrefixMasking(b *testing.B) {
1826 tests := []struct {
1827 name string
1828 ip Addr
1829 bits int
1830 }{
1831 {
1832 name: "IPv4 /32",
1833 ip: IPv4(192, 0, 2, 0),
1834 bits: 32,
1835 },
1836 {
1837 name: "IPv4 /17",
1838 ip: IPv4(192, 0, 2, 0),
1839 bits: 17,
1840 },
1841 {
1842 name: "IPv4 /0",
1843 ip: IPv4(192, 0, 2, 0),
1844 bits: 0,
1845 },
1846 {
1847 name: "IPv6 /128",
1848 ip: mustIP("2001:db8::1"),
1849 bits: 128,
1850 },
1851 {
1852 name: "IPv6 /65",
1853 ip: mustIP("2001:db8::1"),
1854 bits: 65,
1855 },
1856 {
1857 name: "IPv6 /0",
1858 ip: mustIP("2001:db8::1"),
1859 bits: 0,
1860 },
1861 {
1862 name: "IPv6 zone /128",
1863 ip: mustIP("2001:db8::1%eth0"),
1864 bits: 128,
1865 },
1866 {
1867 name: "IPv6 zone /65",
1868 ip: mustIP("2001:db8::1%eth0"),
1869 bits: 65,
1870 },
1871 {
1872 name: "IPv6 zone /0",
1873 ip: mustIP("2001:db8::1%eth0"),
1874 bits: 0,
1875 },
1876 }
1877
1878 for _, tt := range tests {
1879 b.Run(tt.name, func(b *testing.B) {
1880 b.ReportAllocs()
1881
1882 for i := 0; i < b.N; i++ {
1883 sinkPrefix, _ = tt.ip.Prefix(tt.bits)
1884 }
1885 })
1886 }
1887 }
1888
1889 func BenchmarkPrefixMarshalText(b *testing.B) {
1890 b.ReportAllocs()
1891 ipp := MustParsePrefix("66.55.44.33/22")
1892 for i := 0; i < b.N; i++ {
1893 sinkBytes, _ = ipp.MarshalText()
1894 }
1895 }
1896
1897 func BenchmarkParseAddrPort(b *testing.B) {
1898 for _, test := range parseBenchInputs {
1899 var ipp string
1900 if strings.HasPrefix(test.name, "v6") {
1901 ipp = fmt.Sprintf("[%s]:1234", test.ip)
1902 } else {
1903 ipp = fmt.Sprintf("%s:1234", test.ip)
1904 }
1905 b.Run(test.name, func(b *testing.B) {
1906 b.ReportAllocs()
1907
1908 for i := 0; i < b.N; i++ {
1909 sinkAddrPort, _ = ParseAddrPort(ipp)
1910 }
1911 })
1912 }
1913 }
1914
1915 func TestAs4(t *testing.T) {
1916 tests := []struct {
1917 ip Addr
1918 want [4]byte
1919 wantPanic bool
1920 }{
1921 {
1922 ip: mustIP("1.2.3.4"),
1923 want: [4]byte{1, 2, 3, 4},
1924 },
1925 {
1926 ip: AddrFrom16(mustIP("1.2.3.4").As16()),
1927 want: [4]byte{1, 2, 3, 4},
1928 },
1929 {
1930 ip: mustIP("0.0.0.0"),
1931 want: [4]byte{0, 0, 0, 0},
1932 },
1933 {
1934 ip: Addr{},
1935 wantPanic: true,
1936 },
1937 {
1938 ip: mustIP("::1"),
1939 wantPanic: true,
1940 },
1941 }
1942 as4 := func(ip Addr) (v [4]byte, gotPanic bool) {
1943 defer func() {
1944 if recover() != nil {
1945 gotPanic = true
1946 return
1947 }
1948 }()
1949 v = ip.As4()
1950 return
1951 }
1952 for i, tt := range tests {
1953 got, gotPanic := as4(tt.ip)
1954 if gotPanic != tt.wantPanic {
1955 t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic)
1956 continue
1957 }
1958 if got != tt.want {
1959 t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want)
1960 }
1961 }
1962 }
1963
1964 func TestPrefixOverlaps(t *testing.T) {
1965 pfx := mustPrefix
1966 tests := []struct {
1967 a, b Prefix
1968 want bool
1969 }{
1970 {Prefix{}, pfx("1.2.0.0/16"), false},
1971 {pfx("1.2.0.0/16"), Prefix{}, false},
1972 {pfx("::0/3"), pfx("0.0.0.0/3"), false},
1973
1974 {pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true},
1975
1976 {pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true},
1977 {pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true},
1978
1979 {pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true},
1980 {pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true},
1981
1982
1983 {pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true},
1984 {pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true},
1985
1986 {pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true},
1987
1988
1989 {pfx("5::1/128"), pfx("5::0/8"), true},
1990 {pfx("5::0/8"), pfx("5::1/128"), true},
1991
1992
1993 {pfx("1::1/128"), pfx("2::2/128"), false},
1994 {pfx("0100::0/8"), pfx("::1/128"), false},
1995
1996
1997 {PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false},
1998
1999
2000 {PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false},
2001 {PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false},
2002 }
2003 for i, tt := range tests {
2004 if got := tt.a.Overlaps(tt.b); got != tt.want {
2005 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want)
2006 }
2007
2008 if got := tt.b.Overlaps(tt.a); got != tt.want {
2009 t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want)
2010 }
2011 }
2012 }
2013
2014
2015
2016
2017
2018
2019
2020 var (
2021 sinkIP Addr
2022 sinkStdIP net.IP
2023 sinkAddrPort AddrPort
2024 sinkPrefix Prefix
2025 sinkPrefixSlice []Prefix
2026 sinkInternValue unique.Handle[AddrDetail]
2027 sinkIP16 [16]byte
2028 sinkIP4 [4]byte
2029 sinkBool bool
2030 sinkString string
2031 sinkBytes []byte
2032 sinkUDPAddr = &net.UDPAddr{IP: make(net.IP, 0, 16)}
2033 )
2034
2035 func TestNoAllocs(t *testing.T) {
2036
2037
2038 panicIP := func(ip Addr, err error) Addr {
2039 if err != nil {
2040 panic(err)
2041 }
2042 return ip
2043 }
2044 panicPfx := func(pfx Prefix, err error) Prefix {
2045 if err != nil {
2046 panic(err)
2047 }
2048 return pfx
2049 }
2050 panicIPP := func(ipp AddrPort, err error) AddrPort {
2051 if err != nil {
2052 panic(err)
2053 }
2054 return ipp
2055 }
2056 test := func(name string, f func()) {
2057 t.Run(name, func(t *testing.T) {
2058 n := testing.AllocsPerRun(1000, f)
2059 if n != 0 {
2060 t.Fatalf("allocs = %d; want 0", int(n))
2061 }
2062 })
2063 }
2064
2065
2066 test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) })
2067 test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) })
2068 test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) })
2069 test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) })
2070 test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) })
2071 test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") })
2072 test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() })
2073 test("IPv6LinkLocalAllRouters", func() { sinkIP = IPv6LinkLocalAllRouters() })
2074 test("IPv6Loopback", func() { sinkIP = IPv6Loopback() })
2075 test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() })
2076
2077
2078 test("Addr.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() })
2079 test("Addr.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 })
2080 test("Addr.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" })
2081 test("Addr.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" })
2082 test("Addr.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" })
2083 test("Addr.Compare", func() {
2084 a := MustParseAddr("1.2.3.4")
2085 b := MustParseAddr("2.3.4.5")
2086 sinkBool = a.Compare(b) == 0
2087 })
2088 test("Addr.Less", func() {
2089 a := MustParseAddr("1.2.3.4")
2090 b := MustParseAddr("2.3.4.5")
2091 sinkBool = a.Less(b)
2092 })
2093 test("Addr.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() })
2094 test("Addr.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() })
2095 test("Addr.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() })
2096 test("Addr.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() })
2097 test("Addr.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") })
2098 test("Addr.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() })
2099 test("Addr.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() })
2100 test("Addr.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() })
2101 test("Addr.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() })
2102 test("Addr.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() })
2103 test("Addr.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() })
2104 test("Addr.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() })
2105 test("Addr.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() })
2106 test("Addr.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) })
2107 test("Addr.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) })
2108 test("Addr.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() })
2109 test("Addr.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() })
2110 test("Addr.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() })
2111 test("Addr.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() })
2112
2113
2114 test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) })
2115 test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) })
2116 test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") })
2117
2118
2119 test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) })
2120 test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) })
2121 test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) })
2122 test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") })
2123
2124
2125 test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) })
2126 test("Prefix.Overlaps", func() {
2127 a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16")
2128 sinkBool = a.Overlaps(b)
2129 })
2130 test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() })
2131 test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() })
2132 test("Prefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() })
2133 }
2134
2135 func TestAddrStringAllocs(t *testing.T) {
2136 tests := []struct {
2137 name string
2138 ip Addr
2139 wantAllocs int
2140 }{
2141 {"zero", Addr{}, 0},
2142 {"ipv4", MustParseAddr("192.168.1.1"), 1},
2143 {"ipv6", MustParseAddr("2001:db8::1"), 1},
2144 {"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1},
2145 {"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1},
2146 {"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1},
2147 }
2148 optimizationOff := testenv.OptimizationOff()
2149 for _, tc := range tests {
2150 t.Run(tc.name, func(t *testing.T) {
2151 if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") {
2152
2153 t.Skipf("skipping on %v", testenv.Builder())
2154 }
2155 allocs := int(testing.AllocsPerRun(1000, func() {
2156 sinkString = tc.ip.String()
2157 }))
2158 if allocs != tc.wantAllocs {
2159 t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs)
2160 }
2161 })
2162 }
2163 }
2164
2165 func TestPrefixString(t *testing.T) {
2166 tests := []struct {
2167 ipp Prefix
2168 want string
2169 }{
2170 {Prefix{}, "invalid Prefix"},
2171 {PrefixFrom(Addr{}, 8), "invalid Prefix"},
2172 {PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"},
2173 }
2174
2175 for _, tt := range tests {
2176 if got := tt.ipp.String(); got != tt.want {
2177 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2178 }
2179 }
2180 }
2181
2182 func TestInvalidAddrPortString(t *testing.T) {
2183 tests := []struct {
2184 ipp AddrPort
2185 want string
2186 }{
2187 {AddrPort{}, "invalid AddrPort"},
2188 {AddrPortFrom(Addr{}, 80), "invalid AddrPort"},
2189 }
2190
2191 for _, tt := range tests {
2192 if got := tt.ipp.String(); got != tt.want {
2193 t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want)
2194 }
2195 }
2196 }
2197
2198 func TestAsSlice(t *testing.T) {
2199 tests := []struct {
2200 in Addr
2201 want []byte
2202 }{
2203 {in: Addr{}, want: nil},
2204 {in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}},
2205 {in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}},
2206 }
2207
2208 for _, test := range tests {
2209 got := test.in.AsSlice()
2210 if !bytes.Equal(got, test.want) {
2211 t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want)
2212 }
2213 }
2214 }
2215
2216 var sink16 [16]byte
2217
2218 func BenchmarkAs16(b *testing.B) {
2219 addr := MustParseAddr("1::10")
2220 for i := 0; i < b.N; i++ {
2221 sink16 = addr.As16()
2222 }
2223 }
2224
View as plain text