istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/util/sets/set_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 sets
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"testing"
    21  
    22  	"k8s.io/apimachinery/pkg/util/rand"
    23  
    24  	"istio.io/istio/pkg/test/util/assert"
    25  )
    26  
    27  func TestNewSet(t *testing.T) {
    28  	elements := []string{"a", "b", "c"}
    29  	set := New(elements...)
    30  
    31  	if len(set) != len(elements) {
    32  		t.Errorf("Expected length %d != %d", len(set), len(elements))
    33  	}
    34  
    35  	for _, e := range elements {
    36  		if _, exist := set[e]; !exist {
    37  			t.Errorf("%s is not in set %v", e, set)
    38  		}
    39  	}
    40  }
    41  
    42  func TestUnion(t *testing.T) {
    43  	elements := []string{"a", "b", "c", "d"}
    44  	elements2 := []string{"a", "b", "e"}
    45  	want := New("a", "b", "c", "d", "e")
    46  	for _, sets := range [][]Set[string]{
    47  		{New(elements...), New(elements2...)},
    48  		{New(elements2...), New(elements...)},
    49  	} {
    50  		s1, s2 := sets[0], sets[1]
    51  		if got := s1.Union(s2); !got.Equals(want) {
    52  			t.Errorf("expected %v; got %v", want, got)
    53  		}
    54  	}
    55  }
    56  
    57  func TestDifference(t *testing.T) {
    58  	s1 := New("a", "b", "c", "d")
    59  	s2 := New("a", "b", "e")
    60  	want := New("c", "d")
    61  
    62  	t.Run("difference", func(t *testing.T) {
    63  		d := s1.Difference(s2)
    64  		if !want.Equals(d) {
    65  			t.Errorf("want %+v, but got %+v", want, d)
    66  		}
    67  	})
    68  
    69  	t.Run("difference in place", func(t *testing.T) {
    70  		s1c := s1.Copy()
    71  		r := s1c.DifferenceInPlace(s2)
    72  		if !want.Equals(r) {
    73  			t.Errorf("want %+v, but got %+v", want, r)
    74  		}
    75  		// s1c should be updated
    76  		if !want.Equals(s1c) {
    77  			t.Errorf("want %+v, but got %+v", want, s1c)
    78  		}
    79  	})
    80  }
    81  
    82  func TestIntersection(t *testing.T) {
    83  	s1 := New("a", "b", "d")
    84  	s2 := New("a", "b", "c")
    85  	want := New("a", "b")
    86  
    87  	t.Run("intersection", func(t *testing.T) {
    88  		d := s1.Intersection(s2)
    89  		if !d.Equals(want) {
    90  			t.Errorf("want %+v, but got %+v", want, d)
    91  		}
    92  	})
    93  
    94  	t.Run("intersect in replace", func(t *testing.T) {
    95  		s1c := s1.Copy()
    96  		d := s1c.IntersectInPlace(s2)
    97  		if !want.Equals(d) {
    98  			t.Errorf("want %+v, but got %+v", want, d)
    99  		}
   100  		// s1c should be updated
   101  		if !want.Equals(s1c) {
   102  			t.Errorf("want %+v, but got %+v", want, s1c)
   103  		}
   104  	})
   105  }
   106  
   107  func TestSupersetOf(t *testing.T) {
   108  	testCases := []struct {
   109  		name   string
   110  		first  Set[string]
   111  		second Set[string]
   112  		want   bool
   113  	}{
   114  		{
   115  			name:   "both nil",
   116  			first:  nil,
   117  			second: nil,
   118  			want:   true,
   119  		},
   120  		{
   121  			name:   "first nil",
   122  			first:  nil,
   123  			second: New("a"),
   124  			want:   false,
   125  		},
   126  		{
   127  			name:   "second nil",
   128  			first:  New("a"),
   129  			second: nil,
   130  			want:   true,
   131  		},
   132  		{
   133  			name:   "both empty",
   134  			first:  New[string](),
   135  			second: New[string](),
   136  			want:   true,
   137  		},
   138  		{
   139  			name:   "first empty",
   140  			first:  New[string](),
   141  			second: New("a"),
   142  			want:   false,
   143  		},
   144  		{
   145  			name:   "second empty",
   146  			first:  New("a"),
   147  			second: New[string](),
   148  			want:   true,
   149  		},
   150  		{
   151  			name:   "equal",
   152  			first:  New("a", "b"),
   153  			second: New("a", "b"),
   154  			want:   true,
   155  		},
   156  		{
   157  			name:   "first contains all second",
   158  			first:  New("a", "b", "c"),
   159  			second: New("a", "b"),
   160  			want:   true,
   161  		},
   162  		{
   163  			name:   "second contains all first",
   164  			first:  New("a", "b"),
   165  			second: New("a", "b", "c"),
   166  			want:   false,
   167  		},
   168  	}
   169  	for _, tt := range testCases {
   170  		t.Run(tt.name, func(t *testing.T) {
   171  			if got := tt.first.SupersetOf(tt.second); got != tt.want {
   172  				t.Errorf("want %v, but got %v", tt.want, got)
   173  			}
   174  		})
   175  	}
   176  }
   177  
   178  func BenchmarkSupersetOf(b *testing.B) {
   179  	set1 := New[string]()
   180  	for i := 0; i < 1000; i++ {
   181  		set1.Insert(fmt.Sprint(i))
   182  	}
   183  	set2 := New[string]()
   184  	for i := 0; i < 50; i++ {
   185  		set2.Insert(fmt.Sprint(i))
   186  	}
   187  	b.ResetTimer()
   188  
   189  	b.Run("SupersetOf", func(b *testing.B) {
   190  		for n := 0; n < b.N; n++ {
   191  			set1.SupersetOf(set2)
   192  		}
   193  	})
   194  }
   195  
   196  func TestEquals(t *testing.T) {
   197  	tests := []struct {
   198  		name   string
   199  		first  Set[string]
   200  		second Set[string]
   201  		want   bool
   202  	}{
   203  		{
   204  			"both nil",
   205  			nil,
   206  			nil,
   207  			true,
   208  		},
   209  		{
   210  			"unequal length",
   211  			New("test"),
   212  			New("test", "test1"),
   213  			false,
   214  		},
   215  		{
   216  			"equal sets",
   217  			New("test", "test1"),
   218  			New("test", "test1"),
   219  			true,
   220  		},
   221  		{
   222  			"unequal sets",
   223  			New("test", "test1"),
   224  			New("test", "test2"),
   225  			false,
   226  		},
   227  	}
   228  	for _, tt := range tests {
   229  		t.Run(tt.name, func(t *testing.T) {
   230  			if got := tt.first.Equals(tt.second); got != tt.want {
   231  				t.Errorf("Unexpected Equal. got %v, want %v", got, tt.want)
   232  			}
   233  		})
   234  	}
   235  }
   236  
   237  func TestMerge(t *testing.T) {
   238  	cases := []struct {
   239  		s1, s2   Set[string]
   240  		expected []string
   241  	}{
   242  		{
   243  			s1:       New("a1", "a2"),
   244  			s2:       New("a1", "a2"),
   245  			expected: []string{"a1", "a2"},
   246  		},
   247  		{
   248  			s1:       New("a1", "a2", "a3"),
   249  			s2:       New("a1", "a2"),
   250  			expected: []string{"a1", "a2", "a3"},
   251  		},
   252  		{
   253  			s1:       New("a1", "a2"),
   254  			s2:       New("a3", "a4"),
   255  			expected: []string{"a1", "a2", "a3", "a4"},
   256  		},
   257  	}
   258  
   259  	for _, tc := range cases {
   260  		got := tc.s1.Merge(tc.s2)
   261  		assert.Equal(t, tc.expected, SortedList(got))
   262  	}
   263  }
   264  
   265  func TestInsertAll(t *testing.T) {
   266  	tests := []struct {
   267  		name  string
   268  		s     Set[string]
   269  		items []string
   270  		want  Set[string]
   271  	}{
   272  		{
   273  			name:  "insert new item",
   274  			s:     New("a1", "a2"),
   275  			items: []string{"a3"},
   276  			want:  New("a1", "a2", "a3"),
   277  		},
   278  		{
   279  			name:  "inserted item already exists",
   280  			s:     New("a1", "a2"),
   281  			items: []string{"a1"},
   282  			want:  New("a1", "a2"),
   283  		},
   284  	}
   285  	for _, tt := range tests {
   286  		t.Run(tt.name, func(t *testing.T) {
   287  			if got := tt.s.InsertAll(tt.items...); !reflect.DeepEqual(got, tt.want) {
   288  				t.Errorf("InsertAll() = %v, want %v", got, tt.want)
   289  			}
   290  		})
   291  	}
   292  }
   293  
   294  func TestInsertContains(t *testing.T) {
   295  	s := New[string]()
   296  	assert.Equal(t, s.InsertContains("k1"), false)
   297  	assert.Equal(t, s.InsertContains("k1"), true)
   298  	assert.Equal(t, s.InsertContains("k2"), false)
   299  	assert.Equal(t, s.InsertContains("k2"), true)
   300  }
   301  
   302  func BenchmarkSet(b *testing.B) {
   303  	containsTest := New[string]()
   304  	for i := 0; i < 1000; i++ {
   305  		containsTest.Insert(fmt.Sprint(i))
   306  	}
   307  	sortOrder := []string{}
   308  	for i := 0; i < 1000; i++ {
   309  		sortOrder = append(sortOrder, fmt.Sprint(rand.Intn(1000)))
   310  	}
   311  	b.ResetTimer()
   312  	var s Set[string] // ensure no inlining
   313  	b.Run("insert", func(b *testing.B) {
   314  		for n := 0; n < b.N; n++ {
   315  			s = New[string]()
   316  			for i := 0; i < 1000; i++ {
   317  				s.Insert("item")
   318  			}
   319  		}
   320  	})
   321  	b.Run("contains", func(b *testing.B) {
   322  		for n := 0; n < b.N; n++ {
   323  			containsTest.Contains("100")
   324  		}
   325  	})
   326  	b.Run("sorted", func(b *testing.B) {
   327  		for n := 0; n < b.N; n++ {
   328  			b.StopTimer()
   329  			s := New(sortOrder...)
   330  			b.StartTimer()
   331  			SortedList(s)
   332  		}
   333  	})
   334  }
   335  
   336  func TestMapOfSet(t *testing.T) {
   337  	m := map[int]String{}
   338  	InsertOrNew(m, 1, "a")
   339  	InsertOrNew(m, 1, "b")
   340  	InsertOrNew(m, 2, "c")
   341  	assert.Equal(t, m, map[int]String{1: New("a", "b"), 2: New("c")})
   342  
   343  	DeleteCleanupLast(m, 1, "a")
   344  	assert.Equal(t, m, map[int]String{1: New("b"), 2: New("c")})
   345  	DeleteCleanupLast(m, 1, "b")
   346  	DeleteCleanupLast(m, 1, "not found")
   347  	assert.Equal(t, m, map[int]String{2: New("c")})
   348  }
   349  
   350  func TestSetString(t *testing.T) {
   351  	elements := []string{"a"}
   352  	set := New(elements...)
   353  	assert.Equal(t, "[a]", set.String())
   354  }
   355  
   356  func BenchmarkOperateInPlace(b *testing.B) {
   357  	s1 := New[int]()
   358  	for i := 0; i < 100; i++ {
   359  		s1.Insert(i)
   360  	}
   361  	s2 := New[int]()
   362  	for i := 0; i < 100; i += 2 {
   363  		s2.Insert(i)
   364  	}
   365  	b.ResetTimer()
   366  
   367  	b.Run("Difference", func(b *testing.B) {
   368  		for i := 0; i < b.N; i++ {
   369  			s1.Difference(s2)
   370  		}
   371  	})
   372  	b.Run("DifferenceInPlace", func(b *testing.B) {
   373  		for i := 0; i < b.N; i++ {
   374  			s1.DifferenceInPlace(s2)
   375  		}
   376  	})
   377  	b.Run("Intersection", func(b *testing.B) {
   378  		for i := 0; i < b.N; i++ {
   379  			s1.Intersection(s2)
   380  		}
   381  	})
   382  	b.Run("IntersectInPlace", func(b *testing.B) {
   383  		for i := 0; i < b.N; i++ {
   384  			s1.IntersectInPlace(s2)
   385  		}
   386  	})
   387  }