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
621func BenchmarkSelectorFromValidatedSet(b *testing.B) {
622	set := map[string]string{
623		"foo": "foo",
624		"bar": "bar",
625	}
626
627	for i := 0; i < b.N; i++ {
628		if SelectorFromValidatedSet(set).Empty() {
629			b.Errorf("Unexpected selector")
630		}
631	}
632}
633
634func TestRequiresExactMatch(t *testing.T) {
635	testCases := []struct {
636		name          string
637		sel           Selector
638		label         string
639		expectedFound bool
640		expectedValue string
641	}{
642		{
643			name:          "keyInOperatorExactMatch",
644			sel:           internalSelector{Requirement{"key", selection.In, []string{"value"}}},
645			label:         "key",
646			expectedFound: true,
647			expectedValue: "value",
648		},
649		{
650			name:          "keyInOperatorNotExactMatch",
651			sel:           internalSelector{Requirement{"key", selection.In, []string{"value", "value2"}}},
652			label:         "key",
653			expectedFound: false,
654			expectedValue: "",
655		},
656		{
657			name: "keyInOperatorNotExactMatch",
658			sel: internalSelector{
659				Requirement{"key", selection.In, []string{"value", "value1"}},
660				Requirement{"key2", selection.In, []string{"value2"}},
661			},
662			label:         "key2",
663			expectedFound: true,
664			expectedValue: "value2",
665		},
666		{
667			name:          "keyEqualOperatorExactMatch",
668			sel:           internalSelector{Requirement{"key", selection.Equals, []string{"value"}}},
669			label:         "key",
670			expectedFound: true,
671			expectedValue: "value",
672		},
673		{
674			name:          "keyDoubleEqualOperatorExactMatch",
675			sel:           internalSelector{Requirement{"key", selection.DoubleEquals, []string{"value"}}},
676			label:         "key",
677			expectedFound: true,
678			expectedValue: "value",
679		},
680		{
681			name:          "keyNotEqualOperatorExactMatch",
682			sel:           internalSelector{Requirement{"key", selection.NotEquals, []string{"value"}}},
683			label:         "key",
684			expectedFound: false,
685			expectedValue: "",
686		},
687		{
688			name: "keyEqualOperatorExactMatchFirst",
689			sel: internalSelector{
690				Requirement{"key", selection.In, []string{"value"}},
691				Requirement{"key2", selection.In, []string{"value2"}},
692			},
693			label:         "key",
694			expectedFound: true,
695			expectedValue: "value",
696		},
697	}
698	for _, ts := range testCases {
699		t.Run(ts.name, func(t *testing.T) {
700			value, found := ts.sel.RequiresExactMatch(ts.label)
701			if found != ts.expectedFound {
702				t.Errorf("Expected match %v, found %v", ts.expectedFound, found)
703			}
704			if found && value != ts.expectedValue {
705				t.Errorf("Expected value %v, found %v", ts.expectedValue, value)
706			}
707
708		})
709	}
710}
711