1
2
3
4
5
6
7 package netipx
8
9 import (
10 "bytes"
11 "encoding"
12 "flag"
13 "net"
14 "net/netip"
15 "reflect"
16 "testing"
17 )
18
19 type (
20 IPPrefix = netip.Prefix
21 IP = netip.Addr
22 IPPort = netip.AddrPort
23 )
24
25
26 func IPv4(a, b, c, d uint8) IP {
27 return netip.AddrFrom4([4]byte{a, b, c, d})
28 }
29
30 var long = flag.Bool("long", false, "run long tests")
31
32 func TestFromStdIP(t *testing.T) {
33 mustFromStdIPMustPanic := func(std net.IP) (IP, bool) {
34 defer func() {
35 if r := recover(); r == nil {
36 t.Errorf("expected MustFromStdIP(%#v) to panic", std)
37 }
38 }()
39 return MustFromStdIP(std), true
40 }
41
42 tests := []struct {
43 name string
44 fn func(net.IP) (IP, bool)
45 std net.IP
46 want IP
47 }{
48 {
49 name: "v4",
50 fn: FromStdIP,
51 std: []byte{1, 2, 3, 4},
52 want: IPv4(1, 2, 3, 4),
53 },
54 {
55 name: "v6",
56 fn: FromStdIP,
57 std: net.ParseIP("::1"),
58 want: netip.AddrFrom16([...]byte{15: 1}),
59 },
60 {
61 name: "4in6-unmap",
62 fn: FromStdIP,
63 std: net.ParseIP("1.2.3.4"),
64 want: IPv4(1, 2, 3, 4),
65 },
66 {
67 name: "v4-raw",
68 fn: FromStdIPRaw,
69 std: net.ParseIP("1.2.3.4").To4(),
70 want: IPv4(1, 2, 3, 4),
71 },
72 {
73 name: "4in6-raw",
74 fn: FromStdIPRaw,
75 std: net.ParseIP("1.2.3.4"),
76 want: netip.AddrFrom16([...]byte{10: 0xff, 11: 0xff, 12: 1, 13: 2, 14: 3, 15: 4}),
77 },
78 {
79 name: "bad-raw",
80 fn: FromStdIPRaw,
81 std: net.IP{0xff},
82 },
83 {
84 name: "bad-must-panic",
85 fn: mustFromStdIPMustPanic,
86 std: net.IP{0x00, 0x01, 0x02},
87 },
88 }
89 for _, tt := range tests {
90 t.Run(tt.name, func(t *testing.T) {
91 got, ok := tt.fn(tt.std)
92 if got != tt.want {
93 t.Errorf("got (%#v, %v); want %#v", got, ok, tt.want)
94 }
95 })
96 }
97 }
98
99 func TestFromStdAddr(t *testing.T) {
100 tests := []struct {
101 name string
102 std net.IP
103 port int
104 zone string
105 want netip.AddrPort
106 wantOK bool
107 }{
108 {
109 name: "invalid IP",
110 std: net.IP{0xff},
111 },
112 {
113 name: "invalid port",
114 std: net.IP{1, 2, 3, 4},
115 port: -1,
116 },
117 {
118 name: "v4",
119 std: net.IP{1, 2, 3, 4},
120 port: 8080,
121 want: netip.AddrPortFrom(netip.AddrFrom4([...]byte{1, 2, 3, 4}), 8080),
122 wantOK: true,
123 },
124 {
125 name: "v4 with zone",
126 std: net.IP{1, 2, 3, 4},
127 port: 8080,
128 zone: "foobar",
129 },
130 {
131 name: "v6",
132 std: net.ParseIP("fc::"),
133 port: 8080,
134 want: netip.AddrPortFrom(netip.MustParseAddr("fc::"), 8080),
135 wantOK: true,
136 },
137 {
138 name: "v6 with zone",
139 std: net.ParseIP("fc::"),
140 port: 8080,
141 zone: "foobar",
142 want: netip.AddrPortFrom(netip.MustParseAddr("fc::").WithZone("foobar"), 8080),
143 wantOK: true,
144 },
145 }
146 for _, tt := range tests {
147 t.Run(tt.name, func(t *testing.T) {
148 got, ok := FromStdAddr(tt.std, tt.port, tt.zone)
149 if got != tt.want || ok != tt.wantOK {
150 t.Errorf("FromStdAddr(%#v, %d, %q): got (%#v, %v); want (%#v, %t)", tt.std, tt.port, tt.zone, got, ok, tt.want, tt.wantOK)
151 }
152 })
153 }
154 }
155
156 func TestFromStdIPNet(t *testing.T) {
157 tests := []struct {
158 name string
159 std *net.IPNet
160 want IPPrefix
161 }{
162 {
163 name: "invalid IP",
164 std: &net.IPNet{
165 IP: net.IP{0xff},
166 },
167 },
168 {
169 name: "invalid mask",
170 std: &net.IPNet{
171 IP: net.IPv6loopback,
172 Mask: nil,
173 },
174 },
175 {
176 name: "non-contiguous mask",
177 std: &net.IPNet{
178 IP: net.IPv4(192, 0, 2, 0).To4(),
179 Mask: net.IPv4Mask(255, 0, 255, 0),
180 },
181 },
182 {
183 name: "IPv4",
184 std: &net.IPNet{
185 IP: net.IPv4(192, 0, 2, 0).To4(),
186 Mask: net.CIDRMask(24, 32),
187 },
188 want: mustIPPrefix("192.0.2.0/24"),
189 },
190 {
191 name: "IPv6",
192 std: &net.IPNet{
193 IP: net.ParseIP("2001:db8::"),
194 Mask: net.CIDRMask(64, 128),
195 },
196 want: mustIPPrefix("2001:db8::/64"),
197 },
198 }
199 for _, tt := range tests {
200 t.Run(tt.name, func(t *testing.T) {
201 got, ok := FromStdIPNet(tt.std)
202 if !ok && got != (IPPrefix{}) {
203 t.Fatalf("!ok but non-zero result")
204 }
205
206 if got != tt.want {
207 t.Errorf("FromStdIPNet(%q) = %+v; want %+v", tt.std, got, tt.want)
208 }
209 })
210 }
211 }
212
213 func TestAddrIPNet(t *testing.T) {
214 tests := []struct {
215 name string
216 addr netip.Addr
217 want *net.IPNet
218 }{
219 {
220 name: "invalid IP",
221 addr: netip.Addr{},
222 want: &net.IPNet{},
223 },
224 {
225 name: "IPv4",
226 addr: mustIP("127.0.0.1"),
227 want: &net.IPNet{
228 IP: net.IPv4(127, 0, 0, 1).To4(),
229 Mask: net.IPv4Mask(255, 255, 255, 255),
230 },
231 },
232 {
233 name: "IPv6",
234 addr: mustIP("2001:db8::"),
235 want: &net.IPNet{
236 IP: net.ParseIP("2001:db8::"),
237 Mask: net.CIDRMask(128, 128),
238 },
239 },
240 }
241 for _, tt := range tests {
242 t.Run(tt.name, func(t *testing.T) {
243 got := AddrIPNet(tt.addr)
244 if got == nil {
245 t.Fatalf("nil result")
246 } else if !reflect.DeepEqual(got, tt.want) {
247 t.Errorf("AddrIPNet(%q) = %+v; want %+v", tt.addr, got, tt.want)
248 }
249 })
250 }
251 }
252
253 type appendMarshaler interface {
254 encoding.TextMarshaler
255 AppendTo([]byte) []byte
256 }
257
258
259
260 func testAppendToMarshal(t *testing.T, x appendMarshaler) {
261 t.Helper()
262 m, err := x.MarshalText()
263 if err != nil {
264 t.Fatalf("(%v).MarshalText: %v", x, err)
265 }
266 a := make([]byte, 0, len(m))
267 a = x.AppendTo(a)
268 if !bytes.Equal(m, a) {
269 t.Errorf("(%v).MarshalText = %q, (%v).AppendTo = %q", x, m, x, a)
270 }
271 }
272
273 var (
274 mustIP = netip.MustParseAddr
275 mustIPPrefix = netip.MustParsePrefix
276 )
277
278 func mustIPs(strs ...string) []IP {
279 var res []IP
280 for _, s := range strs {
281 res = append(res, mustIP(s))
282 }
283 return res
284 }
285
286 func BenchmarkBinaryMarshalRoundTrip(b *testing.B) {
287 b.ReportAllocs()
288 tests := []struct {
289 name string
290 ip string
291 }{
292 {"ipv4", "1.2.3.4"},
293 {"ipv6", "2001:db8::1"},
294 {"ipv6+zone", "2001:db8::1%eth0"},
295 }
296 for _, tc := range tests {
297 b.Run(tc.name, func(b *testing.B) {
298 ip := mustIP(tc.ip)
299 for i := 0; i < b.N; i++ {
300 bt, err := ip.MarshalBinary()
301 if err != nil {
302 b.Fatal(err)
303 }
304 var ip2 IP
305 if err := ip2.UnmarshalBinary(bt); err != nil {
306 b.Fatal(err)
307 }
308 }
309 })
310 }
311 }
312
313 func BenchmarkStdIPv4(b *testing.B) {
314 b.ReportAllocs()
315 ips := []net.IP{}
316 for i := 0; i < b.N; i++ {
317 ip := net.IPv4(8, 8, 8, 8)
318 ips = ips[:0]
319 for i := 0; i < 100; i++ {
320 ips = append(ips, ip)
321 }
322 }
323 }
324
325 func BenchmarkIPv4(b *testing.B) {
326 b.ReportAllocs()
327 ips := []IP{}
328 for i := 0; i < b.N; i++ {
329 ip := IPv4(8, 8, 8, 8)
330 ips = ips[:0]
331 for i := 0; i < 100; i++ {
332 ips = append(ips, ip)
333 }
334 }
335 }
336
337
338
339
340 type ip4i struct {
341 ip4 [4]byte
342 flags1 byte
343 flags2 byte
344 flags3 byte
345 flags4 byte
346 ipv6 interface{}
347 }
348
349 func newip4i_v4(a, b, c, d byte) ip4i {
350 return ip4i{ip4: [4]byte{a, b, c, d}}
351 }
352
353
354 func BenchmarkIPv4_inline(b *testing.B) {
355 b.ReportAllocs()
356 ips := []ip4i{}
357 for i := 0; i < b.N; i++ {
358 ip := newip4i_v4(8, 8, 8, 8)
359 ips = ips[:0]
360 for i := 0; i < 100; i++ {
361 ips = append(ips, ip)
362 }
363 }
364 }
365
366 func BenchmarkStdIPv6(b *testing.B) {
367 b.ReportAllocs()
368 ips := []net.IP{}
369 for i := 0; i < b.N; i++ {
370 ip := net.ParseIP("2001:db8::1")
371 ips = ips[:0]
372 for i := 0; i < 100; i++ {
373 ips = append(ips, ip)
374 }
375 }
376 }
377
378 func BenchmarkIPv6(b *testing.B) {
379 b.ReportAllocs()
380 ips := []IP{}
381 for i := 0; i < b.N; i++ {
382 ip := mustIP("2001:db8::1")
383 ips = ips[:0]
384 for i := 0; i < 100; i++ {
385 ips = append(ips, ip)
386 }
387 }
388 }
389
390 var parseBenchInputs = []struct {
391 name string
392 ip string
393 }{
394 {"v4", "192.168.1.1"},
395 {"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"},
396 {"v6_ellipsis", "fd7a:115c::626b:430b"},
397 {"v6_v4", "::ffff:192.168.140.255"},
398 {"v6_zone", "1:2::ffff:192.168.140.255%eth1"},
399 }
400
401 func pxv(cidrStrs ...string) (out []IPPrefix) {
402 for _, s := range cidrStrs {
403 out = append(out, mustIPPrefix(s))
404 }
405 return
406 }
407
408 func TestRangePrefixes(t *testing.T) {
409 tests := []struct {
410 from string
411 to string
412 want []IPPrefix
413 }{
414 {"0.0.0.0", "255.255.255.255", pxv("0.0.0.0/0")},
415 {"::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", pxv("::/0")},
416 {"10.0.0.0", "10.255.255.255", pxv("10.0.0.0/8")},
417 {"10.0.0.0", "10.127.255.255", pxv("10.0.0.0/9")},
418 {"0.0.0.4", "0.0.0.11", pxv(
419
420
421
422
423
424
425
426
427 "0.0.0.4/30",
428 "0.0.0.8/30",
429 )},
430 {"10.0.0.0", "11.10.255.255", pxv(
431 "10.0.0.0/8",
432 "11.0.0.0/13",
433 "11.8.0.0/15",
434 "11.10.0.0/16",
435 )},
436 {"1.2.3.5", "5.6.7.8", pxv(
437 "1.2.3.5/32",
438 "1.2.3.6/31",
439 "1.2.3.8/29",
440 "1.2.3.16/28",
441 "1.2.3.32/27",
442 "1.2.3.64/26",
443 "1.2.3.128/25",
444 "1.2.4.0/22",
445 "1.2.8.0/21",
446 "1.2.16.0/20",
447 "1.2.32.0/19",
448 "1.2.64.0/18",
449 "1.2.128.0/17",
450 "1.3.0.0/16",
451 "1.4.0.0/14",
452 "1.8.0.0/13",
453 "1.16.0.0/12",
454 "1.32.0.0/11",
455 "1.64.0.0/10",
456 "1.128.0.0/9",
457 "2.0.0.0/7",
458 "4.0.0.0/8",
459 "5.0.0.0/14",
460 "5.4.0.0/15",
461 "5.6.0.0/22",
462 "5.6.4.0/23",
463 "5.6.6.0/24",
464 "5.6.7.0/29",
465 "5.6.7.8/32",
466 )},
467 }
468 for _, tt := range tests {
469 r := IPRangeFrom(mustIP(tt.from), mustIP(tt.to))
470 got := r.Prefixes()
471 if !reflect.DeepEqual(got, tt.want) {
472 t.Errorf("failed %s->%s. got:", tt.from, tt.to)
473 for _, v := range got {
474 t.Errorf(" %v", v)
475 }
476 t.Error("want:\n")
477 for _, v := range tt.want {
478 t.Errorf(" %v", v)
479 }
480 }
481 }
482 }
483
484 func BenchmarkIPRangePrefixes(b *testing.B) {
485 b.ReportAllocs()
486 buf := make([]IPPrefix, 0, 50)
487 r := IPRange{mustIP("1.2.3.5"), mustIP("5.6.7.8")}
488 for i := 0; i < b.N; i++ {
489 _ = r.AppendPrefixes(buf[:0])
490 }
491 }
492
493 func TestParseIPRange(t *testing.T) {
494 tests := []struct {
495 in string
496 want interface{}
497 }{
498 {"", "no hyphen in range \"\""},
499 {"foo-", `invalid From IP "foo" in range "foo-"`},
500 {"1.2.3.4-foo", `invalid To IP "foo" in range "1.2.3.4-foo"`},
501 {"1.2.3.4-5.6.7.8", IPRange{mustIP("1.2.3.4"), mustIP("5.6.7.8")}},
502 {"1.2.3.4-0.1.2.3", "range 1.2.3.4 to 0.1.2.3 not valid"},
503 {"::1-::5", IPRange{mustIP("::1"), mustIP("::5")}},
504 }
505 for _, tt := range tests {
506 r, err := ParseIPRange(tt.in)
507 var got interface{}
508 if err != nil {
509 got = err.Error()
510 } else {
511 got = r
512 }
513 if got != tt.want {
514 t.Errorf("ParseIPRange(%q) = %v; want %v", tt.in, got, tt.want)
515 }
516 if err == nil {
517 back := r.String()
518 if back != tt.in {
519 t.Errorf("input %q stringifies back as %q", tt.in, back)
520 }
521 }
522
523 var r2 IPRange
524 err = r2.UnmarshalText([]byte(tt.in))
525 if err != nil {
526 got = err.Error()
527 } else {
528 got = r2
529 }
530 if got != tt.want && tt.in != "" {
531 t.Errorf("UnmarshalText(%q) = %v; want %v", tt.in, got, tt.want)
532 }
533
534 testAppendToMarshal(t, r)
535 }
536 }
537
538 func TestIPRangeUnmarshalTextNonZero(t *testing.T) {
539 r := MustParseIPRange("1.2.3.4-5.6.7.8")
540 if err := r.UnmarshalText([]byte("1.2.3.4-5.6.7.8")); err == nil {
541 t.Fatal("unmarshaled into non-empty IPPrefix")
542 }
543 }
544
545 func TestIPRangeContains(t *testing.T) {
546 type rtest struct {
547 ip IP
548 want bool
549 }
550 tests := []struct {
551 r IPRange
552 rtests []rtest
553 }{
554 {
555 IPRangeFrom(mustIP("10.0.0.2"), mustIP("10.0.0.4")),
556 []rtest{
557 {mustIP("10.0.0.1"), false},
558 {mustIP("10.0.0.2"), true},
559 {mustIP("10.0.0.3"), true},
560 {mustIP("10.0.0.4"), true},
561 {mustIP("10.0.0.5"), false},
562 {IP{}, false},
563 {mustIP("::"), false},
564 },
565 },
566 {
567 IPRangeFrom(mustIP("::1"), mustIP("::ffff")),
568 []rtest{
569 {mustIP("::0"), false},
570 {mustIP("::1"), true},
571 {mustIP("::1%z"), false},
572 {mustIP("::ffff"), true},
573 {mustIP("1::"), false},
574 {mustIP("0.0.0.1"), false},
575 {IP{}, false},
576 },
577 },
578 {
579 IPRangeFrom(mustIP("10.0.0.2"), mustIP("::")),
580 []rtest{
581 {mustIP("10.0.0.2"), false},
582 },
583 },
584 {
585 IPRange{},
586 []rtest{
587 {IP{}, false},
588 },
589 },
590 }
591 for _, tt := range tests {
592 for _, rt := range tt.rtests {
593 got := tt.r.Contains(rt.ip)
594 if got != rt.want {
595 t.Errorf("Range(%v).Contains(%v) = %v; want %v", tt.r, rt.ip, got, rt.want)
596 }
597 }
598 }
599 }
600
601 func TestIPRangeOverlaps(t *testing.T) {
602 tests := []struct {
603 r, o IPRange
604 want bool
605 }{
606 {
607 IPRange{},
608 IPRange{},
609 false,
610 },
611 {
612 IPRange{mustIP("10.0.0.1"), mustIP("10.0.0.3")},
613 IPRange{mustIP("10.0.0.3"), mustIP("10.0.0.4")},
614 true,
615 },
616 {
617 IPRange{mustIP("10.0.0.1"), mustIP("10.0.0.3")},
618 IPRange{mustIP("10.0.0.2"), mustIP("10.0.0.4")},
619 true,
620 },
621 {
622 IPRange{mustIP("10.0.0.1"), mustIP("10.0.0.3")},
623 IPRange{mustIP("10.0.0.4"), mustIP("10.0.0.4")},
624 false,
625 },
626 {
627 IPRange{mustIP("10.0.0.1"), mustIP("10.0.0.3")},
628 IPRange{mustIP("10.0.0.0"), mustIP("10.0.0.5")},
629 true,
630 },
631 {
632 IPRange{mustIP("10.0.0.1"), mustIP("10.0.0.3")},
633 IPRange{mustIP("::1"), mustIP("::2")},
634 false,
635 },
636 {
637 IPRange{mustIP("::"), mustIP("ff::")},
638 IPRange{mustIP("cc::1"), mustIP("cc::2")},
639 true,
640 },
641 }
642 for _, tt := range tests {
643 got := tt.r.Overlaps(tt.o)
644 if got != tt.want {
645 t.Errorf("Overlaps(%v, %v) = %v; want %v", tt.r, tt.o, got, tt.want)
646 }
647 got = tt.o.Overlaps(tt.r)
648 if got != tt.want {
649 t.Errorf("Overlaps(%v, %v) (reversed) = %v; want %v", tt.o, tt.r, got, tt.want)
650 }
651 }
652 }
653
654 func TestIPRangeValid(t *testing.T) {
655 tests := []struct {
656 r IPRange
657 want bool
658 }{
659 {IPRange{mustIP("10.0.0.0"), mustIP("10.0.0.255")}, true},
660 {IPRange{mustIP("::1"), mustIP("::2")}, true},
661 {IPRange{mustIP("::1%foo"), mustIP("::2%foo")}, true},
662
663 {IPRange{mustIP("::1%foo"), mustIP("::2%bar")}, false},
664 {IPRange{IP{}, IP{}}, false},
665 {IPRange{mustIP("::2"), mustIP("::1")}, false},
666 {IPRange{mustIP("1.2.3.4"), mustIP("::1")}, false},
667 }
668 for _, tt := range tests {
669 got := tt.r.IsValid()
670 if got != tt.want {
671 t.Errorf("range %v to %v Valid = %v; want %v", tt.r.From(), tt.r.To(), got, tt.want)
672 }
673 }
674 }
675
676 func TestIPRangePrefix(t *testing.T) {
677 tests := []struct {
678 r IPRange
679 want IPPrefix
680 }{
681 {IPRange{mustIP("10.0.0.0"), mustIP("10.0.0.255")}, mustIPPrefix("10.0.0.0/24")},
682 {IPRange{mustIP("10.0.0.0"), mustIP("10.0.0.254")}, IPPrefix{}},
683 {IPRange{mustIP("fc00::"), AddrPrior(mustIP("fe00::"))}, mustIPPrefix("fc00::/7")},
684 }
685 for _, tt := range tests {
686 got, ok := tt.r.Prefix()
687 if ok != (got != IPPrefix{}) {
688 t.Errorf("for %v, Prefix() results inconsistent: %v, %v", tt.r, got, ok)
689 }
690 if got != tt.want {
691 t.Errorf("for %v, Prefix = %v; want %v", tt.r, got, tt.want)
692 }
693 }
694
695 allocs := int(testing.AllocsPerRun(1000, func() {
696 tt := tests[0]
697 if _, ok := tt.r.Prefix(); !ok {
698 t.Fatal("expected okay")
699 }
700 }))
701 if allocs != 0 {
702 t.Errorf("allocs = %v", allocs)
703 }
704 }
705
706 func BenchmarkIPRangePrefix(b *testing.B) {
707 b.ReportAllocs()
708 r := IPRange{mustIP("10.0.0.0"), mustIP("10.0.0.255")}
709 for i := 0; i < b.N; i++ {
710 if _, ok := r.Prefix(); !ok {
711 b.Fatal("expected a prefix")
712 }
713 }
714 }
715
716 var nextPriorTests = []struct {
717 ip IP
718 next IP
719 prior IP
720 }{
721 {mustIP("10.0.0.1"), mustIP("10.0.0.2"), mustIP("10.0.0.0")},
722 {mustIP("10.0.0.255"), mustIP("10.0.1.0"), mustIP("10.0.0.254")},
723 {mustIP("127.0.0.1"), mustIP("127.0.0.2"), mustIP("127.0.0.0")},
724 {mustIP("254.255.255.255"), mustIP("255.0.0.0"), mustIP("254.255.255.254")},
725 {mustIP("255.255.255.255"), IP{}, mustIP("255.255.255.254")},
726 {mustIP("0.0.0.0"), mustIP("0.0.0.1"), IP{}},
727 {mustIP("::"), mustIP("::1"), IP{}},
728 {mustIP("::%x"), mustIP("::1%x"), IP{}},
729 {mustIP("::1"), mustIP("::2"), mustIP("::")},
730 {mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), IP{}, mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")},
731 }
732
733 func TestAddrNextPrior(t *testing.T) {
734 doNextPrior(t)
735
736 for _, ip := range []IP{
737 mustIP("0.0.0.0"),
738 mustIP("::"),
739 } {
740 got := AddrPrior(ip)
741 if got.IsValid() {
742 t.Errorf("IP(%v).Prior = %v; want zero", ip, got)
743 }
744 }
745
746 var allFF [16]byte
747 for i := range allFF {
748 allFF[i] = 0xff
749 }
750
751 for _, ip := range []IP{
752 mustIP("255.255.255.255"),
753 netip.AddrFrom16(allFF),
754 } {
755 got := ip.Next()
756 if got.IsValid() {
757 t.Errorf("IP(%v).Next = %v; want zero", ip, got)
758 }
759 }
760 }
761
762 func BenchmarkIPNextPrior(b *testing.B) {
763 for i := 0; i < b.N; i++ {
764 doNextPrior(b)
765 }
766 }
767
768 func doNextPrior(t testing.TB) {
769 for _, tt := range nextPriorTests {
770 gnext, gprior := AddrNext(tt.ip), AddrPrior(tt.ip)
771 if gnext != tt.next {
772 t.Errorf("IP(%v).Next = %v; want %v", tt.ip, gnext, tt.next)
773 }
774 if gprior != tt.prior {
775 t.Errorf("IP(%v).Prior = %v; want %v", tt.ip, gprior, tt.prior)
776 }
777 if AddrNext(tt.ip).IsValid() && AddrPrior(AddrNext(tt.ip)) != tt.ip {
778 t.Errorf("IP(%v).Next.Prior = %v; want %v", tt.ip, AddrPrior(AddrNext(tt.ip)), tt.ip)
779 }
780 if AddrPrior(tt.ip).IsValid() && AddrNext(AddrPrior(tt.ip)) != tt.ip {
781 t.Errorf("IP(%v).Prior.Next = %v; want %v", tt.ip, AddrNext(AddrPrior(tt.ip)), tt.ip)
782 }
783 }
784 }
785
786
787
788
789
790
791
792 var (
793 sinkIP IP
794 sinkStdIP net.IP
795 sinkIPPort IPPort
796 sinkIPPrefix IPPrefix
797 sinkIPPrefixSlice []IPPrefix
798 sinkIPRange IPRange
799 sinkIP16 [16]byte
800 sinkIP4 [4]byte
801 sinkBool bool
802 sinkString string
803 sinkBytes []byte
804 sinkUDPAddr = &net.UDPAddr{IP: make(net.IP, 0, 16)}
805 )
806
807 func TestNoAllocs(t *testing.T) {
808
809
810 panicIPOK := func(ip IP, ok bool) IP {
811 if !ok {
812 panic("not ok")
813 }
814 return ip
815 }
816 panicPfxOK := func(pfx IPPrefix, ok bool) IPPrefix {
817 if !ok {
818 panic("not ok")
819 }
820 return pfx
821 }
822 panicIPPOK := func(ipp IPPort, ok bool) IPPort {
823 if !ok {
824 panic("not ok")
825 }
826 return ipp
827 }
828
829 test := func(name string, f func()) {
830 t.Run(name, func(t *testing.T) {
831 n := testing.AllocsPerRun(1000, f)
832 if n != 0 {
833 t.Fatalf("allocs = %d; want 0", int(n))
834 }
835 })
836 }
837
838
839 test("FromStdIP", func() { sinkIP = panicIPOK(FromStdIP(net.IP([]byte{1, 2, 3, 4}))) })
840 test("FromStdIPRaw", func() { sinkIP = panicIPOK(FromStdIPRaw(net.IP([]byte{1, 2, 3, 4}))) })
841
842
843 test("FromStdAddr", func() {
844 std := net.IP{1, 2, 3, 4}
845 sinkIPPort = panicIPPOK(FromStdAddr(std, 5678, ""))
846 })
847
848
849 test("FromStdIPNet", func() {
850 std := &net.IPNet{
851 IP: net.IP{1, 2, 3, 4},
852 Mask: net.IPMask{255, 255, 0, 0},
853 }
854 sinkIPPrefix = panicPfxOK(FromStdIPNet(std))
855 })
856
857
858 test("IPRangeFrom", func() { sinkIPRange = IPRangeFrom(IPv4(1, 2, 3, 4), IPv4(4, 3, 2, 1)) })
859 test("ParseIPRange", func() { sinkIPRange = MustParseIPRange("1.2.3.0-1.2.4.150") })
860
861
862 test("IPRange.IsZero", func() { sinkBool = MustParseIPRange("1.2.3.0-1.2.4.150").IsZero() })
863 test("IPRange.IsValid", func() { sinkBool = MustParseIPRange("1.2.3.0-1.2.4.150").IsValid() })
864 test("IPRange.Overlaps", func() {
865 a := MustParseIPRange("1.2.3.0-1.2.3.150")
866 b := MustParseIPRange("1.2.4.0-1.2.4.255")
867 sinkBool = a.Overlaps(b)
868 })
869 test("IPRange.Prefix", func() {
870 a := MustParseIPRange("1.2.3.0-1.2.3.255")
871 sinkIPPrefix = panicPfxOK(a.Prefix())
872 })
873 }
874
View as plain text