1 // Copyright 2013 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 net 6 7 import ( 8 "reflect" 9 "testing" 10 ) 11 12 var testInetaddr = func(ip IPAddr) Addr { return &TCPAddr{IP: ip.IP, Port: 5682, Zone: ip.Zone} } 13 14 var addrListTests = []struct { 15 filter func(IPAddr) bool 16 ips []IPAddr 17 inetaddr func(IPAddr) Addr 18 first Addr 19 primaries addrList 20 fallbacks addrList 21 err error 22 }{ 23 { 24 nil, 25 []IPAddr{ 26 {IP: IPv4(127, 0, 0, 1)}, 27 {IP: IPv6loopback}, 28 }, 29 testInetaddr, 30 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 31 addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}}, 32 addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}}, 33 nil, 34 }, 35 { 36 nil, 37 []IPAddr{ 38 {IP: IPv6loopback}, 39 {IP: IPv4(127, 0, 0, 1)}, 40 }, 41 testInetaddr, 42 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 43 addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}}, 44 addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}}, 45 nil, 46 }, 47 { 48 nil, 49 []IPAddr{ 50 {IP: IPv4(127, 0, 0, 1)}, 51 {IP: IPv4(192, 168, 0, 1)}, 52 }, 53 testInetaddr, 54 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 55 addrList{ 56 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 57 &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682}, 58 }, 59 nil, 60 nil, 61 }, 62 { 63 nil, 64 []IPAddr{ 65 {IP: IPv6loopback}, 66 {IP: ParseIP("fe80::1"), Zone: "eth0"}, 67 }, 68 testInetaddr, 69 &TCPAddr{IP: IPv6loopback, Port: 5682}, 70 addrList{ 71 &TCPAddr{IP: IPv6loopback, Port: 5682}, 72 &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"}, 73 }, 74 nil, 75 nil, 76 }, 77 { 78 nil, 79 []IPAddr{ 80 {IP: IPv4(127, 0, 0, 1)}, 81 {IP: IPv4(192, 168, 0, 1)}, 82 {IP: IPv6loopback}, 83 {IP: ParseIP("fe80::1"), Zone: "eth0"}, 84 }, 85 testInetaddr, 86 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 87 addrList{ 88 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 89 &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682}, 90 }, 91 addrList{ 92 &TCPAddr{IP: IPv6loopback, Port: 5682}, 93 &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"}, 94 }, 95 nil, 96 }, 97 { 98 nil, 99 []IPAddr{ 100 {IP: IPv6loopback}, 101 {IP: ParseIP("fe80::1"), Zone: "eth0"}, 102 {IP: IPv4(127, 0, 0, 1)}, 103 {IP: IPv4(192, 168, 0, 1)}, 104 }, 105 testInetaddr, 106 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 107 addrList{ 108 &TCPAddr{IP: IPv6loopback, Port: 5682}, 109 &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"}, 110 }, 111 addrList{ 112 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 113 &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682}, 114 }, 115 nil, 116 }, 117 { 118 nil, 119 []IPAddr{ 120 {IP: IPv4(127, 0, 0, 1)}, 121 {IP: IPv6loopback}, 122 {IP: IPv4(192, 168, 0, 1)}, 123 {IP: ParseIP("fe80::1"), Zone: "eth0"}, 124 }, 125 testInetaddr, 126 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 127 addrList{ 128 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 129 &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682}, 130 }, 131 addrList{ 132 &TCPAddr{IP: IPv6loopback, Port: 5682}, 133 &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"}, 134 }, 135 nil, 136 }, 137 { 138 nil, 139 []IPAddr{ 140 {IP: IPv6loopback}, 141 {IP: IPv4(127, 0, 0, 1)}, 142 {IP: ParseIP("fe80::1"), Zone: "eth0"}, 143 {IP: IPv4(192, 168, 0, 1)}, 144 }, 145 testInetaddr, 146 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 147 addrList{ 148 &TCPAddr{IP: IPv6loopback, Port: 5682}, 149 &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"}, 150 }, 151 addrList{ 152 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 153 &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682}, 154 }, 155 nil, 156 }, 157 158 { 159 ipv4only, 160 []IPAddr{ 161 {IP: IPv4(127, 0, 0, 1)}, 162 {IP: IPv6loopback}, 163 }, 164 testInetaddr, 165 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 166 addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}}, 167 nil, 168 nil, 169 }, 170 { 171 ipv4only, 172 []IPAddr{ 173 {IP: IPv6loopback}, 174 {IP: IPv4(127, 0, 0, 1)}, 175 }, 176 testInetaddr, 177 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, 178 addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}}, 179 nil, 180 nil, 181 }, 182 183 { 184 ipv6only, 185 []IPAddr{ 186 {IP: IPv4(127, 0, 0, 1)}, 187 {IP: IPv6loopback}, 188 }, 189 testInetaddr, 190 &TCPAddr{IP: IPv6loopback, Port: 5682}, 191 addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}}, 192 nil, 193 nil, 194 }, 195 { 196 ipv6only, 197 []IPAddr{ 198 {IP: IPv6loopback}, 199 {IP: IPv4(127, 0, 0, 1)}, 200 }, 201 testInetaddr, 202 &TCPAddr{IP: IPv6loopback, Port: 5682}, 203 addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}}, 204 nil, 205 nil, 206 }, 207 208 {nil, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress}, 209 210 {ipv4only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress}, 211 {ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, nil, nil, errNoSuitableAddress}, 212 213 {ipv6only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress}, 214 {ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, nil, nil, errNoSuitableAddress}, 215 } 216 217 func TestAddrList(t *testing.T) { 218 if !supportsIPv4 || !supportsIPv6 { 219 t.Skip("both IPv4 and IPv6 are required") 220 } 221 222 for i, tt := range addrListTests { 223 addrs, err := filterAddrList(tt.filter, tt.ips, tt.inetaddr) 224 if err != tt.err { 225 t.Errorf("#%v: got %v; want %v", i, err, tt.err) 226 } 227 if tt.err != nil { 228 if len(addrs) != 0 { 229 t.Errorf("#%v: got %v; want 0", i, len(addrs)) 230 } 231 continue 232 } 233 first := addrs.first(isIPv4) 234 if !reflect.DeepEqual(first, tt.first) { 235 t.Errorf("#%v: got %v; want %v", i, first, tt.first) 236 } 237 primaries, fallbacks := addrs.partition(isIPv4) 238 if !reflect.DeepEqual(primaries, tt.primaries) { 239 t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries) 240 } 241 if !reflect.DeepEqual(fallbacks, tt.fallbacks) { 242 t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks) 243 } 244 expectedLen := len(primaries) + len(fallbacks) 245 if len(addrs) != expectedLen { 246 t.Errorf("#%v: got %v; want %v", i, len(addrs), expectedLen) 247 } 248 } 249 } 250 251 func TestAddrListPartition(t *testing.T) { 252 addrs := addrList{ 253 &IPAddr{IP: ParseIP("fe80::"), Zone: "eth0"}, 254 &IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"}, 255 &IPAddr{IP: ParseIP("fe80::2"), Zone: "eth0"}, 256 } 257 cases := []struct { 258 lastByte byte 259 primaries addrList 260 fallbacks addrList 261 }{ 262 {0, addrList{addrs[0]}, addrList{addrs[1], addrs[2]}}, 263 {1, addrList{addrs[0], addrs[2]}, addrList{addrs[1]}}, 264 {2, addrList{addrs[0], addrs[1]}, addrList{addrs[2]}}, 265 {3, addrList{addrs[0], addrs[1], addrs[2]}, nil}, 266 } 267 for i, tt := range cases { 268 // Inverting the function's output should not affect the outcome. 269 for _, invert := range []bool{false, true} { 270 primaries, fallbacks := addrs.partition(func(a Addr) bool { 271 ip := a.(*IPAddr).IP 272 return (ip[len(ip)-1] == tt.lastByte) != invert 273 }) 274 if !reflect.DeepEqual(primaries, tt.primaries) { 275 t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries) 276 } 277 if !reflect.DeepEqual(fallbacks, tt.fallbacks) { 278 t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks) 279 } 280 } 281 } 282 } 283