github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/net/netip/netip_pkg_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 6 7 import ( 8 "bytes" 9 "encoding" 10 "encoding/json" 11 "strings" 12 "testing" 13 ) 14 15 var ( 16 mustPrefix = MustParsePrefix 17 mustIP = MustParseAddr 18 ) 19 20 func TestPrefixValid(t *testing.T) { 21 v4 := MustParseAddr("1.2.3.4") 22 v6 := MustParseAddr("::1") 23 tests := []struct { 24 ipp Prefix 25 want bool 26 }{ 27 {PrefixFrom(v4, -2), false}, 28 {PrefixFrom(v4, -1), false}, 29 {PrefixFrom(v4, 0), true}, 30 {PrefixFrom(v4, 32), true}, 31 {PrefixFrom(v4, 33), false}, 32 33 {PrefixFrom(v6, -2), false}, 34 {PrefixFrom(v6, -1), false}, 35 {PrefixFrom(v6, 0), true}, 36 {PrefixFrom(v6, 32), true}, 37 {PrefixFrom(v6, 128), true}, 38 {PrefixFrom(v6, 129), false}, 39 40 {PrefixFrom(Addr{}, -2), false}, 41 {PrefixFrom(Addr{}, -1), false}, 42 {PrefixFrom(Addr{}, 0), false}, 43 {PrefixFrom(Addr{}, 32), false}, 44 {PrefixFrom(Addr{}, 128), false}, 45 } 46 for _, tt := range tests { 47 got := tt.ipp.IsValid() 48 if got != tt.want { 49 t.Errorf("(%v).IsValid() = %v want %v", tt.ipp, got, tt.want) 50 } 51 52 // Test that there is only one invalid Prefix representation per Addr. 53 invalid := PrefixFrom(tt.ipp.Addr(), -1) 54 if !got && tt.ipp != invalid { 55 t.Errorf("(%v == %v) = false, want true", tt.ipp, invalid) 56 } 57 } 58 } 59 60 var nextPrevTests = []struct { 61 ip Addr 62 next Addr 63 prev Addr 64 }{ 65 {mustIP("10.0.0.1"), mustIP("10.0.0.2"), mustIP("10.0.0.0")}, 66 {mustIP("10.0.0.255"), mustIP("10.0.1.0"), mustIP("10.0.0.254")}, 67 {mustIP("127.0.0.1"), mustIP("127.0.0.2"), mustIP("127.0.0.0")}, 68 {mustIP("254.255.255.255"), mustIP("255.0.0.0"), mustIP("254.255.255.254")}, 69 {mustIP("255.255.255.255"), Addr{}, mustIP("255.255.255.254")}, 70 {mustIP("0.0.0.0"), mustIP("0.0.0.1"), Addr{}}, 71 {mustIP("::"), mustIP("::1"), Addr{}}, 72 {mustIP("::%x"), mustIP("::1%x"), Addr{}}, 73 {mustIP("::1"), mustIP("::2"), mustIP("::")}, 74 {mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), Addr{}, mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")}, 75 } 76 77 func TestIPNextPrev(t *testing.T) { 78 doNextPrev(t) 79 80 for _, ip := range []Addr{ 81 mustIP("0.0.0.0"), 82 mustIP("::"), 83 } { 84 got := ip.Prev() 85 if !got.isZero() { 86 t.Errorf("IP(%v).Prev = %v; want zero", ip, got) 87 } 88 } 89 90 var allFF [16]byte 91 for i := range allFF { 92 allFF[i] = 0xff 93 } 94 95 for _, ip := range []Addr{ 96 mustIP("255.255.255.255"), 97 AddrFrom16(allFF), 98 } { 99 got := ip.Next() 100 if !got.isZero() { 101 t.Errorf("IP(%v).Next = %v; want zero", ip, got) 102 } 103 } 104 } 105 106 func BenchmarkIPNextPrev(b *testing.B) { 107 for i := 0; i < b.N; i++ { 108 doNextPrev(b) 109 } 110 } 111 112 func doNextPrev(t testing.TB) { 113 for _, tt := range nextPrevTests { 114 gnext, gprev := tt.ip.Next(), tt.ip.Prev() 115 if gnext != tt.next { 116 t.Errorf("IP(%v).Next = %v; want %v", tt.ip, gnext, tt.next) 117 } 118 if gprev != tt.prev { 119 t.Errorf("IP(%v).Prev = %v; want %v", tt.ip, gprev, tt.prev) 120 } 121 if !tt.ip.Next().isZero() && tt.ip.Next().Prev() != tt.ip { 122 t.Errorf("IP(%v).Next.Prev = %v; want %v", tt.ip, tt.ip.Next().Prev(), tt.ip) 123 } 124 if !tt.ip.Prev().isZero() && tt.ip.Prev().Next() != tt.ip { 125 t.Errorf("IP(%v).Prev.Next = %v; want %v", tt.ip, tt.ip.Prev().Next(), tt.ip) 126 } 127 } 128 } 129 130 func TestIPBitLen(t *testing.T) { 131 tests := []struct { 132 ip Addr 133 want int 134 }{ 135 {Addr{}, 0}, 136 {mustIP("0.0.0.0"), 32}, 137 {mustIP("10.0.0.1"), 32}, 138 {mustIP("::"), 128}, 139 {mustIP("fed0::1"), 128}, 140 {mustIP("::ffff:10.0.0.1"), 128}, 141 } 142 for _, tt := range tests { 143 got := tt.ip.BitLen() 144 if got != tt.want { 145 t.Errorf("BitLen(%v) = %d; want %d", tt.ip, got, tt.want) 146 } 147 } 148 } 149 150 func TestPrefixContains(t *testing.T) { 151 tests := []struct { 152 ipp Prefix 153 ip Addr 154 want bool 155 }{ 156 {mustPrefix("9.8.7.6/0"), mustIP("9.8.7.6"), true}, 157 {mustPrefix("9.8.7.6/16"), mustIP("9.8.7.6"), true}, 158 {mustPrefix("9.8.7.6/16"), mustIP("9.8.6.4"), true}, 159 {mustPrefix("9.8.7.6/16"), mustIP("9.9.7.6"), false}, 160 {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.6"), true}, 161 {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false}, 162 {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false}, 163 {mustPrefix("::1/0"), mustIP("::1"), true}, 164 {mustPrefix("::1/0"), mustIP("::2"), true}, 165 {mustPrefix("::1/127"), mustIP("::1"), true}, 166 {mustPrefix("::1/127"), mustIP("::2"), false}, 167 {mustPrefix("::1/128"), mustIP("::1"), true}, 168 {mustPrefix("::1/127"), mustIP("::2"), false}, 169 // Zones ignored: https://go.dev/issue/51899 170 {Prefix{mustIP("1.2.3.4").WithZone("a"), 32}, mustIP("1.2.3.4"), true}, 171 {Prefix{mustIP("::1").WithZone("a"), 128}, mustIP("::1"), true}, 172 // invalid IP 173 {mustPrefix("::1/0"), Addr{}, false}, 174 {mustPrefix("1.2.3.4/0"), Addr{}, false}, 175 // invalid Prefix 176 {PrefixFrom(mustIP("::1"), 129), mustIP("::1"), false}, 177 {PrefixFrom(mustIP("1.2.3.4"), 33), mustIP("1.2.3.4"), false}, 178 {PrefixFrom(Addr{}, 0), mustIP("1.2.3.4"), false}, 179 {PrefixFrom(Addr{}, 32), mustIP("1.2.3.4"), false}, 180 {PrefixFrom(Addr{}, 128), mustIP("::1"), false}, 181 // wrong IP family 182 {mustPrefix("::1/0"), mustIP("1.2.3.4"), false}, 183 {mustPrefix("1.2.3.4/0"), mustIP("::1"), false}, 184 } 185 for _, tt := range tests { 186 got := tt.ipp.Contains(tt.ip) 187 if got != tt.want { 188 t.Errorf("(%v).Contains(%v) = %v want %v", tt.ipp, tt.ip, got, tt.want) 189 } 190 } 191 } 192 193 func TestParseIPError(t *testing.T) { 194 tests := []struct { 195 ip string 196 errstr string 197 }{ 198 { 199 ip: "localhost", 200 }, 201 { 202 ip: "500.0.0.1", 203 errstr: "field has value >255", 204 }, 205 { 206 ip: "::gggg%eth0", 207 errstr: "must have at least one digit", 208 }, 209 { 210 ip: "fe80::1cc0:3e8c:119f:c2e1%", 211 errstr: "zone must be a non-empty string", 212 }, 213 { 214 ip: "%eth0", 215 errstr: "missing IPv6 address", 216 }, 217 } 218 for _, test := range tests { 219 t.Run(test.ip, func(t *testing.T) { 220 _, err := ParseAddr(test.ip) 221 if err == nil { 222 t.Fatal("no error") 223 } 224 if _, ok := err.(parseAddrError); !ok { 225 t.Errorf("error type is %T, want parseIPError", err) 226 } 227 if test.errstr == "" { 228 test.errstr = "unable to parse IP" 229 } 230 if got := err.Error(); !strings.Contains(got, test.errstr) { 231 t.Errorf("error is missing substring %q: %s", test.errstr, got) 232 } 233 }) 234 } 235 } 236 237 func TestParseAddrPort(t *testing.T) { 238 tests := []struct { 239 in string 240 want AddrPort 241 wantErr bool 242 }{ 243 {in: "1.2.3.4:1234", want: AddrPort{mustIP("1.2.3.4"), 1234}}, 244 {in: "1.1.1.1:123456", wantErr: true}, 245 {in: "1.1.1.1:-123", wantErr: true}, 246 {in: "[::1]:1234", want: AddrPort{mustIP("::1"), 1234}}, 247 {in: "[1.2.3.4]:1234", wantErr: true}, 248 {in: "fe80::1:1234", wantErr: true}, 249 {in: ":0", wantErr: true}, // if we need to parse this form, there should be a separate function that explicitly allows it 250 } 251 for _, test := range tests { 252 t.Run(test.in, func(t *testing.T) { 253 got, err := ParseAddrPort(test.in) 254 if err != nil { 255 if test.wantErr { 256 return 257 } 258 t.Fatal(err) 259 } 260 if got != test.want { 261 t.Errorf("got %v; want %v", got, test.want) 262 } 263 if got.String() != test.in { 264 t.Errorf("String = %q; want %q", got.String(), test.in) 265 } 266 }) 267 268 t.Run(test.in+"/AppendTo", func(t *testing.T) { 269 got, err := ParseAddrPort(test.in) 270 if err == nil { 271 testAppendToMarshal(t, got) 272 } 273 }) 274 275 // TextMarshal and TextUnmarshal mostly behave like 276 // ParseAddrPort and String. Divergent behavior are handled in 277 // TestAddrPortMarshalUnmarshal. 278 t.Run(test.in+"/Marshal", func(t *testing.T) { 279 var got AddrPort 280 jsin := `"` + test.in + `"` 281 err := json.Unmarshal([]byte(jsin), &got) 282 if err != nil { 283 if test.wantErr { 284 return 285 } 286 t.Fatal(err) 287 } 288 if got != test.want { 289 t.Errorf("got %v; want %v", got, test.want) 290 } 291 gotb, err := json.Marshal(got) 292 if err != nil { 293 t.Fatal(err) 294 } 295 if string(gotb) != jsin { 296 t.Errorf("Marshal = %q; want %q", string(gotb), jsin) 297 } 298 }) 299 } 300 } 301 302 func TestAddrPortMarshalUnmarshal(t *testing.T) { 303 tests := []struct { 304 in string 305 want AddrPort 306 }{ 307 {"", AddrPort{}}, 308 } 309 310 for _, test := range tests { 311 t.Run(test.in, func(t *testing.T) { 312 orig := `"` + test.in + `"` 313 314 var ipp AddrPort 315 if err := json.Unmarshal([]byte(orig), &ipp); err != nil { 316 t.Fatalf("failed to unmarshal: %v", err) 317 } 318 319 ippb, err := json.Marshal(ipp) 320 if err != nil { 321 t.Fatalf("failed to marshal: %v", err) 322 } 323 324 back := string(ippb) 325 if orig != back { 326 t.Errorf("Marshal = %q; want %q", back, orig) 327 } 328 329 testAppendToMarshal(t, ipp) 330 }) 331 } 332 } 333 334 type appendMarshaler interface { 335 encoding.TextMarshaler 336 AppendTo([]byte) []byte 337 } 338 339 // testAppendToMarshal tests that x's AppendTo and MarshalText methods yield the same results. 340 // x's MarshalText method must not return an error. 341 func testAppendToMarshal(t *testing.T, x appendMarshaler) { 342 t.Helper() 343 m, err := x.MarshalText() 344 if err != nil { 345 t.Fatalf("(%v).MarshalText: %v", x, err) 346 } 347 a := make([]byte, 0, len(m)) 348 a = x.AppendTo(a) 349 if !bytes.Equal(m, a) { 350 t.Errorf("(%v).MarshalText = %q, (%v).AppendTo = %q", x, m, x, a) 351 } 352 } 353 354 func TestIPv6Accessor(t *testing.T) { 355 var a [16]byte 356 for i := range a { 357 a[i] = uint8(i) + 1 358 } 359 ip := AddrFrom16(a) 360 for i := range a { 361 if got, want := ip.v6(uint8(i)), uint8(i)+1; got != want { 362 t.Errorf("v6(%v) = %v; want %v", i, got, want) 363 } 364 } 365 }