1/* 2Copyright 2014 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package labels 18 19import ( 20 "reflect" 21 "strings" 22 "testing" 23 24 "k8s.io/apimachinery/pkg/selection" 25 "k8s.io/apimachinery/pkg/util/sets" 26) 27 28func TestSelectorParse(t *testing.T) { 29 testGoodStrings := []string{ 30 "x=a,y=b,z=c", 31 "", 32 "x!=a,y=b", 33 "x=", 34 "x= ", 35 "x=,z= ", 36 "x= ,z= ", 37 "!x", 38 "x>1", 39 "x>1,z<5", 40 } 41 testBadStrings := []string{ 42 "x=a||y=b", 43 "x==a==b", 44 "!x=a", 45 "x<a", 46 } 47 for _, test := range testGoodStrings { 48 lq, err := Parse(test) 49 if err != nil { 50 t.Errorf("%v: error %v (%#v)\n", test, err, err) 51 } 52 if strings.Replace(test, " ", "", -1) != lq.String() { 53 t.Errorf("%v restring gave: %v\n", test, lq.String()) 54 } 55 } 56 for _, test := range testBadStrings { 57 _, err := Parse(test) 58 if err == nil { 59 t.Errorf("%v: did not get expected error\n", test) 60 } 61 } 62} 63 64func TestDeterministicParse(t *testing.T) { 65 s1, err := Parse("x=a,a=x") 66 s2, err2 := Parse("a=x,x=a") 67 if err != nil || err2 != nil { 68 t.Errorf("Unexpected parse error") 69 } 70 if s1.String() != s2.String() { 71 t.Errorf("Non-deterministic parse") 72 } 73} 74 75func expectMatch(t *testing.T, selector string, ls Set) { 76 lq, err := Parse(selector) 77 if err != nil { 78 t.Errorf("Unable to parse %v as a selector\n", selector) 79 return 80 } 81 if !lq.Matches(ls) { 82 t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls) 83 } 84} 85 86func expectNoMatch(t *testing.T, selector string, ls Set) { 87 lq, err := Parse(selector) 88 if err != nil { 89 t.Errorf("Unable to parse %v as a selector\n", selector) 90 return 91 } 92 if lq.Matches(ls) { 93 t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls) 94 } 95} 96 97func TestEverything(t *testing.T) { 98 if !Everything().Matches(Set{"x": "y"}) { 99 t.Errorf("Nil selector didn't match") 100 } 101 if !Everything().Empty() { 102 t.Errorf("Everything was not empty") 103 } 104} 105 106func TestSelectorMatches(t *testing.T) { 107 expectMatch(t, "", Set{"x": "y"}) 108 expectMatch(t, "x=y", Set{"x": "y"}) 109 expectMatch(t, "x=y,z=w", Set{"x": "y", "z": "w"}) 110 expectMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "a"}) 111 expectMatch(t, "notin=in", Set{"notin": "in"}) // in and notin in exactMatch 112 expectMatch(t, "x", Set{"x": "z"}) 113 expectMatch(t, "!x", Set{"y": "z"}) 114 expectMatch(t, "x>1", Set{"x": "2"}) 115 expectMatch(t, "x<1", Set{"x": "0"}) 116 expectNoMatch(t, "x=z", Set{}) 117 expectNoMatch(t, "x=y", Set{"x": "z"}) 118 expectNoMatch(t, "x=y,z=w", Set{"x": "w", "z": "w"}) 119 expectNoMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "w"}) 120 expectNoMatch(t, "x", Set{"y": "z"}) 121 expectNoMatch(t, "!x", Set{"x": "z"}) 122 expectNoMatch(t, "x>1", Set{"x": "0"}) 123 expectNoMatch(t, "x<1", Set{"x": "2"}) 124 125 labelset := Set{ 126 "foo": "bar", 127 "baz": "blah", 128 } 129 expectMatch(t, "foo=bar", labelset) 130 expectMatch(t, "baz=blah", labelset) 131 expectMatch(t, "foo=bar,baz=blah", labelset) 132 expectNoMatch(t, "foo=blah", labelset) 133 expectNoMatch(t, "baz=bar", labelset) 134 expectNoMatch(t, "foo=bar,foobar=bar,baz=blah", labelset) 135} 136 137func expectMatchDirect(t *testing.T, selector, ls Set) { 138 if !SelectorFromSet(selector).Matches(ls) { 139 t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls) 140 } 141} 142 143func expectNoMatchDirect(t *testing.T, selector, ls Set) { 144 if SelectorFromSet(selector).Matches(ls) { 145 t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls) 146 } 147} 148 149func TestSetMatches(t *testing.T) { 150 labelset := Set{ 151 "foo": "bar", 152 "baz": "blah", 153 } 154 expectMatchDirect(t, Set{}, labelset) 155 expectMatchDirect(t, Set{"foo": "bar"}, labelset) 156 expectMatchDirect(t, Set{"baz": "blah"}, labelset) 157 expectMatchDirect(t, Set{"foo": "bar", "baz": "blah"}, labelset) 158 159 //TODO: bad values not handled for the moment in SelectorFromSet 160 //expectNoMatchDirect(t, Set{"foo": "=blah"}, labelset) 161 //expectNoMatchDirect(t, Set{"baz": "=bar"}, labelset) 162 //expectNoMatchDirect(t, Set{"foo": "=bar", "foobar": "bar", "baz": "blah"}, labelset) 163} 164 165func TestNilMapIsValid(t *testing.T) { 166 selector := Set(nil).AsSelector() 167 if selector == nil { 168 t.Errorf("Selector for nil set should be Everything") 169 } 170 if !selector.Empty() { 171 t.Errorf("Selector for nil set should be Empty") 172 } 173} 174 175func TestSetIsEmpty(t *testing.T) { 176 if !(Set{}).AsSelector().Empty() { 177 t.Errorf("Empty set should be empty") 178 } 179 if !(NewSelector()).Empty() { 180 t.Errorf("Nil Selector should be empty") 181 } 182} 183 184func TestLexer(t *testing.T) { 185 testcases := []struct { 186 s string 187 t Token 188 }{ 189 {"", EndOfStringToken}, 190 {",", CommaToken}, 191 {"notin", NotInToken}, 192 {"in", InToken}, 193 {"=", EqualsToken}, 194 {"==", DoubleEqualsToken}, 195 {">", GreaterThanToken}, 196 {"<", LessThanToken}, 197 //Note that Lex returns the longest valid token found 198 {"!", DoesNotExistToken}, 199 {"!=", NotEqualsToken}, 200 {"(", OpenParToken}, 201 {")", ClosedParToken}, 202 //Non-"special" characters are considered part of an identifier 203 {"~", IdentifierToken}, 204 {"||", IdentifierToken}, 205 } 206 for _, v := range testcases { 207 l := &Lexer{s: v.s, pos: 0} 208 token, lit := l.Lex() 209 if token != v.t { 210 t.Errorf("Got %d it should be %d for '%s'", token, v.t, v.s) 211 } 212 if v.t != ErrorToken && lit != v.s { 213 t.Errorf("Got '%s' it should be '%s'", lit, v.s) 214 } 215 } 216} 217 218func min(l, r int) (m int) { 219 m = r 220 if l < r { 221 m = l 222 } 223 return m 224} 225 226func TestLexerSequence(t *testing.T) { 227 testcases := []struct { 228 s string 229 t []Token 230 }{ 231 {"key in ( value )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, ClosedParToken}}, 232 {"key notin ( value )", []Token{IdentifierToken, NotInToken, OpenParToken, IdentifierToken, ClosedParToken}}, 233 {"key in ( value1, value2 )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, CommaToken, IdentifierToken, ClosedParToken}}, 234 {"key", []Token{IdentifierToken}}, 235 {"!key", []Token{DoesNotExistToken, IdentifierToken}}, 236 {"()", []Token{OpenParToken, ClosedParToken}}, 237 {"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken}}, 238 {"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken}}, 239 {"key>2", []Token{IdentifierToken, GreaterThanToken, IdentifierToken}}, 240 {"key<1", []Token{IdentifierToken, LessThanToken, IdentifierToken}}, 241 } 242 for _, v := range testcases { 243 var literals []string 244 var tokens []Token 245 l := &Lexer{s: v.s, pos: 0} 246 for { 247 token, lit := l.Lex() 248 if token == EndOfStringToken { 249 break 250 } 251 tokens = append(tokens, token) 252 literals = append(literals, lit) 253 } 254 if len(tokens) != len(v.t) { 255 t.Errorf("Bad number of tokens for '%s %d, %d", v.s, len(tokens), len(v.t)) 256 } 257 for i := 0; i < min(len(tokens), len(v.t)); i++ { 258 if tokens[i] != v.t[i] { 259 t.Errorf("Test '%s': Mismatching in token type found '%v' it should be '%v'", v.s, tokens[i], v.t[i]) 260 } 261 } 262 } 263} 264func TestParserLookahead(t *testing.T) { 265 testcases := []struct { 266 s string 267 t []Token 268 }{ 269 {"key in ( value )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, ClosedParToken, EndOfStringToken}}, 270 {"key notin ( value )", []Token{IdentifierToken, NotInToken, OpenParToken, IdentifierToken, ClosedParToken, EndOfStringToken}}, 271 {"key in ( value1, value2 )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, CommaToken, IdentifierToken, ClosedParToken, EndOfStringToken}}, 272 {"key", []Token{IdentifierToken, EndOfStringToken}}, 273 {"!key", []Token{DoesNotExistToken, IdentifierToken, EndOfStringToken}}, 274 {"()", []Token{OpenParToken, ClosedParToken, EndOfStringToken}}, 275 {"", []Token{EndOfStringToken}}, 276 {"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken, EndOfStringToken}}, 277 {"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken, EndOfStringToken}}, 278 {"key>2", []Token{IdentifierToken, GreaterThanToken, IdentifierToken, EndOfStringToken}}, 279 {"key<1", []Token{IdentifierToken, LessThanToken, IdentifierToken, EndOfStringToken}}, 280 } 281 for _, v := range testcases { 282 p := &Parser{l: &Lexer{s: v.s, pos: 0}, position: 0} 283 p.scan() 284 if len(p.scannedItems) != len(v.t) { 285 t.Errorf("Expected %d items found %d", len(v.t), len(p.scannedItems)) 286 } 287 for { 288 token, lit := p.lookahead(KeyAndOperator) 289 290 token2, lit2 := p.consume(KeyAndOperator) 291 if token == EndOfStringToken { 292 break 293 } 294 if token != token2 || lit != lit2 { 295 t.Errorf("Bad values") 296 } 297 } 298 } 299} 300 301func TestRequirementConstructor(t *testing.T) { 302 requirementConstructorTests := []struct { 303 Key string 304 Op selection.Operator 305 Vals sets.String 306 Success bool 307 }{ 308 {"x", selection.In, nil, false}, 309 {"x", selection.NotIn, sets.NewString(), false}, 310 {"x", selection.In, sets.NewString("foo"), true}, 311 {"x", selection.NotIn, sets.NewString("foo"), true}, 312 {"x", selection.Exists, nil, true}, 313 {"x", selection.DoesNotExist, nil, true}, 314 {"1foo", selection.In, sets.NewString("bar"), true}, 315 {"1234", selection.In, sets.NewString("bar"), true}, 316 {"y", selection.GreaterThan, sets.NewString("1"), true}, 317 {"z", selection.LessThan, sets.NewString("6"), true}, 318 {"foo", selection.GreaterThan, sets.NewString("bar"), false}, 319 {"barz", selection.LessThan, sets.NewString("blah"), false}, 320 {strings.Repeat("a", 254), selection.Exists, nil, false}, //breaks DNS rule that len(key) <= 253 321 } 322 for _, rc := range requirementConstructorTests { 323 if _, err := NewRequirement(rc.Key, rc.Op, rc.Vals.List()); err == nil && !rc.Success { 324 t.Errorf("expected error with key:%#v op:%v vals:%v, got no error", rc.Key, rc.Op, rc.Vals) 325 } else if err != nil && rc.Success { 326 t.Errorf("expected no error with key:%#v op:%v vals:%v, got:%v", rc.Key, rc.Op, rc.Vals, err) 327 } 328 } 329} 330 331func TestToString(t *testing.T) { 332 var req Requirement 333 toStringTests := []struct { 334 In *internalSelector 335 Out string 336 Valid bool 337 }{ 338 339 {&internalSelector{ 340 getRequirement("x", selection.In, sets.NewString("abc", "def"), t), 341 getRequirement("y", selection.NotIn, sets.NewString("jkl"), t), 342 getRequirement("z", selection.Exists, nil, t)}, 343 "x in (abc,def),y notin (jkl),z", true}, 344 {&internalSelector{ 345 getRequirement("x", selection.NotIn, sets.NewString("abc", "def"), t), 346 getRequirement("y", selection.NotEquals, sets.NewString("jkl"), t), 347 getRequirement("z", selection.DoesNotExist, nil, t)}, 348 "x notin (abc,def),y!=jkl,!z", true}, 349 {&internalSelector{ 350 getRequirement("x", selection.In, sets.NewString("abc", "def"), t), 351 req}, // adding empty req for the trailing ',' 352 "x in (abc,def),", false}, 353 {&internalSelector{ 354 getRequirement("x", selection.NotIn, sets.NewString("abc"), t), 355 getRequirement("y", selection.In, sets.NewString("jkl", "mno"), t), 356 getRequirement("z", selection.NotIn, sets.NewString(""), t)}, 357 "x notin (abc),y in (jkl,mno),z notin ()", true}, 358 {&internalSelector{ 359 getRequirement("x", selection.Equals, sets.NewString("abc"), t), 360 getRequirement("y", selection.DoubleEquals, sets.NewString("jkl"), t), 361 getRequirement("z", selection.NotEquals, sets.NewString("a"), t), 362 getRequirement("z", selection.Exists, nil, t)}, 363 "x=abc,y==jkl,z!=a,z", true}, 364 {&internalSelector{ 365 getRequirement("x", selection.GreaterThan, sets.NewString("2"), t), 366 getRequirement("y", selection.LessThan, sets.NewString("8"), t), 367 getRequirement("z", selection.Exists, nil, t)}, 368 "x>2,y<8,z", true}, 369 } 370 for _, ts := range toStringTests { 371 if out := ts.In.String(); out == "" && ts.Valid { 372 t.Errorf("%#v.String() => '%v' expected no error", ts.In, out) 373 } else if out != ts.Out { 374 t.Errorf("%#v.String() => '%v' want '%v'", ts.In, out, ts.Out) 375 } 376 } 377} 378 379func TestRequirementSelectorMatching(t *testing.T) { 380 var req Requirement 381 labelSelectorMatchingTests := []struct { 382 Set Set 383 Sel Selector 384 Match bool 385 }{ 386 {Set{"x": "foo", "y": "baz"}, &internalSelector{ 387 req, 388 }, false}, 389 {Set{"x": "foo", "y": "baz"}, &internalSelector{ 390 getRequirement("x", selection.In, sets.NewString("foo"), t), 391 getRequirement("y", selection.NotIn, sets.NewString("alpha"), t), 392 }, true}, 393 {Set{"x": "foo", "y": "baz"}, &internalSelector{ 394 getRequirement("x", selection.In, sets.NewString("foo"), t), 395 getRequirement("y", selection.In, sets.NewString("alpha"), t), 396 }, false}, 397 {Set{"y": ""}, &internalSelector{ 398 getRequirement("x", selection.NotIn, sets.NewString(""), t), 399 getRequirement("y", selection.Exists, nil, t), 400 }, true}, 401 {Set{"y": ""}, &internalSelector{ 402 getRequirement("x", selection.DoesNotExist, nil, t), 403 getRequirement("y", selection.Exists, nil, t), 404 }, true}, 405 {Set{"y": ""}, &internalSelector{ 406 getRequirement("x", selection.NotIn, sets.NewString(""), t), 407 getRequirement("y", selection.DoesNotExist, nil, t), 408 }, false}, 409 {Set{"y": "baz"}, &internalSelector{ 410 getRequirement("x", selection.In, sets.NewString(""), t), 411 }, false}, 412 {Set{"z": "2"}, &internalSelector{ 413 getRequirement("z", selection.GreaterThan, sets.NewString("1"), t), 414 }, true}, 415 {Set{"z": "v2"}, &internalSelector{ 416 getRequirement("z", selection.GreaterThan, sets.NewString("1"), t), 417 }, false}, 418 } 419 for _, lsm := range labelSelectorMatchingTests { 420 if match := lsm.Sel.Matches(lsm.Set); match != lsm.Match { 421 t.Errorf("%+v.Matches(%#v) => %v, want %v", lsm.Sel, lsm.Set, match, lsm.Match) 422 } 423 } 424} 425 426func TestSetSelectorParser(t *testing.T) { 427 setSelectorParserTests := []struct { 428 In string 429 Out Selector 430 Match bool 431 Valid bool 432 }{ 433 {"", NewSelector(), true, true}, 434 {"\rx", internalSelector{ 435 getRequirement("x", selection.Exists, nil, t), 436 }, true, true}, 437 {"this-is-a-dns.domain.com/key-with-dash", internalSelector{ 438 getRequirement("this-is-a-dns.domain.com/key-with-dash", selection.Exists, nil, t), 439 }, true, true}, 440 {"this-is-another-dns.domain.com/key-with-dash in (so,what)", internalSelector{ 441 getRequirement("this-is-another-dns.domain.com/key-with-dash", selection.In, sets.NewString("so", "what"), t), 442 }, true, true}, 443 {"0.1.2.domain/99 notin (10.10.100.1, tick.tack.clock)", internalSelector{ 444 getRequirement("0.1.2.domain/99", selection.NotIn, sets.NewString("10.10.100.1", "tick.tack.clock"), t), 445 }, true, true}, 446 {"foo in (abc)", internalSelector{ 447 getRequirement("foo", selection.In, sets.NewString("abc"), t), 448 }, true, true}, 449 {"x notin\n (abc)", internalSelector{ 450 getRequirement("x", selection.NotIn, sets.NewString("abc"), t), 451 }, true, true}, 452 {"x notin \t (abc,def)", internalSelector{ 453 getRequirement("x", selection.NotIn, sets.NewString("abc", "def"), t), 454 }, true, true}, 455 {"x in (abc,def)", internalSelector{ 456 getRequirement("x", selection.In, sets.NewString("abc", "def"), t), 457 }, true, true}, 458 {"x in (abc,)", internalSelector{ 459 getRequirement("x", selection.In, sets.NewString("abc", ""), t), 460 }, true, true}, 461 {"x in ()", internalSelector{ 462 getRequirement("x", selection.In, sets.NewString(""), t), 463 }, true, true}, 464 {"x notin (abc,,def),bar,z in (),w", internalSelector{ 465 getRequirement("bar", selection.Exists, nil, t), 466 getRequirement("w", selection.Exists, nil, t), 467 getRequirement("x", selection.NotIn, sets.NewString("abc", "", "def"), t), 468 getRequirement("z", selection.In, sets.NewString(""), t), 469 }, true, true}, 470 {"x,y in (a)", internalSelector{ 471 getRequirement("y", selection.In, sets.NewString("a"), t), 472 getRequirement("x", selection.Exists, nil, t), 473 }, false, true}, 474 {"x=a", internalSelector{ 475 getRequirement("x", selection.Equals, sets.NewString("a"), t), 476 }, true, true}, 477 {"x>1", internalSelector{ 478 getRequirement("x", selection.GreaterThan, sets.NewString("1"), t), 479 }, true, true}, 480 {"x<7", internalSelector{ 481 getRequirement("x", selection.LessThan, sets.NewString("7"), t), 482 }, true, true}, 483 {"x=a,y!=b", internalSelector{ 484 getRequirement("x", selection.Equals, sets.NewString("a"), t), 485 getRequirement("y", selection.NotEquals, sets.NewString("b"), t), 486 }, true, true}, 487 {"x=a,y!=b,z in (h,i,j)", internalSelector{ 488 getRequirement("x", selection.Equals, sets.NewString("a"), t), 489 getRequirement("y", selection.NotEquals, sets.NewString("b"), t), 490 getRequirement("z", selection.In, sets.NewString("h", "i", "j"), t), 491 }, true, true}, 492 {"x=a||y=b", internalSelector{}, false, false}, 493 {"x,,y", nil, true, false}, 494 {",x,y", nil, true, false}, 495 {"x nott in (y)", nil, true, false}, 496 {"x notin ( )", internalSelector{ 497 getRequirement("x", selection.NotIn, sets.NewString(""), t), 498 }, true, true}, 499 {"x notin (, a)", internalSelector{ 500 getRequirement("x", selection.NotIn, sets.NewString("", "a"), t), 501 }, true, true}, 502 {"a in (xyz),", nil, true, false}, 503 {"a in (xyz)b notin ()", nil, true, false}, 504 {"a ", internalSelector{ 505 getRequirement("a", selection.Exists, nil, t), 506 }, true, true}, 507 {"a in (x,y,notin, z,in)", internalSelector{ 508 getRequirement("a", selection.In, sets.NewString("in", "notin", "x", "y", "z"), t), 509 }, true, true}, // operator 'in' inside list of identifiers 510 {"a in (xyz abc)", nil, false, false}, // no comma 511 {"a notin(", nil, true, false}, // bad formed 512 {"a (", nil, false, false}, // cpar 513 {"(", nil, false, false}, // opar 514 } 515 516 for _, ssp := range setSelectorParserTests { 517 if sel, err := Parse(ssp.In); err != nil && ssp.Valid { 518 t.Errorf("Parse(%s) => %v expected no error", ssp.In, err) 519 } else if err == nil && !ssp.Valid { 520 t.Errorf("Parse(%s) => %+v expected error", ssp.In, sel) 521 } else if ssp.Match && !reflect.DeepEqual(sel, ssp.Out) { 522 t.Errorf("Parse(%s) => parse output '%#v' doesn't match '%#v' expected match", ssp.In, sel, ssp.Out) 523 } 524 } 525} 526 527func getRequirement(key string, op selection.Operator, vals sets.String, t *testing.T) Requirement { 528 req, err := NewRequirement(key, op, vals.List()) 529 if err != nil { 530 t.Errorf("NewRequirement(%v, %v, %v) resulted in error:%v", key, op, vals, err) 531 return Requirement{} 532 } 533 return *req 534} 535 536func TestAdd(t *testing.T) { 537 testCases := []struct { 538 name string 539 sel Selector 540 key string 541 operator selection.Operator 542 values []string 543 refSelector Selector 544 }{ 545 { 546 "keyInOperator", 547 internalSelector{}, 548 "key", 549 selection.In, 550 []string{"value"}, 551 internalSelector{Requirement{"key", selection.In, []string{"value"}}}, 552 }, 553 { 554 "keyEqualsOperator", 555 internalSelector{Requirement{"key", selection.In, []string{"value"}}}, 556 "key2", 557 selection.Equals, 558 []string{"value2"}, 559 internalSelector{ 560 Requirement{"key", selection.In, []string{"value"}}, 561 Requirement{"key2", selection.Equals, []string{"value2"}}, 562 }, 563 }, 564 } 565 for _, ts := range testCases { 566 req, err := NewRequirement(ts.key, ts.operator, ts.values) 567 if err != nil { 568 t.Errorf("%s - Unable to create labels.Requirement", ts.name) 569 } 570 ts.sel = ts.sel.Add(*req) 571 if !reflect.DeepEqual(ts.sel, ts.refSelector) { 572 t.Errorf("%s - Expected %v found %v", ts.name, ts.refSelector, ts.sel) 573 } 574 } 575} 576 577func TestSafeSort(t *testing.T) { 578 tests := []struct { 579 name string 580 in []string 581 inCopy []string 582 want []string 583 }{ 584 { 585 name: "nil strings", 586 in: nil, 587 inCopy: nil, 588 want: nil, 589 }, 590 { 591 name: "ordered strings", 592 in: []string{"bar", "foo"}, 593 inCopy: []string{"bar", "foo"}, 594 want: []string{"bar", "foo"}, 595 }, 596 { 597 name: "unordered strings", 598 in: []string{"foo", "bar"}, 599 inCopy: []string{"foo", "bar"}, 600 want: []string{"bar", "foo"}, 601 }, 602 { 603 name: "duplicated strings", 604 in: []string{"foo", "bar", "foo", "bar"}, 605 inCopy: []string{"foo", "bar", "foo", "bar"}, 606 want: []string{"bar", "bar", "foo", "foo"}, 607 }, 608 } 609 for _, tt := range tests { 610 t.Run(tt.name, func(t *testing.T) { 611 if got := safeSort(tt.in); !reflect.DeepEqual(got, tt.want) { 612 t.Errorf("safeSort() = %v, want %v", got, tt.want) 613 } 614 if !reflect.DeepEqual(tt.in, tt.inCopy) { 615 t.Errorf("after safeSort(), input = %v, want %v", tt.in, tt.inCopy) 616 } 617 }) 618 } 619} 620