Source file src/net/netip/netip_pkg_test.go

     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  }
   366  

View as plain text