istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/model/config_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package model_test 16 17 import ( 18 "fmt" 19 "reflect" 20 "strconv" 21 "testing" 22 23 "github.com/davecgh/go-spew/spew" 24 25 "istio.io/istio/pilot/pkg/model" 26 mock_config "istio.io/istio/pilot/test/mock" 27 "istio.io/istio/pkg/config" 28 "istio.io/istio/pkg/config/host" 29 "istio.io/istio/pkg/config/labels" 30 "istio.io/istio/pkg/config/protocol" 31 "istio.io/istio/pkg/config/schema/collection" 32 "istio.io/istio/pkg/config/schema/kind" 33 "istio.io/istio/pkg/config/schema/resource" 34 "istio.io/istio/pkg/util/sets" 35 ) 36 37 // getByMessageName finds a schema by message name if it is available 38 // In test setup, we do not have more than one descriptor with the same message type, so this 39 // function is ok for testing purpose. 40 func getByMessageName(schemas collection.Schemas, name string) (resource.Schema, bool) { 41 for _, s := range schemas.All() { 42 if s.Proto() == name { 43 return s, true 44 } 45 } 46 return nil, false 47 } 48 49 func schemaFor(kind, proto string) resource.Schema { 50 return resource.Builder{ 51 Kind: kind, 52 Plural: kind + "s", 53 Proto: proto, 54 }.BuildNoValidate() 55 } 56 57 func TestConfigDescriptor(t *testing.T) { 58 a := schemaFor("a", "proxy.A") 59 schemas := collection.SchemasFor( 60 a, 61 schemaFor("b", "proxy.B"), 62 schemaFor("c", "proxy.C")) 63 want := []string{"a", "b", "c"} 64 got := schemas.Kinds() 65 if !reflect.DeepEqual(got, want) { 66 t.Errorf("descriptor.Types() => got %+vwant %+v", spew.Sdump(got), spew.Sdump(want)) 67 } 68 69 aType, aExists := schemas.FindByGroupVersionKind(a.GroupVersionKind()) 70 if !aExists || !reflect.DeepEqual(aType, a) { 71 t.Errorf("descriptor.GetByType(a) => got %+v, want %+v", aType, a) 72 } 73 if _, exists := schemas.FindByGroupVersionKind(config.GroupVersionKind{Kind: "missing"}); exists { 74 t.Error("descriptor.GetByType(missing) => got true, want false") 75 } 76 77 aSchema, aSchemaExists := getByMessageName(schemas, a.Proto()) 78 if !aSchemaExists || !reflect.DeepEqual(aSchema, a) { 79 t.Errorf("descriptor.GetByMessageName(a) => got %+v, want %+v", aType, a) 80 } 81 _, aSchemaNotExist := getByMessageName(schemas, "blah") 82 if aSchemaNotExist { 83 t.Errorf("descriptor.GetByMessageName(blah) => got true, want false") 84 } 85 } 86 87 func TestEventString(t *testing.T) { 88 cases := []struct { 89 in model.Event 90 want string 91 }{ 92 {model.EventAdd, "add"}, 93 {model.EventUpdate, "update"}, 94 {model.EventDelete, "delete"}, 95 } 96 for _, c := range cases { 97 if got := c.in.String(); got != c.want { 98 t.Errorf("Failed: got %q want %q", got, c.want) 99 } 100 } 101 } 102 103 func TestPortList(t *testing.T) { 104 pl := model.PortList{ 105 {Name: "http", Port: 80, Protocol: protocol.HTTP}, 106 {Name: "http-alt", Port: 8080, Protocol: protocol.HTTP}, 107 } 108 109 gotNames := pl.GetNames() 110 wantNames := []string{"http", "http-alt"} 111 if !reflect.DeepEqual(gotNames, wantNames) { 112 t.Errorf("GetNames() failed: got %v want %v", gotNames, wantNames) 113 } 114 115 cases := []struct { 116 name string 117 port *model.Port 118 found bool 119 }{ 120 {name: pl[0].Name, port: pl[0], found: true}, 121 {name: "foobar", found: false}, 122 } 123 124 for _, c := range cases { 125 gotPort, gotFound := pl.Get(c.name) 126 if c.found != gotFound || !reflect.DeepEqual(gotPort, c.port) { 127 t.Errorf("Get() failed: gotFound=%v wantFound=%v\ngot %+vwant %+v", 128 gotFound, c.found, spew.Sdump(gotPort), spew.Sdump(c.port)) 129 } 130 } 131 } 132 133 func TestSubsetKey(t *testing.T) { 134 hostname := host.Name("hostname") 135 cases := []struct { 136 hostname host.Name 137 subset string 138 port int 139 want string 140 }{ 141 { 142 hostname: "hostname", 143 subset: "subset", 144 port: 80, 145 want: "outbound|80|subset|hostname", 146 }, 147 { 148 hostname: "hostname", 149 subset: "", 150 port: 80, 151 want: "outbound|80||hostname", 152 }, 153 } 154 155 for _, c := range cases { 156 got := model.BuildSubsetKey(model.TrafficDirectionOutbound, c.subset, hostname, c.port) 157 if got != c.want { 158 t.Errorf("Failed: got %q want %q", got, c.want) 159 } 160 161 // test parse subset key. ParseSubsetKey is the inverse of BuildSubsetKey 162 _, s, h, p := model.ParseSubsetKey(got) 163 if s != c.subset || h != c.hostname || p != c.port { 164 t.Errorf("Failed: got %s,%s,%d want %s,%s,%d", s, h, p, c.subset, c.hostname, c.port) 165 } 166 } 167 } 168 169 func TestLabelsEquals(t *testing.T) { 170 cases := []struct { 171 a, b labels.Instance 172 want bool 173 }{ 174 { 175 a: nil, 176 b: nil, 177 want: true, 178 }, 179 { 180 a: nil, 181 b: labels.Instance{"a": "b"}, 182 }, 183 { 184 a: labels.Instance{"a": "b"}, 185 b: nil, 186 }, 187 { 188 a: labels.Instance{"a": "b"}, 189 b: labels.Instance{"a": "b"}, 190 want: true, 191 }, 192 { 193 a: labels.Instance{"a": "b"}, 194 b: labels.Instance{"a": "b", "c": "d"}, 195 }, 196 { 197 b: labels.Instance{"a": "b", "c": "d"}, 198 a: labels.Instance{"a": "b"}, 199 }, 200 { 201 b: labels.Instance{"a": "b", "c": "d"}, 202 a: labels.Instance{"a": "b", "c": "d"}, 203 want: true, 204 }, 205 } 206 for _, c := range cases { 207 if got := c.a.Equals(c.b); got != c.want { 208 t.Errorf("Failed: got eq=%v want=%v for %q ?= %q", got, c.want, c.a, c.b) 209 } 210 } 211 } 212 213 func TestConfigKey(t *testing.T) { 214 cfg := mock_config.Make("ns", 2) 215 want := "test.istio.io/v1/MockConfig/ns/mock-config2" 216 if key := cfg.Meta.Key(); key != want { 217 t.Fatalf("config.Key() => got %q, want %q", key, want) 218 } 219 } 220 221 func TestResolveShortnameToFQDN(t *testing.T) { 222 tests := []struct { 223 name string 224 meta config.Meta 225 out host.Name 226 }{ 227 { 228 "*", config.Meta{}, "*", 229 }, 230 { 231 "*", config.Meta{Namespace: "default", Domain: "cluster.local"}, "*", 232 }, 233 { 234 "foo", config.Meta{Namespace: "default", Domain: "cluster.local"}, "foo.default.svc.cluster.local", 235 }, 236 { 237 "foo.bar", config.Meta{Namespace: "default", Domain: "cluster.local"}, "foo.bar", 238 }, 239 { 240 "foo", config.Meta{Domain: "cluster.local"}, "foo.svc.cluster.local", 241 }, 242 { 243 "foo", config.Meta{Namespace: "default"}, "foo.default", 244 }, 245 { 246 "42.185.131.210", config.Meta{Namespace: "default"}, "42.185.131.210", 247 }, 248 { 249 "42.185.131.210", config.Meta{Namespace: "cluster.local"}, "42.185.131.210", 250 }, 251 { 252 "2a00:4000::614", config.Meta{Namespace: "default"}, "2a00:4000::614", 253 }, 254 { 255 "2a00:4000::614", config.Meta{Namespace: "cluster.local"}, "2a00:4000::614", 256 }, 257 } 258 259 for idx, tt := range tests { 260 t.Run(fmt.Sprintf("[%d] %s", idx, tt.out), func(t *testing.T) { 261 if actual := model.ResolveShortnameToFQDN(tt.name, tt.meta); actual != tt.out { 262 t.Fatalf("model.ResolveShortnameToFQDN(%q, %v) = %q wanted %q", tt.name, tt.meta, actual, tt.out) 263 } 264 }) 265 } 266 } 267 268 func TestMostSpecificHostMatch(t *testing.T) { 269 tests := []struct { 270 in []host.Name 271 needle host.Name 272 want host.Name 273 }{ 274 // this has to be a sorted list 275 {[]host.Name{}, "*", ""}, 276 {[]host.Name{"*.foo.com", "*.com"}, "bar.foo.com", "*.foo.com"}, 277 {[]host.Name{"*.foo.com", "*.com"}, "foo.com", "*.com"}, 278 {[]host.Name{"foo.com", "*.com"}, "*.foo.com", "*.com"}, 279 280 {[]host.Name{"*.foo.com", "foo.com"}, "foo.com", "foo.com"}, 281 {[]host.Name{"*.foo.com", "foo.com"}, "*.foo.com", "*.foo.com"}, 282 283 // this passes because we sort alphabetically 284 {[]host.Name{"bar.com", "foo.com"}, "*.com", ""}, 285 286 {[]host.Name{"bar.com", "*.foo.com"}, "*foo.com", ""}, 287 {[]host.Name{"foo.com", "*.foo.com"}, "*foo.com", ""}, 288 289 // should prioritize closest match 290 {[]host.Name{"*.bar.com", "foo.bar.com"}, "foo.bar.com", "foo.bar.com"}, 291 {[]host.Name{"*.foo.bar.com", "bar.foo.bar.com"}, "bar.foo.bar.com", "bar.foo.bar.com"}, 292 293 // should not match non-wildcards for wildcard needle 294 {[]host.Name{"bar.foo.com", "foo.bar.com"}, "*.foo.com", ""}, 295 {[]host.Name{"foo.bar.foo.com", "bar.foo.bar.com"}, "*.bar.foo.com", ""}, 296 } 297 298 for idx, tt := range tests { 299 specific := sets.New[host.Name]() 300 wildcard := sets.New[host.Name]() 301 for _, h := range tt.in { 302 if h.IsWildCarded() { 303 wildcard.Insert(h) 304 } else { 305 specific.Insert(h) 306 } 307 } 308 309 t.Run(fmt.Sprintf("[%d] %s", idx, tt.needle), func(t *testing.T) { 310 actual, value, found := model.MostSpecificHostMatch(tt.needle, specific, wildcard) 311 if tt.want != "" && !found { 312 t.Fatalf("model.MostSpecificHostMatch(%q, %v) = %v, %v, %t; want: %v", tt.needle, tt.in, actual, value, found, tt.want) 313 } else if actual != tt.want { 314 t.Fatalf("model.MostSpecificHostMatch(%q, %v) = %v, %v, %t; want: %v", tt.needle, tt.in, actual, value, found, tt.want) 315 } 316 if found { 317 if actual.IsWildCarded() && value != wildcard[actual] { 318 t.Fatalf("model.MostSpecificHostMatch(%q, %v) = %v, %v, %t; want: %v", tt.needle, tt.in, actual, value, found, tt.want) 319 } 320 if !actual.IsWildCarded() && value != specific[actual] { 321 t.Fatalf("model.MostSpecificHostMatch(%q, %v) = %v, %v, %t; want: %v", tt.needle, tt.in, actual, value, found, tt.want) 322 } 323 } 324 }) 325 } 326 } 327 328 func BenchmarkMostSpecificHostMatch(b *testing.B) { 329 benchmarks := []struct { 330 name string 331 needle host.Name 332 baseHost string 333 hosts []host.Name 334 specificHostsMap sets.Set[host.Name] 335 wildcardHostsMap sets.Set[host.Name] 336 time int 337 matches bool 338 }{ 339 {"10ExactNoMatch", host.Name("foo.bar.com.10"), "bar.com", []host.Name{}, nil, nil, 10, false}, 340 {"50ExactNoMatch", host.Name("foo.bar.com.50"), "bar.com", []host.Name{}, nil, nil, 50, false}, 341 {"100ExactNoMatch", host.Name("foo.bar.com.100"), "bar.com", []host.Name{}, nil, nil, 100, false}, 342 {"1000ExactNoMatch", host.Name("foo.bar.com.1000"), "bar.com", []host.Name{}, nil, nil, 1000, false}, 343 {"5000ExactNoMatch", host.Name("foo.bar.com.5000"), "bar.com", []host.Name{}, nil, nil, 5000, false}, 344 345 {"10ExactMatch", host.Name("foo.bar.com.10"), "foo.bar.com", []host.Name{}, nil, nil, 10, true}, 346 {"50ExactMatch", host.Name("foo.bar.com.50"), "foo.bar.com", []host.Name{}, nil, nil, 50, true}, 347 {"100ExactMatch", host.Name("foo.bar.com.100"), "foo.bar.com", []host.Name{}, nil, nil, 100, true}, 348 {"1000ExactMatch", host.Name("foo.bar.com.1000"), "foo.bar.com", []host.Name{}, nil, nil, 1000, true}, 349 {"5000ExactMatch", host.Name("foo.bar.com.5000"), "foo.bar.com", []host.Name{}, nil, nil, 5000, true}, 350 351 {"10DestRuleWildcardNoMatch", host.Name("foo.bar.com.10"), "*.foo.bar.com", []host.Name{}, nil, nil, 10, false}, 352 {"50DestRuleWildcardNoMatch", host.Name("foo.bar.com.50"), "*.foo.bar.com", []host.Name{}, nil, nil, 50, false}, 353 {"100DestRuleWildcardNoMatch", host.Name("foo.bar.com.100"), "*.foo.bar.com", []host.Name{}, nil, nil, 100, false}, 354 {"1000DestRuleWildcardNoMatch", host.Name("foo.bar.com.1000"), "*.foo.bar.com", []host.Name{}, nil, nil, 1000, false}, 355 {"5000DestRuleWildcardNoMatch", host.Name("foo.bar.com.5000"), "*.foo.bar.com", []host.Name{}, nil, nil, 5000, false}, 356 357 {"10DestRuleWildcardMatch", host.Name("foo.bar.baz.com.10"), "*.bar.baz.com", []host.Name{}, nil, nil, 10, true}, 358 {"50DestRuleWildcardMatch", host.Name("foo.bar.baz.com.50"), "*.bar.baz.com", []host.Name{}, nil, nil, 50, true}, 359 {"100DestRuleWildcardMatch", host.Name("foo.bar.baz.com.100"), "*.bar.baz.com", []host.Name{}, nil, nil, 100, true}, 360 {"1000DestRuleWildcardMatch", host.Name("foo.bar.baz.com.1000"), "*.bar.baz.com", []host.Name{}, nil, nil, 1000, true}, 361 {"5000DestRuleWildcardMatch", host.Name("foo.bar.baz.com.5000"), "*.bar.baz.com", []host.Name{}, nil, nil, 5000, true}, 362 363 {"10NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, nil, 10, false}, 364 {"50NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, nil, 50, false}, 365 {"100NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, nil, 100, false}, 366 {"1000NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, nil, 1000, false}, 367 {"5000NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, nil, 5000, false}, 368 369 {"10NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.10"), "*.foo.bar.com", []host.Name{}, nil, nil, 10, true}, 370 {"50NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.50"), "*.foo.bar.com", []host.Name{}, nil, nil, 50, true}, 371 {"100NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.100"), "*.foo.bar.com", []host.Name{}, nil, nil, 100, true}, 372 {"1000NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.1000"), "*.foo.bar.com", []host.Name{}, nil, nil, 1000, true}, 373 {"5000NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.5000"), "*.foo.bar.com", []host.Name{}, nil, nil, 5000, true}, 374 } 375 376 for _, bm := range benchmarks { 377 bm.specificHostsMap = sets.NewWithLength[host.Name](bm.time) 378 bm.wildcardHostsMap = sets.NewWithLength[host.Name](bm.time) 379 380 for i := 1; i <= bm.time; i++ { 381 h := host.Name(bm.baseHost + "." + strconv.Itoa(i)) 382 if h.IsWildCarded() { 383 bm.wildcardHostsMap.Insert(h) 384 } else { 385 bm.specificHostsMap.Insert(h) 386 } 387 } 388 389 b.Run(bm.name, func(b *testing.B) { 390 for n := 0; n < b.N; n++ { 391 _, _, ok := model.MostSpecificHostMatch(bm.needle, bm.specificHostsMap, bm.wildcardHostsMap) 392 if bm.matches != ok { 393 b.Fatalf("expected to find match") 394 } 395 } 396 }) 397 } 398 } 399 400 func BenchmarkMostSpecificHostMatchMixed(b *testing.B) { 401 benchmarks := []struct { 402 name string 403 needle host.Name 404 baseHost string 405 hosts []host.Name 406 specificHostsMap sets.Set[host.Name] 407 wildcardHostsMap sets.Set[host.Name] 408 time int 409 matches bool 410 }{ 411 {"10DestRuleWildcardNoMatch", host.Name("foo.bar.com.10"), "foo.bar.com", []host.Name{}, nil, nil, 10, false}, 412 {"50DestRuleWildcardNoMatch", host.Name("foo.bar.com.50"), "foo.bar.com", []host.Name{}, nil, nil, 50, false}, 413 {"100DestRuleWildcardNoMatch", host.Name("foo.bar.com.100"), "foo.bar.com", []host.Name{}, nil, nil, 100, false}, 414 {"1000DestRuleWildcardNoMatch", host.Name("foo.bar.com.1000"), "foo.bar.com", []host.Name{}, nil, nil, 1000, false}, 415 {"5000DestRuleWildcardNoMatch", host.Name("foo.bar.com.5000"), "foo.bar.com", []host.Name{}, nil, nil, 5000, false}, 416 417 {"10DestRuleWildcardMatch", host.Name("foo.bar.baz.com.10"), "bar.baz.com", []host.Name{}, nil, nil, 10, true}, 418 {"50DestRuleWildcardMatch", host.Name("foo.bar.baz.com.50"), "bar.baz.com", []host.Name{}, nil, nil, 50, true}, 419 {"100DestRuleWildcardMatch", host.Name("foo.bar.baz.com.100"), "bar.baz.com", []host.Name{}, nil, nil, 100, true}, 420 {"1000DestRuleWildcardMatch", host.Name("foo.bar.baz.com.1000"), "bar.baz.com", []host.Name{}, nil, nil, 1000, true}, 421 {"5000DestRuleWildcardMatch", host.Name("foo.bar.baz.com.5000"), "bar.baz.com", []host.Name{}, nil, nil, 5000, true}, 422 423 {"10NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "foo.bar.com", []host.Name{}, nil, nil, 10, false}, 424 {"50NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "foo.bar.com", []host.Name{}, nil, nil, 50, false}, 425 {"100NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "foo.bar.com", []host.Name{}, nil, nil, 100, false}, 426 {"1000NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "foo.bar.com", []host.Name{}, nil, nil, 1000, false}, 427 {"5000NeedleWildcardNoMatch", host.Name("*.bar.foo.bar.com"), "foo.bar.com", []host.Name{}, nil, nil, 5000, false}, 428 429 {"10NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.10"), "foo.bar.com", []host.Name{}, nil, nil, 10, true}, 430 {"50NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.50"), "foo.bar.com", []host.Name{}, nil, nil, 50, true}, 431 {"100NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.100"), "foo.bar.com", []host.Name{}, nil, nil, 100, true}, 432 {"1000NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.1000"), "foo.bar.com", []host.Name{}, nil, nil, 1000, true}, 433 {"5000NeedleWildcardMatch", host.Name("*.bar.foo.bar.com.5000"), "foo.bar.com", []host.Name{}, nil, nil, 5000, true}, 434 } 435 436 for _, bm := range benchmarks { 437 bm.specificHostsMap = make(map[host.Name]struct{}, bm.time) 438 bm.wildcardHostsMap = make(map[host.Name]struct{}, bm.time) 439 440 for i := 1; i <= bm.time; i++ { 441 // these specific non-wildcard hosts are crafted this way to never match the needle, 442 // this should replicate real-world scenarios of mixed specific and wildcard hosts 443 specific := host.Name(strconv.Itoa(i) + "." + bm.baseHost) 444 // generate correct wildcard hosts, one of these will match 445 wildcard := host.Name("*." + bm.baseHost + "." + strconv.Itoa(i)) 446 447 bm.specificHostsMap[specific] = struct{}{} 448 bm.wildcardHostsMap[wildcard] = struct{}{} 449 } 450 451 b.Run(bm.name, func(b *testing.B) { 452 for n := 0; n < b.N; n++ { 453 _, _, ok := model.MostSpecificHostMatch(bm.needle, bm.specificHostsMap, bm.wildcardHostsMap) 454 if bm.matches != ok { 455 b.Fatalf("expected to find match") 456 } 457 } 458 }) 459 } 460 } 461 462 func BenchmarkMostSpecificHostMatchMultiMatch(b *testing.B) { 463 benchmarks := []struct { 464 name string 465 needle host.Name 466 hosts []host.Name 467 specificHostsMap map[host.Name]struct{} 468 wildcardHostsMap map[host.Name]struct{} 469 }{ 470 {"DestRuleWildcard", host.Name("a.foo.bar.baz.com"), []host.Name{"*.foo.bar.baz.com", "*.bar.baz.com", "*.baz.com", "*.com"}, nil, nil}, 471 472 {"NeedleWildcard", host.Name("*.a.foo.bar.baz.com"), []host.Name{"*.foo.bar.baz.com", "*.bar.baz.com", "*.baz.com", "*.com"}, nil, nil}, 473 } 474 475 for _, bm := range benchmarks { 476 bm.specificHostsMap = sets.New[host.Name]() 477 bm.wildcardHostsMap = sets.NewWithLength[host.Name](len(bm.hosts)) 478 479 for _, h := range bm.hosts { 480 if h.IsWildCarded() { 481 bm.wildcardHostsMap[h] = struct{}{} 482 } else { 483 bm.specificHostsMap[h] = struct{}{} 484 } 485 } 486 487 b.Run(bm.name, func(b *testing.B) { 488 for n := 0; n < b.N; n++ { 489 _, _, ok := model.MostSpecificHostMatch(bm.needle, bm.specificHostsMap, bm.wildcardHostsMap) 490 if !ok { 491 b.Fatalf("expected to find match") 492 } 493 } 494 }) 495 } 496 } 497 498 func BenchmarkHashCode(b *testing.B) { 499 benchmarks := []struct { 500 name string 501 config model.ConfigKey 502 }{ 503 { 504 name: "small string", 505 config: model.ConfigKey{ 506 Kind: kind.VirtualService, 507 Name: "abc", 508 Namespace: "ns-foo", 509 }, 510 }, 511 { 512 name: "middle string", 513 config: model.ConfigKey{ 514 Kind: kind.VirtualService, 515 Name: "foo.svc.cluster.local.middle.len", 516 Namespace: "ns-foo-a-middle-string-with-len", 517 }, 518 }, 519 { 520 name: "long string", 521 config: model.ConfigKey{ 522 Kind: kind.VirtualService, 523 Name: "foo.svc.cluster.local.middle.len.foo.svc.cluster.local.middle.len", 524 Namespace: "ns-foo-a-middle-string-with-len.ns-foo-a-middle-string-with-len", 525 }, 526 }, 527 } 528 for _, bm := range benchmarks { 529 b.Run(bm.name, func(b *testing.B) { 530 for n := 0; n < b.N; n++ { 531 bm.config.HashCode() 532 } 533 }) 534 } 535 } 536 537 func TestHashCodeCollision(t *testing.T) { 538 config1 := model.ConfigKey{ 539 Kind: kind.VirtualService, 540 Name: "abc", 541 Namespace: "ns-foo", 542 } 543 544 config2 := model.ConfigKey{ 545 Kind: kind.VirtualService, 546 Name: "ab", 547 Namespace: "cns-foo", 548 } 549 550 if config1.HashCode() == config2.HashCode() { 551 t.Fatalf("Hash code of config1 %s should not be equal to config2 %s", config1.String(), config2.String()) 552 } 553 }