github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/path/map_test.go (about)

     1  // Copyright (c) 2017 Arista Networks, Inc.
     2  // Use of this source code is governed by the Apache License 2.0
     3  // that can be found in the COPYING file.
     4  
     5  package path
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/aristanetworks/goarista/key"
    13  	"github.com/aristanetworks/goarista/test"
    14  )
    15  
    16  func accumulator(counter map[int]int) VisitorFunc {
    17  	return func(val interface{}) error {
    18  		counter[val.(int)]++
    19  		return nil
    20  	}
    21  }
    22  
    23  func TestIsEmpty(t *testing.T) {
    24  	m := Map{}
    25  
    26  	if !m.IsEmpty() {
    27  		t.Errorf("Expected IsEmpty() to return true; Got false")
    28  	}
    29  
    30  	nonWildcardPath := key.Path{key.New("foo")}
    31  	wildcardPath := key.Path{Wildcard, key.New("bar"), key.New("baz")}
    32  
    33  	m.Set(nonWildcardPath, 0)
    34  	if m.IsEmpty() {
    35  		t.Errorf("Expected IsEmpty() to return false; Got true")
    36  	}
    37  
    38  	m.Set(wildcardPath, 2)
    39  	if m.IsEmpty() {
    40  		t.Errorf("Expected IsEmpty() to return false; Got true")
    41  	}
    42  
    43  	m.Delete(nonWildcardPath)
    44  	if m.IsEmpty() {
    45  		t.Errorf("Expected IsEmpty() to return false; Got true")
    46  	}
    47  
    48  	m.Delete(wildcardPath)
    49  	if !m.IsEmpty() {
    50  		t.Errorf("Expected IsEmpty() to return true; Got false")
    51  	}
    52  
    53  	m.Set(nil, nil)
    54  	if m.IsEmpty() {
    55  		t.Errorf("Expected IsEmpty() to return false; Got true")
    56  	}
    57  
    58  	m.Delete(nil)
    59  	if !m.IsEmpty() {
    60  		t.Errorf("Expected IsEmpty() to return true; Got false")
    61  	}
    62  }
    63  
    64  func TestMapSet(t *testing.T) {
    65  	m := Map{}
    66  	a := m.Set(key.Path{key.New("foo")}, 0)
    67  	b := m.Set(key.Path{key.New("foo")}, 1)
    68  	if !a || b {
    69  		t.Fatal("Map.Set not working properly")
    70  	}
    71  }
    72  
    73  func TestMapVisit(t *testing.T) {
    74  	m := Map{}
    75  	m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 1)
    76  	m.Set(key.Path{Wildcard, key.New("bar"), key.New("baz")}, 2)
    77  	m.Set(key.Path{Wildcard, Wildcard, key.New("baz")}, 3)
    78  	m.Set(key.Path{Wildcard, Wildcard, Wildcard}, 4)
    79  	m.Set(key.Path{key.New("foo"), Wildcard, Wildcard}, 5)
    80  	m.Set(key.Path{key.New("foo"), key.New("bar"), Wildcard}, 6)
    81  	m.Set(key.Path{key.New("foo"), Wildcard, key.New("baz")}, 7)
    82  	m.Set(key.Path{Wildcard, key.New("bar"), Wildcard}, 8)
    83  
    84  	m.Set(key.Path{}, 10)
    85  
    86  	m.Set(key.Path{Wildcard}, 20)
    87  	m.Set(key.Path{key.New("foo")}, 21)
    88  
    89  	m.Set(key.Path{key.New("zap"), key.New("zip")}, 30)
    90  	m.Set(key.Path{key.New("zap"), key.New("zip")}, 31)
    91  
    92  	m.Set(key.Path{key.New("zip"), Wildcard}, 40)
    93  	m.Set(key.Path{key.New("zip"), Wildcard}, 41)
    94  
    95  	testCases := []struct {
    96  		path     key.Path
    97  		expected map[int]int
    98  	}{{
    99  		path:     key.Path{key.New("foo"), key.New("bar"), key.New("baz")},
   100  		expected: map[int]int{1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1},
   101  	}, {
   102  		path:     key.Path{key.New("qux"), key.New("bar"), key.New("baz")},
   103  		expected: map[int]int{2: 1, 3: 1, 4: 1, 8: 1},
   104  	}, {
   105  		path:     key.Path{key.New("foo"), key.New("qux"), key.New("baz")},
   106  		expected: map[int]int{3: 1, 4: 1, 5: 1, 7: 1},
   107  	}, {
   108  		path:     key.Path{key.New("foo"), key.New("bar"), key.New("qux")},
   109  		expected: map[int]int{4: 1, 5: 1, 6: 1, 8: 1},
   110  	}, {
   111  		path:     key.Path{},
   112  		expected: map[int]int{10: 1},
   113  	}, {
   114  		path:     key.Path{key.New("foo")},
   115  		expected: map[int]int{20: 1, 21: 1},
   116  	}, {
   117  		path:     key.Path{key.New("foo"), key.New("bar")},
   118  		expected: map[int]int{},
   119  	}, {
   120  		path:     key.Path{key.New("zap"), key.New("zip")},
   121  		expected: map[int]int{31: 1},
   122  	}, {
   123  		path:     key.Path{key.New("zip"), key.New("zap")},
   124  		expected: map[int]int{41: 1},
   125  	}}
   126  
   127  	for _, tc := range testCases {
   128  		result := make(map[int]int, len(tc.expected))
   129  		m.Visit(tc.path, accumulator(result))
   130  		if diff := test.Diff(tc.expected, result); diff != "" {
   131  			t.Errorf("Test case %v: %s", tc.path, diff)
   132  		}
   133  	}
   134  }
   135  
   136  func TestMapVisitError(t *testing.T) {
   137  	m := Map{}
   138  	m.Set(key.Path{key.New("foo"), key.New("bar")}, 1)
   139  	m.Set(key.Path{Wildcard, key.New("bar")}, 2)
   140  
   141  	errTest := errors.New("Test")
   142  
   143  	err := m.Visit(key.Path{key.New("foo"), key.New("bar")},
   144  		func(v interface{}) error { return errTest })
   145  	if err != errTest {
   146  		t.Errorf("Unexpected error. Expected: %v, Got: %v", errTest, err)
   147  	}
   148  	err = m.VisitPrefixes(key.Path{key.New("foo"), key.New("bar"), key.New("baz")},
   149  		func(v interface{}) error { return errTest })
   150  	if err != errTest {
   151  		t.Errorf("Unexpected error. Expected: %v, Got: %v", errTest, err)
   152  	}
   153  }
   154  
   155  func TestMapGet(t *testing.T) {
   156  	m := Map{}
   157  	m.Set(key.Path{}, 0)
   158  	m.Set(key.Path{key.New("foo"), key.New("bar")}, 1)
   159  	m.Set(key.Path{key.New("foo"), Wildcard}, 2)
   160  	m.Set(key.Path{Wildcard, key.New("bar")}, 3)
   161  	m.Set(key.Path{key.New("zap"), key.New("zip")}, 4)
   162  	m.Set(key.Path{key.New("baz"), key.New("qux")}, nil)
   163  
   164  	testCases := []struct {
   165  		path key.Path
   166  		v    interface{}
   167  		ok   bool
   168  	}{{
   169  		path: nil,
   170  		v:    0,
   171  		ok:   true,
   172  	}, {
   173  		path: key.Path{key.New("foo"), key.New("bar")},
   174  		v:    1,
   175  		ok:   true,
   176  	}, {
   177  		path: key.Path{key.New("foo"), Wildcard},
   178  		v:    2,
   179  		ok:   true,
   180  	}, {
   181  		path: key.Path{Wildcard, key.New("bar")},
   182  		v:    3,
   183  		ok:   true,
   184  	}, {
   185  		path: key.Path{key.New("baz"), key.New("qux")},
   186  		v:    nil,
   187  		ok:   true,
   188  	}, {
   189  		path: key.Path{key.New("bar"), key.New("foo")},
   190  		v:    nil,
   191  	}, {
   192  		path: key.Path{key.New("zap"), Wildcard},
   193  		v:    nil,
   194  	}}
   195  
   196  	for _, tc := range testCases {
   197  		v, ok := m.Get(tc.path)
   198  		if v != tc.v || ok != tc.ok {
   199  			t.Errorf("Test case %v: Expected (v: %v, ok: %t), Got (v: %v, ok: %t)",
   200  				tc.path, tc.v, tc.ok, v, ok)
   201  		}
   202  	}
   203  }
   204  
   205  func TestMapGetLongestPrefix(t *testing.T) {
   206  	type testMap struct {
   207  		pathMap        Map
   208  		expectedValues map[string]interface{}
   209  	}
   210  	makeMap := func(paths []string) (result testMap) {
   211  		result.expectedValues = make(map[string]interface{})
   212  
   213  		nextValue := uint32(1)
   214  		for _, path := range paths {
   215  			result.pathMap.Set(FromString(path), nextValue)
   216  			result.expectedValues[path] = nextValue
   217  			nextValue++
   218  		}
   219  
   220  		return
   221  	}
   222  
   223  	regularMap := makeMap([]string{
   224  		"/",
   225  		"/a",
   226  		"/a/b",
   227  		"/a/b/c/d",
   228  		"/a/b/c/d/e",
   229  		"/r/s",
   230  		"/r/s/t",
   231  		"/u/v",
   232  	})
   233  
   234  	noEntryAtRootMap := makeMap([]string{
   235  		"/r/s",
   236  		"/r/s/t",
   237  		"/u/v",
   238  	})
   239  
   240  	rootOnlyMap := makeMap([]string{"/"})
   241  
   242  	emptyMap := makeMap(nil)
   243  
   244  	testCases := []struct {
   245  		name        string
   246  		mp          testMap
   247  		path        string
   248  		ok          bool
   249  		longestPath string
   250  	}{
   251  		// The root path
   252  		{
   253  			name:        "exact match, descendents, root path",
   254  			mp:          regularMap,
   255  			path:        "/",
   256  			ok:          true,
   257  			longestPath: "/",
   258  		},
   259  		{
   260  			name:        "no exact match, descendents, root path",
   261  			mp:          noEntryAtRootMap,
   262  			path:        "/",
   263  			ok:          false,
   264  			longestPath: "",
   265  		},
   266  		{
   267  			name:        "exact match, no descendents, root path",
   268  			mp:          rootOnlyMap,
   269  			path:        "/",
   270  			ok:          true,
   271  			longestPath: "/",
   272  		},
   273  		{
   274  			name:        "no exact match, no descendents, root path",
   275  			mp:          emptyMap,
   276  			path:        "/",
   277  			ok:          false,
   278  			longestPath: "",
   279  		},
   280  
   281  		// Non-root paths when the path map has entries associated with shorter
   282  		// prefixes
   283  		{
   284  			name:        "exact match, descendents, ancestor",
   285  			mp:          regularMap,
   286  			path:        "/a/b/c/d",
   287  			ok:          true,
   288  			longestPath: "/a/b/c/d",
   289  		},
   290  		{
   291  			name:        "no exact match, descendents, ancestor",
   292  			mp:          regularMap,
   293  			path:        "/a/b/c",
   294  			ok:          true,
   295  			longestPath: "/a/b",
   296  		},
   297  		{
   298  			name:        "exact match, no descendents, ancestor",
   299  			mp:          regularMap,
   300  			path:        "/a/b/c/d/e",
   301  			ok:          true,
   302  			longestPath: "/a/b/c/d/e",
   303  		},
   304  		// When considering divergent paths (i.e. paths p where the path map has
   305  		// neither an entry associated with p nor any entry associated with a
   306  		// descendent path of p), they may diverge from the map nodes at a node
   307  		// representing an entry or they may diverge from a node representing a
   308  		// non-entry.
   309  		{
   310  			name: "no exact match, no descendents, ancestor, stray from " +
   311  				"internal entry",
   312  			mp:          regularMap,
   313  			path:        "/a/b/f",
   314  			ok:          true,
   315  			longestPath: "/a/b",
   316  		},
   317  		{
   318  			name: "no exact match, no descendents, ancestor, stray two entries " +
   319  				"from internal entry",
   320  			mp:          regularMap,
   321  			path:        "/a/b/f/g",
   322  			ok:          true,
   323  			longestPath: "/a/b",
   324  		},
   325  		{
   326  			name: "no exact match, no descendents, ancestor, stray from leaf " +
   327  				"entry",
   328  			mp:          regularMap,
   329  			path:        "/a/b/c/d/e/f",
   330  			ok:          true,
   331  			longestPath: "/a/b/c/d/e",
   332  		},
   333  		{
   334  			name: "no exact match, no descendents, ancestor, stray two entries " +
   335  				"from leaf entry",
   336  			mp:          regularMap,
   337  			path:        "/a/b/c/d/e/f/g",
   338  			ok:          true,
   339  			longestPath: "/a/b/c/d/e",
   340  		},
   341  		{
   342  			name: "no exact match, no descendents, ancestor, stray from " +
   343  				"internal non-entry",
   344  			mp:          regularMap,
   345  			path:        "/a/b/c/f",
   346  			ok:          true,
   347  			longestPath: "/a/b",
   348  		},
   349  		{
   350  			name: "no exact match, no descendents, ancestor, stray two entries " +
   351  				"from internal non-entry",
   352  			mp:          regularMap,
   353  			path:        "/a/b/c/f",
   354  			ok:          true,
   355  			longestPath: "/a/b",
   356  		},
   357  
   358  		// Non-root paths when the path map has no entries associated with shorter
   359  		// prefixes except for an entry associated with the root path
   360  		{
   361  			name:        "exact match, descendents, ancestor is root",
   362  			mp:          regularMap,
   363  			path:        "/r/s",
   364  			ok:          true,
   365  			longestPath: "/r/s",
   366  		},
   367  		{
   368  			name:        "no exact match, descendents, ancestor is root",
   369  			mp:          regularMap,
   370  			path:        "/r",
   371  			ok:          true,
   372  			longestPath: "/",
   373  		},
   374  		{
   375  			name:        "exact match, no descendents, ancestor is root",
   376  			mp:          regularMap,
   377  			path:        "/u/v",
   378  			ok:          true,
   379  			longestPath: "/u/v",
   380  		},
   381  		{
   382  			name:        "no exact match, no descendents, ancestor is root",
   383  			mp:          regularMap,
   384  			path:        "/x/y/z",
   385  			ok:          true,
   386  			longestPath: "/",
   387  		},
   388  
   389  		// Non-root paths when the path map has no entries associated with shorter
   390  		// prefixes
   391  		{
   392  			name:        "exact match, descendents, no ancestor",
   393  			mp:          noEntryAtRootMap,
   394  			path:        "/r/s",
   395  			ok:          true,
   396  			longestPath: "/r/s",
   397  		},
   398  		{
   399  			name:        "no exact match, descendents, no ancestor",
   400  			mp:          noEntryAtRootMap,
   401  			path:        "/r",
   402  			ok:          false,
   403  			longestPath: "",
   404  		},
   405  		{
   406  			name:        "exact match, no descendents, no ancestor",
   407  			mp:          noEntryAtRootMap,
   408  			path:        "/u/v",
   409  			ok:          true,
   410  			longestPath: "/u/v",
   411  		},
   412  		{
   413  			name:        "no exact match, no descendents, no ancestor",
   414  			mp:          noEntryAtRootMap,
   415  			path:        "/x/y/z",
   416  			ok:          false,
   417  			longestPath: "",
   418  		},
   419  	}
   420  
   421  	for _, tc := range testCases {
   422  		t.Run(tc.name, func(t *testing.T) {
   423  			// Ensure single canonical representation of test case struct.
   424  			if !tc.ok && tc.longestPath != "" {
   425  				t.Fatalf(
   426  					"Test case %q expects ok == false but has a configured "+
   427  						"longestPath value of %q. Please clear this.",
   428  					tc.name,
   429  					tc.longestPath,
   430  				)
   431  			}
   432  
   433  			// Dump details of test case for easy access on failure.
   434  			defer func() {
   435  				if t.Failed() {
   436  					t.Logf("Failed with test case: %+v", tc)
   437  				}
   438  			}()
   439  
   440  			inputPath := FromString(tc.path)
   441  
   442  			t.Logf("Running GetLongestPrefix with %v", inputPath)
   443  			longestPrefix, v, ok := tc.mp.pathMap.GetLongestPrefix(inputPath)
   444  
   445  			if ok != tc.ok {
   446  				t.Fatalf("Unexpected ok value; expected:%v actual:%v", tc.ok, ok)
   447  			}
   448  
   449  			if !ok {
   450  				if !Equal(longestPrefix, nil) {
   451  					// Note: path.Equal([]key.Key{}, nil) == true.
   452  					t.Errorf("Unexpected non-empty longestPrefix: %v", longestPrefix)
   453  				}
   454  				if v != nil {
   455  					t.Errorf(
   456  						"Expected zero-value (nil); received unexpected value: %v",
   457  						v,
   458  					)
   459  				}
   460  			} else {
   461  				expectedlongestPrefix := FromString(tc.longestPath)
   462  				if !Equal(longestPrefix, expectedlongestPrefix) {
   463  					t.Errorf(
   464  						"Unexpected longestPrefix; expected:%v actual:%v",
   465  						expectedlongestPrefix,
   466  						longestPrefix,
   467  					)
   468  				}
   469  
   470  				expectedValue := tc.mp.expectedValues[tc.longestPath]
   471  				if v != expectedValue {
   472  					t.Errorf(
   473  						"Unexpected entry value; expected:%v actual:%v",
   474  						expectedValue,
   475  						v,
   476  					)
   477  				}
   478  			}
   479  		})
   480  	}
   481  }
   482  
   483  func countNodes(m *Map) int {
   484  	if m == nil {
   485  		return 0
   486  	}
   487  	count := 1
   488  	count += countNodes(m.wildcard)
   489  	for it := m.children.Iter(); it.Next(); {
   490  		count += countNodes(it.Elem())
   491  	}
   492  	return count
   493  }
   494  
   495  func TestMapDelete(t *testing.T) {
   496  	m := Map{}
   497  	m.Set(key.Path{}, 0)
   498  	m.Set(key.Path{Wildcard}, 1)
   499  	m.Set(key.Path{key.New("foo"), key.New("bar")}, 2)
   500  	m.Set(key.Path{key.New("foo"), Wildcard}, 3)
   501  	m.Set(key.Path{key.New("foo")}, 4)
   502  
   503  	n := countNodes(&m)
   504  	if n != 5 {
   505  		t.Errorf("Initial count wrong. Expected: 5, Got: %d", n)
   506  	}
   507  
   508  	testCases := []struct {
   509  		del      key.Path    // key.Path to delete
   510  		expected bool        // expected return value of Delete
   511  		visit    key.Path    // key.Path to Visit
   512  		before   map[int]int // Expected to find items before deletion
   513  		after    map[int]int // Expected to find items after deletion
   514  		count    int         // Count of nodes
   515  	}{{
   516  		del:      key.Path{key.New("zap")}, // A no-op Delete
   517  		expected: false,
   518  		visit:    key.Path{key.New("foo"), key.New("bar")},
   519  		before:   map[int]int{2: 1, 3: 1},
   520  		after:    map[int]int{2: 1, 3: 1},
   521  		count:    5,
   522  	}, {
   523  		del:      key.Path{key.New("foo"), key.New("bar")},
   524  		expected: true,
   525  		visit:    key.Path{key.New("foo"), key.New("bar")},
   526  		before:   map[int]int{2: 1, 3: 1},
   527  		after:    map[int]int{3: 1},
   528  		count:    4,
   529  	}, {
   530  		del:      key.Path{key.New("foo")},
   531  		expected: true,
   532  		visit:    key.Path{key.New("foo")},
   533  		before:   map[int]int{1: 1, 4: 1},
   534  		after:    map[int]int{1: 1},
   535  		count:    4,
   536  	}, {
   537  		del:      key.Path{key.New("foo")},
   538  		expected: false,
   539  		visit:    key.Path{key.New("foo")},
   540  		before:   map[int]int{1: 1},
   541  		after:    map[int]int{1: 1},
   542  		count:    4,
   543  	}, {
   544  		del:      key.Path{Wildcard},
   545  		expected: true,
   546  		visit:    key.Path{key.New("foo")},
   547  		before:   map[int]int{1: 1},
   548  		after:    map[int]int{},
   549  		count:    3,
   550  	}, {
   551  		del:      key.Path{Wildcard},
   552  		expected: false,
   553  		visit:    key.Path{key.New("foo")},
   554  		before:   map[int]int{},
   555  		after:    map[int]int{},
   556  		count:    3,
   557  	}, {
   558  		del:      key.Path{key.New("foo"), Wildcard},
   559  		expected: true,
   560  		visit:    key.Path{key.New("foo"), key.New("bar")},
   561  		before:   map[int]int{3: 1},
   562  		after:    map[int]int{},
   563  		count:    1, // Should have deleted "foo" and "bar" nodes
   564  	}, {
   565  		del:      key.Path{},
   566  		expected: true,
   567  		visit:    key.Path{},
   568  		before:   map[int]int{0: 1},
   569  		after:    map[int]int{},
   570  		count:    1, // Root node can't be deleted
   571  	}}
   572  
   573  	for i, tc := range testCases {
   574  		beforeResult := make(map[int]int, len(tc.before))
   575  		m.Visit(tc.visit, accumulator(beforeResult))
   576  		if diff := test.Diff(tc.before, beforeResult); diff != "" {
   577  			t.Errorf("Test case %d (%v): %s", i, tc.del, diff)
   578  		}
   579  
   580  		if got := m.Delete(tc.del); got != tc.expected {
   581  			t.Errorf("Test case %d (%v): Unexpected return. Expected %t, Got: %t",
   582  				i, tc.del, tc.expected, got)
   583  		}
   584  
   585  		afterResult := make(map[int]int, len(tc.after))
   586  		m.Visit(tc.visit, accumulator(afterResult))
   587  		if diff := test.Diff(tc.after, afterResult); diff != "" {
   588  			t.Errorf("Test case %d (%v): %s", i, tc.del, diff)
   589  		}
   590  	}
   591  }
   592  
   593  func TestMapVisitPrefixes(t *testing.T) {
   594  	m := Map{}
   595  	m.Set(key.Path{}, 0)
   596  	m.Set(key.Path{key.New("foo")}, 1)
   597  	m.Set(key.Path{key.New("foo"), key.New("bar")}, 2)
   598  	m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 3)
   599  	m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz"), key.New("quux")}, 4)
   600  	m.Set(key.Path{key.New("quux"), key.New("bar")}, 5)
   601  	m.Set(key.Path{key.New("foo"), key.New("quux")}, 6)
   602  	m.Set(key.Path{Wildcard}, 7)
   603  	m.Set(key.Path{key.New("foo"), Wildcard}, 8)
   604  	m.Set(key.Path{Wildcard, key.New("bar")}, 9)
   605  	m.Set(key.Path{Wildcard, key.New("quux")}, 10)
   606  	m.Set(key.Path{key.New("quux"), key.New("quux"), key.New("quux"), key.New("quux")}, 11)
   607  
   608  	testCases := []struct {
   609  		path     key.Path
   610  		expected map[int]int
   611  	}{{
   612  		path:     key.Path{key.New("foo"), key.New("bar"), key.New("baz")},
   613  		expected: map[int]int{0: 1, 1: 1, 2: 1, 3: 1, 7: 1, 8: 1, 9: 1},
   614  	}, {
   615  		path:     key.Path{key.New("zip"), key.New("zap")},
   616  		expected: map[int]int{0: 1, 7: 1},
   617  	}, {
   618  		path:     key.Path{key.New("foo"), key.New("zap")},
   619  		expected: map[int]int{0: 1, 1: 1, 8: 1, 7: 1},
   620  	}, {
   621  		path:     key.Path{key.New("quux"), key.New("quux"), key.New("quux")},
   622  		expected: map[int]int{0: 1, 7: 1, 10: 1},
   623  	}}
   624  
   625  	for _, tc := range testCases {
   626  		result := make(map[int]int, len(tc.expected))
   627  		m.VisitPrefixes(tc.path, accumulator(result))
   628  		if diff := test.Diff(tc.expected, result); diff != "" {
   629  			t.Errorf("Test case %v: %s", tc.path, diff)
   630  		}
   631  	}
   632  }
   633  
   634  func TestMapVisitPrefixed(t *testing.T) {
   635  	m := Map{}
   636  	m.Set(key.Path{}, 0)
   637  	m.Set(key.Path{key.New("qux")}, 1)
   638  	m.Set(key.Path{key.New("foo")}, 2)
   639  	m.Set(key.Path{key.New("foo"), key.New("qux")}, 3)
   640  	m.Set(key.Path{key.New("foo"), key.New("bar")}, 4)
   641  	m.Set(key.Path{Wildcard, key.New("bar")}, 5)
   642  	m.Set(key.Path{key.New("foo"), Wildcard}, 6)
   643  	m.Set(key.Path{key.New("qux"), key.New("foo"), key.New("bar")}, 7)
   644  
   645  	testCases := []struct {
   646  		in  key.Path
   647  		out map[int]int
   648  	}{{
   649  		in:  key.Path{},
   650  		out: map[int]int{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1},
   651  	}, {
   652  		in:  key.Path{key.New("qux")},
   653  		out: map[int]int{1: 1, 5: 1, 7: 1},
   654  	}, {
   655  		in:  key.Path{key.New("foo")},
   656  		out: map[int]int{2: 1, 3: 1, 4: 1, 5: 1, 6: 1},
   657  	}, {
   658  		in:  key.Path{key.New("foo"), key.New("qux")},
   659  		out: map[int]int{3: 1, 6: 1},
   660  	}, {
   661  		in:  key.Path{key.New("foo"), key.New("bar")},
   662  		out: map[int]int{4: 1, 5: 1, 6: 1},
   663  	}, {
   664  		in:  key.Path{key.New(int64(0))},
   665  		out: map[int]int{5: 1},
   666  	}, {
   667  		in:  key.Path{Wildcard},
   668  		out: map[int]int{5: 1},
   669  	}, {
   670  		in:  key.Path{Wildcard, Wildcard},
   671  		out: map[int]int{},
   672  	}}
   673  
   674  	for _, tc := range testCases {
   675  		out := make(map[int]int, len(tc.out))
   676  		m.VisitPrefixed(tc.in, accumulator(out))
   677  		if diff := test.Diff(tc.out, out); diff != "" {
   678  			t.Errorf("Test case %v: %s", tc.out, diff)
   679  		}
   680  	}
   681  }
   682  
   683  func TestMapVisitChildren(t *testing.T) {
   684  	m := Map{}
   685  	m.Set(key.Path{}, 0)
   686  	m.Set(key.Path{key.New("foo")}, 1)
   687  	m.Set(key.Path{key.New("foo"), key.New("bar")}, 2)
   688  	m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz")}, 3)
   689  	m.Set(key.Path{key.New("foo"), key.New("bar"), key.New("baz"), key.New("quux")}, 4)
   690  	m.Set(key.Path{key.New("quux"), key.New("bar")}, 5)
   691  	m.Set(key.Path{key.New("foo"), key.New("quux")}, 6)
   692  	m.Set(key.Path{Wildcard}, 7)
   693  	m.Set(key.Path{key.New("foo"), Wildcard}, 8)
   694  	m.Set(key.Path{Wildcard, key.New("bar")}, 9)
   695  	m.Set(key.Path{Wildcard, key.New("bar"), key.New("quux")}, 10)
   696  	m.Set(key.Path{key.New("quux"), key.New("quux"), key.New("quux"), key.New("quux")}, 11)
   697  	m.Set(key.Path{key.New("a"), key.New("b"), key.New("c"), key.New("d")}, 12)
   698  	m.Set(key.Path{key.New("a"), key.New("b")}, 13)
   699  
   700  	testCases := []struct {
   701  		path     key.Path
   702  		expected map[int]int
   703  	}{{
   704  		path:     key.Path{key.New("foo"), key.New("bar"), key.New("baz")},
   705  		expected: map[int]int{4: 1},
   706  	}, {
   707  		path:     key.Path{key.New("zip"), key.New("zap")},
   708  		expected: map[int]int{},
   709  	}, {
   710  		path:     key.Path{key.New("foo"), key.New("bar")},
   711  		expected: map[int]int{3: 1, 10: 1},
   712  	}, {
   713  		path:     key.Path{key.New("quux"), key.New("quux"), key.New("quux")},
   714  		expected: map[int]int{11: 1},
   715  	}, {
   716  		path:     key.Path{key.New("a"), key.New("b")},
   717  		expected: map[int]int{},
   718  	}}
   719  
   720  	for _, tc := range testCases {
   721  		result := make(map[int]int, len(tc.expected))
   722  		m.VisitChildren(tc.path, accumulator(result))
   723  		if diff := test.Diff(tc.expected, result); diff != "" {
   724  			t.Errorf("Test case %v: %s", tc.path, diff)
   725  			t.Errorf("tc.expected: %#v, got %#v", tc.expected, result)
   726  		}
   727  	}
   728  }
   729  
   730  func TestMapString(t *testing.T) {
   731  	m := Map{}
   732  	m.Set(key.Path{}, 0)
   733  	m.Set(key.Path{key.New("foo"), key.New("bar")}, 1)
   734  	m.Set(key.Path{key.New("foo"), key.New("quux")}, 2)
   735  	m.Set(key.Path{key.New("foo"), Wildcard}, 3)
   736  
   737  	expected := `Val: 0
   738  Child "foo":
   739    Child "*":
   740      Val: 3
   741    Child "bar":
   742      Val: 1
   743    Child "quux":
   744      Val: 2
   745  `
   746  	got := fmt.Sprint(&m)
   747  
   748  	if expected != got {
   749  		t.Errorf("Unexpected string. Expected:\n\n%s\n\nGot:\n\n%s", expected, got)
   750  	}
   751  }
   752  
   753  func genWords(count, wordLength int) key.Path {
   754  	chars := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
   755  	if count+wordLength > len(chars) {
   756  		panic("need more chars")
   757  	}
   758  	result := make(key.Path, count)
   759  	for i := 0; i < count; i++ {
   760  		result[i] = key.New(string(chars[i : i+wordLength]))
   761  	}
   762  	return result
   763  }
   764  
   765  func benchmarkPathMap(pathLength, pathDepth int, b *testing.B) {
   766  	// Push pathDepth paths, each of length pathLength
   767  	path := genWords(pathLength, 10)
   768  	words := genWords(pathDepth, 10)
   769  	root := &Map{}
   770  	m := root
   771  	for _, element := range path {
   772  		m.children = newKeyMap[any]()
   773  		for _, word := range words {
   774  			m.children.Set(word, &Map{})
   775  		}
   776  		next, _ := m.children.Get(element)
   777  		m = next
   778  	}
   779  	b.ReportAllocs()
   780  	b.ResetTimer()
   781  	for i := 0; i < b.N; i++ {
   782  		root.Visit(path, func(v interface{}) error { return nil })
   783  	}
   784  }
   785  
   786  func BenchmarkPathMap1x25(b *testing.B)  { benchmarkPathMap(1, 25, b) }
   787  func BenchmarkPathMap10x50(b *testing.B) { benchmarkPathMap(10, 25, b) }
   788  func BenchmarkPathMap20x50(b *testing.B) { benchmarkPathMap(20, 25, b) }