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