...

Source file src/sigs.k8s.io/structured-merge-diff/v4/fieldpath/set_test.go

Documentation: sigs.k8s.io/structured-merge-diff/v4/fieldpath

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fieldpath
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"math/rand"
    23  	"testing"
    24  
    25  	"gopkg.in/yaml.v2"
    26  	"sigs.k8s.io/structured-merge-diff/v4/schema"
    27  )
    28  
    29  type randomPathAlphabet []PathElement
    30  
    31  func (a randomPathAlphabet) makePath(minLen, maxLen int) Path {
    32  	n := minLen
    33  	if minLen < maxLen {
    34  		n += rand.Intn(maxLen - minLen)
    35  	}
    36  	var p Path
    37  	for i := 0; i < n; i++ {
    38  		p = append(p, a[rand.Intn(len(a))])
    39  	}
    40  	return p
    41  }
    42  
    43  var randomPathMaker = randomPathAlphabet(MakePathOrDie(
    44  	"aaa",
    45  	"aab",
    46  	"aac",
    47  	"aad",
    48  	"aae",
    49  	"aaf",
    50  	KeyByFields("name", "first"),
    51  	KeyByFields("name", "second"),
    52  	KeyByFields("port", 443, "protocol", "tcp"),
    53  	KeyByFields("port", 443, "protocol", "udp"),
    54  	_V(1),
    55  	_V(2),
    56  	_V(3),
    57  	_V("aa"),
    58  	_V("ab"),
    59  	_V(true),
    60  	1, 2, 3, 4,
    61  ))
    62  
    63  func BenchmarkFieldSet(b *testing.B) {
    64  	cases := []struct {
    65  		size       int
    66  		minPathLen int
    67  		maxPathLen int
    68  	}{
    69  		//{10, 1, 2},
    70  		{20, 2, 3},
    71  		{50, 2, 4},
    72  		{100, 3, 6},
    73  		{500, 3, 7},
    74  		{1000, 3, 8},
    75  	}
    76  	for i := range cases {
    77  		here := cases[i]
    78  		makeSet := func() *Set {
    79  			x := NewSet()
    80  			for j := 0; j < here.size; j++ {
    81  				x.Insert(randomPathMaker.makePath(here.minPathLen, here.maxPathLen))
    82  			}
    83  			return x
    84  		}
    85  		operands := make([]*Set, 500)
    86  		serialized := make([][]byte, len(operands))
    87  		for i := range operands {
    88  			operands[i] = makeSet()
    89  			serialized[i], _ = operands[i].ToJSON()
    90  		}
    91  		randOperand := func() *Set { return operands[rand.Intn(len(operands))] }
    92  
    93  		b.Run(fmt.Sprintf("insert-%v", here.size), func(b *testing.B) {
    94  			b.ReportAllocs()
    95  			for i := 0; i < b.N; i++ {
    96  				makeSet()
    97  			}
    98  		})
    99  		b.Run(fmt.Sprintf("has-%v", here.size), func(b *testing.B) {
   100  			b.ReportAllocs()
   101  			for i := 0; i < b.N; i++ {
   102  				randOperand().Has(randomPathMaker.makePath(here.minPathLen, here.maxPathLen))
   103  			}
   104  		})
   105  		b.Run(fmt.Sprintf("serialize-%v", here.size), func(b *testing.B) {
   106  			b.ReportAllocs()
   107  			for i := 0; i < b.N; i++ {
   108  				randOperand().ToJSON()
   109  			}
   110  		})
   111  		b.Run(fmt.Sprintf("deserialize-%v", here.size), func(b *testing.B) {
   112  			b.ReportAllocs()
   113  			s := NewSet()
   114  			for i := 0; i < b.N; i++ {
   115  				s.FromJSON(bytes.NewReader(serialized[rand.Intn(len(serialized))]))
   116  			}
   117  		})
   118  
   119  		b.Run(fmt.Sprintf("union-%v", here.size), func(b *testing.B) {
   120  			b.ReportAllocs()
   121  			for i := 0; i < b.N; i++ {
   122  				randOperand().Union(randOperand())
   123  			}
   124  		})
   125  		b.Run(fmt.Sprintf("intersection-%v", here.size), func(b *testing.B) {
   126  			b.ReportAllocs()
   127  			for i := 0; i < b.N; i++ {
   128  				randOperand().Intersection(randOperand())
   129  			}
   130  		})
   131  		b.Run(fmt.Sprintf("difference-%v", here.size), func(b *testing.B) {
   132  			b.ReportAllocs()
   133  			for i := 0; i < b.N; i++ {
   134  				randOperand().Difference(randOperand())
   135  			}
   136  		})
   137  		b.Run(fmt.Sprintf("recursive-difference-%v", here.size), func(b *testing.B) {
   138  			b.ReportAllocs()
   139  			for i := 0; i < b.N; i++ {
   140  				randOperand().RecursiveDifference(randOperand())
   141  			}
   142  		})
   143  		b.Run(fmt.Sprintf("leaves-%v", here.size), func(b *testing.B) {
   144  			b.ReportAllocs()
   145  			for i := 0; i < b.N; i++ {
   146  				randOperand().Leaves()
   147  			}
   148  		})
   149  	}
   150  }
   151  
   152  func TestSetInsertHas(t *testing.T) {
   153  	s1 := NewSet(
   154  		MakePathOrDie("foo", 0, "bar", "baz"),
   155  		MakePathOrDie("foo", 0, "bar"),
   156  		MakePathOrDie("foo", 0),
   157  		MakePathOrDie("foo", 1, "bar", "baz"),
   158  		MakePathOrDie("foo", 1, "bar"),
   159  		MakePathOrDie("qux", KeyByFields("name", "first")),
   160  		MakePathOrDie("qux", KeyByFields("name", "first"), "bar"),
   161  		MakePathOrDie("qux", KeyByFields("name", "second"), "bar"),
   162  		MakePathOrDie("canonicalOrder", KeyByFields(
   163  			"a", "a",
   164  			"b", "a",
   165  			"c", "a",
   166  			"d", "a",
   167  			"e", "a",
   168  			"f", "a",
   169  		)),
   170  	)
   171  
   172  	table := []struct {
   173  		set              *Set
   174  		check            Path
   175  		expectMembership bool
   176  	}{
   177  		{s1, MakePathOrDie("qux", KeyByFields("name", "second")), false},
   178  		{s1, MakePathOrDie("qux", KeyByFields("name", "second"), "bar"), true},
   179  		{s1, MakePathOrDie("qux", KeyByFields("name", "first")), true},
   180  		{s1, MakePathOrDie("xuq", KeyByFields("name", "first")), false},
   181  		{s1, MakePathOrDie("foo", 0), true},
   182  		{s1, MakePathOrDie("foo", 0, "bar"), true},
   183  		{s1, MakePathOrDie("foo", 0, "bar", "baz"), true},
   184  		{s1, MakePathOrDie("foo", 1), false},
   185  		{s1, MakePathOrDie("foo", 1, "bar"), true},
   186  		{s1, MakePathOrDie("foo", 1, "bar", "baz"), true},
   187  		{s1, MakePathOrDie("canonicalOrder", KeyByFields(
   188  			"f", "a",
   189  			"e", "a",
   190  			"d", "a",
   191  			"c", "a",
   192  			"b", "a",
   193  			"a", "a",
   194  		)), true}}
   195  
   196  	for _, tt := range table {
   197  		got := tt.set.Has(tt.check)
   198  		if e, a := tt.expectMembership, got; e != a {
   199  			t.Errorf("%v: wanted %v, got %v", tt.check.String(), e, a)
   200  		}
   201  	}
   202  
   203  	if NewSet().Has(Path{}) {
   204  		t.Errorf("empty set should not include the empty path")
   205  	}
   206  	if NewSet(Path{}).Has(Path{}) {
   207  		t.Errorf("empty set should not include the empty path")
   208  	}
   209  }
   210  
   211  func TestSetString(t *testing.T) {
   212  	p := MakePathOrDie("foo", KeyByFields("name", "first"))
   213  	s1 := NewSet(p)
   214  
   215  	if p.String() != s1.String() {
   216  		t.Errorf("expected single entry set to just call the path's string, but got %s %s", p, s1)
   217  	}
   218  }
   219  
   220  func TestSetIterSize(t *testing.T) {
   221  	s1 := NewSet(
   222  		MakePathOrDie("foo", 0, "bar", "baz"),
   223  		MakePathOrDie("foo", 0, "bar", "zot"),
   224  		MakePathOrDie("foo", 0, "bar"),
   225  		MakePathOrDie("foo", 0),
   226  		MakePathOrDie("foo", 1, "bar", "baz"),
   227  		MakePathOrDie("foo", 1, "bar"),
   228  		MakePathOrDie("qux", KeyByFields("name", "first")),
   229  		MakePathOrDie("qux", KeyByFields("name", "first"), "bar"),
   230  		MakePathOrDie("qux", KeyByFields("name", "second"), "bar"),
   231  	)
   232  
   233  	s2 := NewSet()
   234  
   235  	addedCount := 0
   236  	s1.Iterate(func(p Path) {
   237  		if s2.Size() != addedCount {
   238  			t.Errorf("added %v items to set, but size is %v", addedCount, s2.Size())
   239  		}
   240  		if addedCount > 0 == s2.Empty() {
   241  			t.Errorf("added %v items to set, but s2.Empty() is %v", addedCount, s2.Empty())
   242  		}
   243  		s2.Insert(p)
   244  		addedCount++
   245  	})
   246  
   247  	if !s1.Equals(s2) {
   248  		// No point in using String() if iterate is broken...
   249  		t.Errorf("Iterate missed something?\n%#v\n%#v", s1, s2)
   250  	}
   251  }
   252  
   253  func TestSetEquals(t *testing.T) {
   254  	table := []struct {
   255  		a     *Set
   256  		b     *Set
   257  		equal bool
   258  	}{
   259  		{
   260  			a:     NewSet(MakePathOrDie("foo")),
   261  			b:     NewSet(MakePathOrDie("bar")),
   262  			equal: false,
   263  		},
   264  		{
   265  			a:     NewSet(MakePathOrDie("foo")),
   266  			b:     NewSet(MakePathOrDie("foo")),
   267  			equal: true,
   268  		},
   269  		{
   270  			a:     NewSet(),
   271  			b:     NewSet(MakePathOrDie(0, "foo")),
   272  			equal: false,
   273  		},
   274  		{
   275  			a:     NewSet(MakePathOrDie(1, "foo")),
   276  			b:     NewSet(MakePathOrDie(0, "foo")),
   277  			equal: false,
   278  		},
   279  		{
   280  			a:     NewSet(MakePathOrDie(1, "foo")),
   281  			b:     NewSet(MakePathOrDie(1, "foo", "bar")),
   282  			equal: false,
   283  		},
   284  		{
   285  			a: NewSet(
   286  				MakePathOrDie(0),
   287  				MakePathOrDie(1),
   288  			),
   289  			b: NewSet(
   290  				MakePathOrDie(1),
   291  				MakePathOrDie(0),
   292  			),
   293  			equal: true,
   294  		},
   295  		{
   296  			a: NewSet(
   297  				MakePathOrDie("foo", 0),
   298  				MakePathOrDie("foo", 1),
   299  			),
   300  			b: NewSet(
   301  				MakePathOrDie("foo", 1),
   302  				MakePathOrDie("foo", 0),
   303  			),
   304  			equal: true,
   305  		},
   306  		{
   307  			a: NewSet(
   308  				MakePathOrDie("foo", 0),
   309  				MakePathOrDie("foo"),
   310  				MakePathOrDie("bar", "baz"),
   311  				MakePathOrDie("qux", KeyByFields("name", "first")),
   312  			),
   313  			b: NewSet(
   314  				MakePathOrDie("foo", 1),
   315  				MakePathOrDie("bar", "baz"),
   316  				MakePathOrDie("bar"),
   317  				MakePathOrDie("qux", KeyByFields("name", "second")),
   318  			),
   319  			equal: false,
   320  		},
   321  	}
   322  
   323  	for _, tt := range table {
   324  		if e, a := tt.equal, tt.a.Equals(tt.b); e != a {
   325  			t.Errorf("expected %v, got %v for:\na=\n%v\nb=\n%v", e, a, tt.a, tt.b)
   326  		}
   327  	}
   328  }
   329  
   330  func TestSetUnion(t *testing.T) {
   331  	// Even though this is not a table driven test, since the thing under
   332  	// test is recursive, we should be able to craft a single input that is
   333  	// sufficient to check all code paths.
   334  
   335  	s1 := NewSet(
   336  		MakePathOrDie("foo", 0),
   337  		MakePathOrDie("foo"),
   338  		MakePathOrDie("bar", "baz"),
   339  		MakePathOrDie("qux", KeyByFields("name", "first")),
   340  		MakePathOrDie("parent", "child", "grandchild"),
   341  	)
   342  
   343  	s2 := NewSet(
   344  		MakePathOrDie("foo", 1),
   345  		MakePathOrDie("bar", "baz"),
   346  		MakePathOrDie("bar"),
   347  		MakePathOrDie("qux", KeyByFields("name", "second")),
   348  		MakePathOrDie("parent", "child"),
   349  	)
   350  
   351  	u := NewSet(
   352  		MakePathOrDie("foo", 0),
   353  		MakePathOrDie("foo", 1),
   354  		MakePathOrDie("foo"),
   355  		MakePathOrDie("bar", "baz"),
   356  		MakePathOrDie("bar"),
   357  		MakePathOrDie("qux", KeyByFields("name", "first")),
   358  		MakePathOrDie("qux", KeyByFields("name", "second")),
   359  		MakePathOrDie("parent", "child"),
   360  		MakePathOrDie("parent", "child", "grandchild"),
   361  	)
   362  
   363  	got := s1.Union(s2)
   364  
   365  	if !got.Equals(u) {
   366  		t.Errorf("union: expected: \n%v\n, got \n%v\n", u, got)
   367  	}
   368  }
   369  
   370  func TestSetIntersectionDifference(t *testing.T) {
   371  	// Even though this is not a table driven test, since the thing under
   372  	// test is recursive, we should be able to craft a single input that is
   373  	// sufficient to check all code paths.
   374  
   375  	nameFirst := KeyByFields("name", "first")
   376  	s1 := NewSet(
   377  		MakePathOrDie("a0"),
   378  		MakePathOrDie("a1"),
   379  		MakePathOrDie("foo", 0),
   380  		MakePathOrDie("foo", 1),
   381  		MakePathOrDie("b0", nameFirst),
   382  		MakePathOrDie("b1", nameFirst),
   383  		MakePathOrDie("bar", "c0"),
   384  
   385  		MakePathOrDie("cp", nameFirst, "child"),
   386  	)
   387  
   388  	s2 := NewSet(
   389  		MakePathOrDie("a1"),
   390  		MakePathOrDie("a2"),
   391  		MakePathOrDie("foo", 1),
   392  		MakePathOrDie("foo", 2),
   393  		MakePathOrDie("b1", nameFirst),
   394  		MakePathOrDie("b2", nameFirst),
   395  		MakePathOrDie("bar", "c2"),
   396  
   397  		MakePathOrDie("cp", nameFirst),
   398  	)
   399  	t.Logf("s1:\n%v\n", s1)
   400  	t.Logf("s2:\n%v\n", s2)
   401  
   402  	t.Run("intersection", func(t *testing.T) {
   403  		i := NewSet(
   404  			MakePathOrDie("a1"),
   405  			MakePathOrDie("foo", 1),
   406  			MakePathOrDie("b1", nameFirst),
   407  		)
   408  
   409  		got := s1.Intersection(s2)
   410  		if !got.Equals(i) {
   411  			t.Errorf("expected: \n%v\n, got \n%v\n", i, got)
   412  		}
   413  	})
   414  
   415  	t.Run("s1 - s2", func(t *testing.T) {
   416  		sDiffS2 := NewSet(
   417  			MakePathOrDie("a0"),
   418  			MakePathOrDie("foo", 0),
   419  			MakePathOrDie("b0", nameFirst),
   420  			MakePathOrDie("bar", "c0"),
   421  			MakePathOrDie("cp", nameFirst, "child"),
   422  		)
   423  
   424  		got := s1.Difference(s2)
   425  		if !got.Equals(sDiffS2) {
   426  			t.Errorf("expected: \n%v\n, got \n%v\n", sDiffS2, got)
   427  		}
   428  	})
   429  
   430  	t.Run("s2 - s1", func(t *testing.T) {
   431  		s2DiffS := NewSet(
   432  			MakePathOrDie("a2"),
   433  			MakePathOrDie("foo", 2),
   434  			MakePathOrDie("b2", nameFirst),
   435  			MakePathOrDie("bar", "c2"),
   436  			MakePathOrDie("cp", nameFirst),
   437  		)
   438  
   439  		got := s2.Difference(s1)
   440  		if !got.Equals(s2DiffS) {
   441  			t.Errorf("expected: \n%v\n, got \n%v\n", s2DiffS, got)
   442  		}
   443  	})
   444  
   445  	t.Run("intersection (the hard way)", func(t *testing.T) {
   446  		i := NewSet(
   447  			MakePathOrDie("a1"),
   448  			MakePathOrDie("foo", 1),
   449  			MakePathOrDie("b1", nameFirst),
   450  		)
   451  
   452  		// We can construct Intersection out of two union and
   453  		// three difference calls.
   454  		u := s1.Union(s2)
   455  		t.Logf("s1 u s2:\n%v\n", u)
   456  		notIntersection := s2.Difference(s1).Union(s1.Difference(s2))
   457  		t.Logf("s1 !i s2:\n%v\n", notIntersection)
   458  		got := u.Difference(notIntersection)
   459  		if !got.Equals(i) {
   460  			t.Errorf("expected: \n%v\n, got \n%v\n", i, got)
   461  		}
   462  	})
   463  }
   464  
   465  func TestSetLeaves(t *testing.T) {
   466  	table := []struct {
   467  		name     string
   468  		input    *Set
   469  		expected *Set
   470  	}{
   471  		{
   472  			name:     "empty set",
   473  			input:    NewSet(),
   474  			expected: NewSet(),
   475  		}, {
   476  			name: "all leaves",
   477  			input: NewSet(
   478  				_P("path1"),
   479  				_P("path2"),
   480  				_P("path3"),
   481  			),
   482  			expected: NewSet(
   483  				_P("path1"),
   484  				_P("path2"),
   485  				_P("path3"),
   486  			),
   487  		}, {
   488  			name: "only one leaf",
   489  			input: NewSet(
   490  				_P("root"),
   491  				_P("root", "l1"),
   492  				_P("root", "l1", "l2"),
   493  				_P("root", "l1", "l2", "l3"),
   494  			),
   495  			expected: NewSet(
   496  				_P("root", "l1", "l2", "l3"),
   497  			),
   498  		}, {
   499  			name: "multiple values, check for overwrite",
   500  			input: NewSet(
   501  				_P("root", KeyByFields("name", "a")),
   502  				_P("root", KeyByFields("name", "a"), "name"),
   503  				_P("root", KeyByFields("name", "a"), "value", "b"),
   504  				_P("root", KeyByFields("name", "a"), "value", "c"),
   505  			),
   506  			expected: NewSet(
   507  				_P("root", KeyByFields("name", "a"), "name"),
   508  				_P("root", KeyByFields("name", "a"), "value", "b"),
   509  				_P("root", KeyByFields("name", "a"), "value", "c"),
   510  			),
   511  		}, {
   512  			name: "multiple values and nested",
   513  			input: NewSet(
   514  				_P("root", KeyByFields("name", "a")),
   515  				_P("root", KeyByFields("name", "a"), "name"),
   516  				_P("root", KeyByFields("name", "a"), "value", "b"),
   517  				_P("root", KeyByFields("name", "a"), "value", "b", "d"),
   518  				_P("root", KeyByFields("name", "a"), "value", "c"),
   519  			),
   520  			expected: NewSet(
   521  				_P("root", KeyByFields("name", "a"), "name"),
   522  				_P("root", KeyByFields("name", "a"), "value", "b", "d"),
   523  				_P("root", KeyByFields("name", "a"), "value", "c"),
   524  			),
   525  		}, {
   526  			name: "all-in-one",
   527  			input: NewSet(
   528  				_P("root"),
   529  				_P("root", KeyByFields("name", "a")),
   530  				_P("root", KeyByFields("name", "a"), "name"),
   531  				_P("root", KeyByFields("name", "a"), "value", "b"),
   532  				_P("root", KeyByFields("name", "a"), "value", "b", "c"),
   533  				_P("root", KeyByFields("name", "a"), "value", "d"),
   534  				_P("root", KeyByFields("name", "a"), "value", "e"),
   535  				_P("root", "x"),
   536  				_P("root", "x", "y"),
   537  				_P("root", "x", "z"),
   538  				_P("root", KeyByFields("name", "p")),
   539  				_P("root", KeyByFields("name", "p"), "name"),
   540  				_P("root", KeyByFields("name", "p"), "value", "q"),
   541  			),
   542  			expected: NewSet(
   543  				_P("root", KeyByFields("name", "a"), "name"),
   544  				_P("root", KeyByFields("name", "a"), "value", "b", "c"),
   545  				_P("root", KeyByFields("name", "a"), "value", "d"),
   546  				_P("root", KeyByFields("name", "a"), "value", "e"),
   547  				_P("root", "x", "y"),
   548  				_P("root", "x", "z"),
   549  				_P("root", KeyByFields("name", "p"), "name"),
   550  				_P("root", KeyByFields("name", "p"), "value", "q"),
   551  			),
   552  		},
   553  	}
   554  
   555  	for _, tt := range table {
   556  		t.Run(tt.name, func(t *testing.T) {
   557  			if got := tt.input.Leaves(); !tt.expected.Equals(got) {
   558  				t.Errorf("expected %v, got %v for input %v", tt.expected, got, tt.input)
   559  			}
   560  		})
   561  	}
   562  
   563  }
   564  
   565  func TestSetDifference(t *testing.T) {
   566  	table := []struct {
   567  		name                      string
   568  		a                         *Set
   569  		b                         *Set
   570  		expectDifference          *Set
   571  		expectRecursiveDifference *Set
   572  	}{
   573  		{
   574  			name:                      "removes simple path",
   575  			a:                         NewSet(MakePathOrDie("a")),
   576  			b:                         NewSet(MakePathOrDie("a")),
   577  			expectDifference:          NewSet(),
   578  			expectRecursiveDifference: NewSet(),
   579  		},
   580  		{
   581  			name:                      "removes direct path",
   582  			a:                         NewSet(MakePathOrDie("a", "b", "c")),
   583  			b:                         NewSet(MakePathOrDie("a", "b", "c")),
   584  			expectDifference:          NewSet(),
   585  			expectRecursiveDifference: NewSet(),
   586  		},
   587  		{
   588  			name: "only removes matching child",
   589  			a: NewSet(
   590  				MakePathOrDie("a", "b", "c"),
   591  				MakePathOrDie("b", "b", "c"),
   592  			),
   593  			b:                         NewSet(MakePathOrDie("a", "b", "c")),
   594  			expectDifference:          NewSet(MakePathOrDie("b", "b", "c")),
   595  			expectRecursiveDifference: NewSet(MakePathOrDie("b", "b", "c")),
   596  		},
   597  		{
   598  			name: "does not remove parent of specific path",
   599  			a: NewSet(
   600  				MakePathOrDie("a"),
   601  			),
   602  			b:                         NewSet(MakePathOrDie("a", "aa")),
   603  			expectDifference:          NewSet(MakePathOrDie("a")),
   604  			expectRecursiveDifference: NewSet(MakePathOrDie("a")),
   605  		},
   606  		{
   607  			name:                      "RecursiveDifference removes nested path",
   608  			a:                         NewSet(MakePathOrDie("a", "b", "c")),
   609  			b:                         NewSet(MakePathOrDie("a")),
   610  			expectDifference:          NewSet(MakePathOrDie("a", "b", "c")),
   611  			expectRecursiveDifference: NewSet(),
   612  		},
   613  		{
   614  			name: "RecursiveDifference only removes nested path for matching children",
   615  			a: NewSet(
   616  				MakePathOrDie("a", "aa", "aab"),
   617  				MakePathOrDie("a", "ab", "aba"),
   618  			),
   619  			b: NewSet(MakePathOrDie("a", "aa")),
   620  			expectDifference: NewSet(
   621  				MakePathOrDie("a", "aa", "aab"),
   622  				MakePathOrDie("a", "ab", "aba"),
   623  			),
   624  			expectRecursiveDifference: NewSet(MakePathOrDie("a", "ab", "aba")),
   625  		},
   626  		{
   627  			name: "RecursiveDifference removes all matching children",
   628  			a: NewSet(
   629  				MakePathOrDie("a", "aa", "aab"),
   630  				MakePathOrDie("a", "ab", "aba"),
   631  			),
   632  			b: NewSet(MakePathOrDie("a")),
   633  			expectDifference: NewSet(
   634  				MakePathOrDie("a", "aa", "aab"),
   635  				MakePathOrDie("a", "ab", "aba"),
   636  			),
   637  			expectRecursiveDifference: NewSet(),
   638  		},
   639  	}
   640  
   641  	for _, c := range table {
   642  		t.Run(c.name, func(t *testing.T) {
   643  			if result := c.a.Difference(c.b); !result.Equals(c.expectDifference) {
   644  				t.Fatalf("Difference expected: \n%v\n, got: \n%v\n", c.expectDifference, result)
   645  			}
   646  			if result := c.a.RecursiveDifference(c.b); !result.Equals(c.expectRecursiveDifference) {
   647  				t.Fatalf("RecursiveDifference expected: \n%v\n, got: \n%v\n", c.expectRecursiveDifference, result)
   648  			}
   649  		})
   650  	}
   651  }
   652  
   653  var nestedSchema = func() (*schema.Schema, schema.TypeRef) {
   654  	sc := &schema.Schema{}
   655  	name := "type"
   656  	err := yaml.Unmarshal([]byte(`types:
   657  - name: type
   658    map:
   659      elementType:
   660        namedType: type
   661      fields:
   662        - name: named
   663          type:
   664            namedType: type
   665        - name: list
   666          type:
   667            list:
   668              elementRelationShip: associative
   669              keys: ["name"]
   670              elementType:
   671                namedType: type
   672        - name: value
   673          type:
   674            scalar: numeric
   675  `), &sc)
   676  	if err != nil {
   677  		panic(err)
   678  	}
   679  	return sc, schema.TypeRef{NamedType: &name}
   680  }
   681  
   682  var _P = MakePathOrDie
   683  
   684  func TestEnsureNamedFieldsAreMembers(t *testing.T) {
   685  	table := []struct {
   686  		set, expected *Set
   687  	}{
   688  		{
   689  			set: NewSet(_P("named", "named", "value")),
   690  			expected: NewSet(
   691  				_P("named", "named", "value"),
   692  				_P("named", "named"),
   693  				_P("named"),
   694  			),
   695  		},
   696  		{
   697  			set: NewSet(_P("named", "a", "named", "value"), _P("a", "named", "value"), _P("a", "b", "value")),
   698  			expected: NewSet(
   699  				_P("named", "a", "named", "value"),
   700  				_P("named", "a", "named"),
   701  				_P("named"),
   702  				_P("a", "named", "value"),
   703  				_P("a", "named"),
   704  				_P("a", "b", "value"),
   705  			),
   706  		},
   707  		{
   708  			set: NewSet(_P("named", "list", KeyByFields("name", "a"), "named", "a", "value")),
   709  			expected: NewSet(
   710  				_P("named", "list", KeyByFields("name", "a"), "named", "a", "value"),
   711  				_P("named", "list", KeyByFields("name", "a"), "named"),
   712  				_P("named", "list"),
   713  				_P("named"),
   714  			),
   715  		},
   716  	}
   717  
   718  	for _, test := range table {
   719  		t.Run(fmt.Sprintf("%v", test.set), func(t *testing.T) {
   720  			got := test.set.EnsureNamedFieldsAreMembers(nestedSchema())
   721  			if !got.Equals(test.expected) {
   722  				t.Errorf("expected %v, got %v (missing: %v/superfluous: %v)",
   723  					test.expected,
   724  					got,
   725  					test.expected.Difference(got),
   726  					got.Difference(test.expected),
   727  				)
   728  			}
   729  		})
   730  	}
   731  }
   732  
   733  func TestSetNodeMapIterate(t *testing.T) {
   734  	set := &SetNodeMap{}
   735  	toAdd := 5
   736  	addedElements := make([]string, toAdd)
   737  	for i := 0; i < toAdd; i++ {
   738  		p := i
   739  		pe := PathElement{Index: &p}
   740  		addedElements[i] = pe.String()
   741  		_ = set.Descend(pe)
   742  	}
   743  
   744  	iteratedElements := make(map[string]bool, toAdd)
   745  	set.Iterate(func(pe PathElement) {
   746  		iteratedElements[pe.String()] = true
   747  	})
   748  
   749  	if len(iteratedElements) != toAdd {
   750  		t.Errorf("expected %v elements to be iterated over, got %v", toAdd, len(iteratedElements))
   751  	}
   752  	for _, pe := range addedElements {
   753  		if _, ok := iteratedElements[pe]; !ok {
   754  			t.Errorf("expected to have iterated over %v, but never did", pe)
   755  		}
   756  	}
   757  }
   758  

View as plain text