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