github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/stree/stree_test.go (about)

     1  // Copyright 2023-2024 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package stree
    15  
    16  import (
    17  	crand "crypto/rand"
    18  	"encoding/hex"
    19  	"flag"
    20  	"fmt"
    21  	"math/rand"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  	"unsafe"
    26  )
    27  
    28  // Print Results: go test -v  --args --results
    29  // For some benchmarks.
    30  var runResults = flag.Bool("results", false, "Enable Results Tests")
    31  
    32  func TestSubjectTreeBasics(t *testing.T) {
    33  	st := NewSubjectTree[int]()
    34  	require_Equal(t, st.Size(), 0)
    35  	// Single leaf
    36  	old, updated := st.Insert(b("foo.bar.baz"), 22)
    37  	require_True(t, old == nil)
    38  	require_False(t, updated)
    39  	require_Equal(t, st.Size(), 1)
    40  	// Find with single leaf.
    41  	v, found := st.Find(b("foo.bar.baz"))
    42  	require_True(t, found)
    43  	require_Equal(t, *v, 22)
    44  	// Update single leaf
    45  	old, updated = st.Insert(b("foo.bar.baz"), 33)
    46  	require_True(t, old != nil)
    47  	require_Equal(t, *old, 22)
    48  	require_True(t, updated)
    49  	require_Equal(t, st.Size(), 1)
    50  	// Split the tree
    51  	old, updated = st.Insert(b("foo.bar"), 22)
    52  	require_True(t, old == nil)
    53  	require_False(t, updated)
    54  	require_Equal(t, st.Size(), 2)
    55  	// Now we have node4 -> leaf*2
    56  	v, found = st.Find(b("foo.bar"))
    57  	require_True(t, found)
    58  	require_Equal(t, *v, 22)
    59  	// Make sure we can still retrieve the original after the split.
    60  	v, found = st.Find(b("foo.bar.baz"))
    61  	require_True(t, found)
    62  	require_Equal(t, *v, 33)
    63  }
    64  
    65  func TestSubjectTreeNodeGrow(t *testing.T) {
    66  	st := NewSubjectTree[int]()
    67  	for i := 0; i < 4; i++ {
    68  		subj := b(fmt.Sprintf("foo.bar.%c", 'A'+i))
    69  		old, updated := st.Insert(subj, 22)
    70  		require_True(t, old == nil)
    71  		require_False(t, updated)
    72  	}
    73  	// We have filled a node4.
    74  	_, ok := st.root.(*node4)
    75  	require_True(t, ok)
    76  	// This one will trigger us to grow.
    77  	old, updated := st.Insert(b("foo.bar.E"), 22)
    78  	require_True(t, old == nil)
    79  	require_False(t, updated)
    80  	_, ok = st.root.(*node16)
    81  	require_True(t, ok)
    82  	// We do not have node48, so once we fill this we should jump to node256.
    83  	for i := 5; i < 16; i++ {
    84  		subj := b(fmt.Sprintf("foo.bar.%c", 'A'+i))
    85  		old, updated := st.Insert(subj, 22)
    86  		require_True(t, old == nil)
    87  		require_False(t, updated)
    88  	}
    89  	// This one will trigger us to grow.
    90  	old, updated = st.Insert(b("foo.bar.Q"), 22)
    91  	require_True(t, old == nil)
    92  	require_False(t, updated)
    93  	_, ok = st.root.(*node256)
    94  	require_True(t, ok)
    95  }
    96  
    97  func TestSubjectTreeNodePrefixMismatch(t *testing.T) {
    98  	st := NewSubjectTree[int]()
    99  	st.Insert(b("foo.bar.A"), 11)
   100  	st.Insert(b("foo.bar.B"), 22)
   101  	st.Insert(b("foo.bar.C"), 33)
   102  	// Grab current root. Split below will cause update.
   103  	or := st.root
   104  	// This one will force a split of the node
   105  	st.Insert(b("foo.foo.A"), 44)
   106  	require_True(t, or != st.root)
   107  	// Now make sure we can retrieve correctly.
   108  	v, found := st.Find(b("foo.bar.A"))
   109  	require_True(t, found)
   110  	require_Equal(t, *v, 11)
   111  	v, found = st.Find(b("foo.bar.B"))
   112  	require_True(t, found)
   113  	require_Equal(t, *v, 22)
   114  	v, found = st.Find(b("foo.bar.C"))
   115  	require_True(t, found)
   116  	require_Equal(t, *v, 33)
   117  	v, found = st.Find(b("foo.foo.A"))
   118  	require_True(t, found)
   119  	require_Equal(t, *v, 44)
   120  }
   121  
   122  func TestSubjectTreeNodeDelete(t *testing.T) {
   123  	st := NewSubjectTree[int]()
   124  	st.Insert(b("foo.bar.A"), 22)
   125  	v, found := st.Delete(b("foo.bar.A"))
   126  	require_True(t, found)
   127  	require_Equal(t, *v, 22)
   128  	require_Equal(t, st.root, nil)
   129  	v, found = st.Delete(b("foo.bar.A"))
   130  	require_False(t, found)
   131  	require_Equal(t, v, nil)
   132  	v, found = st.Find(b("foo.foo.A"))
   133  	require_False(t, found)
   134  	require_Equal(t, v, nil)
   135  	// Kick to a node4.
   136  	st.Insert(b("foo.bar.A"), 11)
   137  	st.Insert(b("foo.bar.B"), 22)
   138  	st.Insert(b("foo.bar.C"), 33)
   139  	// Make sure we can delete and that we shrink back to leaf.
   140  	v, found = st.Delete(b("foo.bar.C"))
   141  	require_True(t, found)
   142  	require_Equal(t, *v, 33)
   143  	v, found = st.Delete(b("foo.bar.B"))
   144  	require_True(t, found)
   145  	require_Equal(t, *v, 22)
   146  	// We should have shrunk here.
   147  	require_True(t, st.root.isLeaf())
   148  	v, found = st.Delete(b("foo.bar.A"))
   149  	require_True(t, found)
   150  	require_Equal(t, *v, 11)
   151  	require_Equal(t, st.root, nil)
   152  	// Now pop up to a node16 and make sure we can shrink back down.
   153  	for i := 0; i < 5; i++ {
   154  		subj := fmt.Sprintf("foo.bar.%c", 'A'+i)
   155  		st.Insert(b(subj), 22)
   156  	}
   157  	_, ok := st.root.(*node16)
   158  	require_True(t, ok)
   159  	v, found = st.Delete(b("foo.bar.A"))
   160  	require_True(t, found)
   161  	require_Equal(t, *v, 22)
   162  	_, ok = st.root.(*node4)
   163  	require_True(t, ok)
   164  	// Now pop up to node256
   165  	st = NewSubjectTree[int]()
   166  	for i := 0; i < 17; i++ {
   167  		subj := fmt.Sprintf("foo.bar.%c", 'A'+i)
   168  		st.Insert(b(subj), 22)
   169  	}
   170  	_, ok = st.root.(*node256)
   171  	require_True(t, ok)
   172  	v, found = st.Delete(b("foo.bar.A"))
   173  	require_True(t, found)
   174  	require_Equal(t, *v, 22)
   175  	_, ok = st.root.(*node16)
   176  	require_True(t, ok)
   177  	v, found = st.Find(b("foo.bar.B"))
   178  	require_True(t, found)
   179  	require_Equal(t, *v, 22)
   180  }
   181  
   182  func TestSubjectTreeNodesAndPaths(t *testing.T) {
   183  	st := NewSubjectTree[int]()
   184  	check := func(subj string) {
   185  		t.Helper()
   186  		v, found := st.Find(b(subj))
   187  		require_True(t, found)
   188  		require_Equal(t, *v, 22)
   189  	}
   190  	st.Insert(b("foo.bar.A"), 22)
   191  	st.Insert(b("foo.bar.B"), 22)
   192  	st.Insert(b("foo.bar.C"), 22)
   193  	st.Insert(b("foo.bar"), 22)
   194  	check("foo.bar.A")
   195  	check("foo.bar.B")
   196  	check("foo.bar.C")
   197  	check("foo.bar")
   198  	// This will do several things in terms of shrinking and pruning,
   199  	// want to make sure it gets prefix correct for new top node4.
   200  	st.Delete(b("foo.bar"))
   201  	check("foo.bar.A")
   202  	check("foo.bar.B")
   203  	check("foo.bar.C")
   204  }
   205  
   206  // Check that we are constructing a proper tree with complex insert patterns.
   207  func TestSubjectTreeConstruction(t *testing.T) {
   208  	st := NewSubjectTree[int]()
   209  	st.Insert(b("foo.bar.A"), 1)
   210  	st.Insert(b("foo.bar.B"), 2)
   211  	st.Insert(b("foo.bar.C"), 3)
   212  	st.Insert(b("foo.baz.A"), 11)
   213  	st.Insert(b("foo.baz.B"), 22)
   214  	st.Insert(b("foo.baz.C"), 33)
   215  	st.Insert(b("foo.bar"), 42)
   216  
   217  	checkNode := func(an *node, kind string, pors string, numChildren uint16) {
   218  		//		t.Helper()
   219  		require_True(t, an != nil)
   220  		n := *an
   221  		require_True(t, n != nil)
   222  		require_Equal(t, n.kind(), kind)
   223  		require_Equal(t, pors, string(n.path()))
   224  		require_Equal(t, numChildren, n.numChildren())
   225  	}
   226  
   227  	checkNode(&st.root, "NODE4", "foo.ba", 2)
   228  	nn := st.root.findChild('r')
   229  	checkNode(nn, "NODE4", "r", 2)
   230  	checkNode((*nn).findChild(0), "LEAF", "", 0)
   231  	rnn := (*nn).findChild('.')
   232  	checkNode(rnn, "NODE4", ".", 3)
   233  	checkNode((*rnn).findChild('A'), "LEAF", "A", 0)
   234  	checkNode((*rnn).findChild('B'), "LEAF", "B", 0)
   235  	checkNode((*rnn).findChild('C'), "LEAF", "C", 0)
   236  	znn := st.root.findChild('z')
   237  	checkNode(znn, "NODE4", "z.", 3)
   238  	checkNode((*znn).findChild('A'), "LEAF", "A", 0)
   239  	checkNode((*znn).findChild('B'), "LEAF", "B", 0)
   240  	checkNode((*znn).findChild('C'), "LEAF", "C", 0)
   241  	// Use st.Dump() if you want a tree print out.
   242  
   243  	// Now delete "foo.bar" and make sure put ourselves back together properly.
   244  	v, found := st.Delete(b("foo.bar"))
   245  	require_True(t, found)
   246  	require_Equal(t, *v, 42)
   247  
   248  	checkNode(&st.root, "NODE4", "foo.ba", 2)
   249  	nn = st.root.findChild('r')
   250  	checkNode(nn, "NODE4", "r.", 3)
   251  	checkNode((*nn).findChild('A'), "LEAF", "A", 0)
   252  	checkNode((*nn).findChild('B'), "LEAF", "B", 0)
   253  	checkNode((*nn).findChild('C'), "LEAF", "C", 0)
   254  	znn = st.root.findChild('z')
   255  	checkNode(znn, "NODE4", "z.", 3)
   256  	checkNode((*znn).findChild('A'), "LEAF", "A", 0)
   257  	checkNode((*znn).findChild('B'), "LEAF", "B", 0)
   258  	checkNode((*znn).findChild('C'), "LEAF", "C", 0)
   259  }
   260  
   261  func match(t *testing.T, st *SubjectTree[int], filter string, expected int) {
   262  	t.Helper()
   263  	var matches []int
   264  	st.Match(b(filter), func(_ []byte, v *int) {
   265  		matches = append(matches, *v)
   266  	})
   267  	require_Equal(t, expected, len(matches))
   268  }
   269  
   270  func TestSubjectTreeMatchLeafOnly(t *testing.T) {
   271  	st := NewSubjectTree[int]()
   272  	st.Insert(b("foo.bar.baz.A"), 1)
   273  
   274  	// Check all placements of pwc in token space.
   275  	match(t, st, "foo.bar.*.A", 1)
   276  	match(t, st, "foo.*.baz.A", 1)
   277  	match(t, st, "foo.*.*.A", 1)
   278  	match(t, st, "foo.*.*.*", 1)
   279  	match(t, st, "*.*.*.*", 1)
   280  	// Now check fwc.
   281  	match(t, st, ">", 1)
   282  	match(t, st, "foo.>", 1)
   283  	match(t, st, "foo.*.>", 1)
   284  	match(t, st, "foo.bar.>", 1)
   285  	match(t, st, "foo.bar.*.>", 1)
   286  
   287  	// Check partials so they do not trigger on leafs.
   288  	match(t, st, "foo.bar.baz", 0)
   289  }
   290  
   291  func TestSubjectTreeMatchNodes(t *testing.T) {
   292  	st := NewSubjectTree[int]()
   293  	st.Insert(b("foo.bar.A"), 1)
   294  	st.Insert(b("foo.bar.B"), 2)
   295  	st.Insert(b("foo.bar.C"), 3)
   296  	st.Insert(b("foo.baz.A"), 11)
   297  	st.Insert(b("foo.baz.B"), 22)
   298  	st.Insert(b("foo.baz.C"), 33)
   299  
   300  	// Test literals.
   301  	match(t, st, "foo.bar.A", 1)
   302  	match(t, st, "foo.baz.A", 1)
   303  	match(t, st, "foo.bar", 0)
   304  	// Test internal pwc
   305  	match(t, st, "foo.*.A", 2)
   306  	// Test terminal pwc
   307  	match(t, st, "foo.bar.*", 3)
   308  	match(t, st, "foo.baz.*", 3)
   309  	// Check fwc
   310  	match(t, st, ">", 6)
   311  	match(t, st, "foo.>", 6)
   312  	match(t, st, "foo.bar.>", 3)
   313  	match(t, st, "foo.baz.>", 3)
   314  	// Make sure we do not have false positives on prefix matches.
   315  	match(t, st, "foo.ba", 0)
   316  
   317  	// Now add in "foo.bar" to make a more complex tree construction
   318  	// and re-test.
   319  	st.Insert(b("foo.bar"), 42)
   320  
   321  	// Test literals.
   322  	match(t, st, "foo.bar.A", 1)
   323  	match(t, st, "foo.baz.A", 1)
   324  	match(t, st, "foo.bar", 1)
   325  	// Test internal pwc
   326  	match(t, st, "foo.*.A", 2)
   327  	// Test terminal pwc
   328  	match(t, st, "foo.bar.*", 3)
   329  	match(t, st, "foo.baz.*", 3)
   330  	// Check fwc
   331  	match(t, st, ">", 7)
   332  	match(t, st, "foo.>", 7)
   333  	match(t, st, "foo.bar.>", 3)
   334  	match(t, st, "foo.baz.>", 3)
   335  }
   336  
   337  func TestSubjectTreeNoPrefix(t *testing.T) {
   338  	st := NewSubjectTree[int]()
   339  	for i := 0; i < 26; i++ {
   340  		subj := b(fmt.Sprintf("%c", 'A'+i))
   341  		old, updated := st.Insert(subj, 22)
   342  		require_True(t, old == nil)
   343  		require_False(t, updated)
   344  	}
   345  	n, ok := st.root.(*node256)
   346  	require_True(t, ok)
   347  	require_Equal(t, n.numChildren(), 26)
   348  	v, found := st.Delete(b("B"))
   349  	require_True(t, found)
   350  	require_Equal(t, *v, 22)
   351  	require_Equal(t, n.numChildren(), 25)
   352  	v, found = st.Delete(b("Z"))
   353  	require_True(t, found)
   354  	require_Equal(t, *v, 22)
   355  	require_Equal(t, n.numChildren(), 24)
   356  }
   357  
   358  func TestSubjectTreePartialTerminalWildcardBugMatch(t *testing.T) {
   359  	st := NewSubjectTree[int]()
   360  	st.Insert(b("STATE.GLOBAL.CELL1.7PDSGAALXNN000010.PROPERTY-A"), 5)
   361  	st.Insert(b("STATE.GLOBAL.CELL1.7PDSGAALXNN000010.PROPERTY-B"), 1)
   362  	st.Insert(b("STATE.GLOBAL.CELL1.7PDSGAALXNN000010.PROPERTY-C"), 2)
   363  	match(t, st, "STATE.GLOBAL.CELL1.7PDSGAALXNN000010.*", 3)
   364  }
   365  
   366  func TestSubjectTreeMaxPrefix(t *testing.T) {
   367  	st := NewSubjectTree[int]()
   368  	longPre := strings.Repeat("Z", maxPrefixLen+2)
   369  	for i := 0; i < 2; i++ {
   370  		subj := b(fmt.Sprintf("%s.%c", longPre, 'A'+i))
   371  		old, updated := st.Insert(subj, 22)
   372  		require_True(t, old == nil)
   373  		require_False(t, updated)
   374  	}
   375  	v, found := st.Find(b(fmt.Sprintf("%s.B", longPre)))
   376  	require_True(t, found)
   377  	require_Equal(t, *v, 22)
   378  	v, found = st.Delete(b(fmt.Sprintf("%s.A", longPre)))
   379  	require_True(t, found)
   380  	require_Equal(t, *v, 22)
   381  	require_Equal(t, st.root.numChildren(), 1)
   382  }
   383  
   384  func TestSubjectTreeMatchSubjectParam(t *testing.T) {
   385  	st := NewSubjectTree[int]()
   386  	st.Insert(b("foo.bar.A"), 1)
   387  	st.Insert(b("foo.bar.B"), 2)
   388  	st.Insert(b("foo.bar.C"), 3)
   389  	st.Insert(b("foo.baz.A"), 11)
   390  	st.Insert(b("foo.baz.B"), 22)
   391  	st.Insert(b("foo.baz.C"), 33)
   392  	st.Insert(b("foo.bar"), 42)
   393  
   394  	checkValMap := map[string]int{
   395  		"foo.bar.A": 1,
   396  		"foo.bar.B": 2,
   397  		"foo.bar.C": 3,
   398  		"foo.baz.A": 11,
   399  		"foo.baz.B": 22,
   400  		"foo.baz.C": 33,
   401  		"foo.bar":   42,
   402  	}
   403  	// Make sure we get a proper subject parameter and it matches our value properly.
   404  	st.Match([]byte(">"), func(subject []byte, v *int) {
   405  		if expected, ok := checkValMap[string(subject)]; !ok {
   406  			t.Fatalf("Unexpected subject parameter: %q", subject)
   407  		} else if expected != *v {
   408  			t.Fatalf("Expected %q to have value of %d, but got %d", subject, expected, *v)
   409  		}
   410  	})
   411  }
   412  
   413  func TestSubjectTreeMatchRandomDoublePWC(t *testing.T) {
   414  	st := NewSubjectTree[int]()
   415  	for i := 1; i <= 10_000; i++ {
   416  		subj := fmt.Sprintf("foo.%d.%d", rand.Intn(20)+1, i)
   417  		st.Insert(b(subj), 42)
   418  	}
   419  	match(t, st, "foo.*.*", 10_000)
   420  
   421  	// Check with pwc and short interior token.
   422  	seen, verified := 0, 0
   423  	st.Match(b("*.2.*"), func(_ []byte, _ *int) {
   424  		seen++
   425  	})
   426  	// Now check via walk to make sure we are right.
   427  	st.Iter(func(subject []byte, v *int) bool {
   428  		tokens := strings.Split(string(subject), ".")
   429  		require_Equal(t, len(tokens), 3)
   430  		if tokens[1] == "2" {
   431  			verified++
   432  		}
   433  		return true
   434  	})
   435  	require_Equal(t, seen, verified)
   436  
   437  	seen, verified = 0, 0
   438  	st.Match(b("*.*.222"), func(_ []byte, _ *int) {
   439  		seen++
   440  	})
   441  	st.Iter(func(subject []byte, v *int) bool {
   442  		tokens := strings.Split(string(subject), ".")
   443  		require_Equal(t, len(tokens), 3)
   444  		if tokens[2] == "222" {
   445  			verified++
   446  		}
   447  		return true
   448  	})
   449  	require_Equal(t, seen, verified)
   450  }
   451  
   452  func TestSubjectTreeIter(t *testing.T) {
   453  	st := NewSubjectTree[int]()
   454  	st.Insert(b("foo.bar.A"), 1)
   455  	st.Insert(b("foo.bar.B"), 2)
   456  	st.Insert(b("foo.bar.C"), 3)
   457  	st.Insert(b("foo.baz.A"), 11)
   458  	st.Insert(b("foo.baz.B"), 22)
   459  	st.Insert(b("foo.baz.C"), 33)
   460  	st.Insert(b("foo.bar"), 42)
   461  
   462  	checkValMap := map[string]int{
   463  		"foo.bar.A": 1,
   464  		"foo.bar.B": 2,
   465  		"foo.bar.C": 3,
   466  		"foo.baz.A": 11,
   467  		"foo.baz.B": 22,
   468  		"foo.baz.C": 33,
   469  		"foo.bar":   42,
   470  	}
   471  	checkOrder := []string{
   472  		"foo.bar",
   473  		"foo.bar.A",
   474  		"foo.bar.B",
   475  		"foo.bar.C",
   476  		"foo.baz.A",
   477  		"foo.baz.B",
   478  		"foo.baz.C",
   479  	}
   480  	var received int
   481  	walk := func(subject []byte, v *int) bool {
   482  		if expected := checkOrder[received]; expected != string(subject) {
   483  			t.Fatalf("Expected %q for %d item returned, got %q", expected, received, subject)
   484  		}
   485  		received++
   486  		require_True(t, v != nil)
   487  		if expected := checkValMap[string(subject)]; expected != *v {
   488  			t.Fatalf("Expected %q to have value of %d, but got %d", subject, expected, *v)
   489  		}
   490  		return true
   491  	}
   492  	// Kick in the iter.
   493  	st.Iter(walk)
   494  	require_Equal(t, received, len(checkOrder))
   495  
   496  	// Make sure we can terminate properly.
   497  	received = 0
   498  	st.Iter(func(subject []byte, v *int) bool {
   499  		received++
   500  		return received != 4
   501  	})
   502  	require_Equal(t, received, 4)
   503  }
   504  
   505  func TestSubjectTreeInsertSamePivotBug(t *testing.T) {
   506  	testSubjects := [][]byte{
   507  		[]byte("0d00.2abbb82c1d.6e16.fa7f85470e.3e46"),
   508  		[]byte("534b12.3486c17249.4dde0666"),
   509  		[]byte("6f26aabd.920ee3.d4d3.5ffc69f6"),
   510  		[]byte("8850.ade3b74c31.aa533f77.9f59.a4bd8415.b3ed7b4111"),
   511  		[]byte("5a75047dcb.5548e845b6.76024a34.14d5b3.80c426.51db871c3a"),
   512  		[]byte("825fa8acfc.5331.00caf8bbbd.107c4b.c291.126d1d010e"),
   513  	}
   514  	st := NewSubjectTree[int]()
   515  	for _, subj := range testSubjects {
   516  		old, updated := st.Insert(subj, 22)
   517  		require_True(t, old == nil)
   518  		require_False(t, updated)
   519  		if _, found := st.Find(subj); !found {
   520  			t.Fatalf("Could not find subject %q which should be findable", subj)
   521  		}
   522  	}
   523  }
   524  
   525  func TestSubjectTreeMatchTsepSecondThenPartialPartBug(t *testing.T) {
   526  	st := NewSubjectTree[int]()
   527  	st.Insert(b("foo.xxxxx.foo1234.zz"), 22)
   528  	st.Insert(b("foo.yyy.foo123.zz"), 22)
   529  	st.Insert(b("foo.yyybar789.zz"), 22)
   530  	st.Insert(b("foo.yyy.foo12345.zz"), 22)
   531  	st.Insert(b("foo.yyy.foo12345.yy"), 22)
   532  	st.Insert(b("foo.yyy.foo123456789.zz"), 22)
   533  	match(t, st, "foo.*.foo123456789.*", 1)
   534  	match(t, st, "foo.*.*.zzz.foo.>", 0)
   535  }
   536  
   537  func TestSubjectTreeMatchMultipleWildcardBasic(t *testing.T) {
   538  	st := NewSubjectTree[int]()
   539  	st.Insert(b("A.B.C.D.0.G.H.I.0"), 22)
   540  	st.Insert(b("A.B.C.D.1.G.H.I.0"), 22)
   541  	match(t, st, "A.B.*.D.1.*.*.I.0", 1)
   542  }
   543  
   544  func TestSubjectTreeMatchInvalidWildcard(t *testing.T) {
   545  	st := NewSubjectTree[int]()
   546  	st.Insert(b("foo.123"), 22)
   547  	st.Insert(b("one.two.three.four.five"), 22)
   548  	st.Insert(b("'*.123"), 22)
   549  	match(t, st, "invalid.>", 0)
   550  	match(t, st, ">", 3)
   551  	match(t, st, `'*.*`, 1)
   552  	match(t, st, `'*.*.*'`, 0)
   553  	// None of these should match.
   554  	match(t, st, "`>`", 0)
   555  	match(t, st, `">"`, 0)
   556  	match(t, st, `'>'`, 0)
   557  	match(t, st, `'*.>'`, 0)
   558  	match(t, st, `'*.>.`, 0)
   559  	match(t, st, "`invalid.>`", 0)
   560  	match(t, st, `'*.*'`, 0)
   561  }
   562  
   563  func TestSubjectTreeRandomTrackEntries(t *testing.T) {
   564  	st := NewSubjectTree[int]()
   565  	smap := make(map[string]struct{}, 1000)
   566  
   567  	// Make sure all added items can be found.
   568  	check := func() {
   569  		t.Helper()
   570  		for subj := range smap {
   571  			if _, found := st.Find(b(subj)); !found {
   572  				t.Fatalf("Could not find subject %q which should be findable", subj)
   573  			}
   574  		}
   575  	}
   576  
   577  	buf := make([]byte, 10)
   578  	for i := 0; i < 1000; i++ {
   579  		var sb strings.Builder
   580  		// 1-6 tokens.
   581  		numTokens := rand.Intn(6) + 1
   582  		for i := 0; i < numTokens; i++ {
   583  			tlen := rand.Intn(4) + 2
   584  			tok := buf[:tlen]
   585  			crand.Read(tok)
   586  			sb.WriteString(hex.EncodeToString(tok))
   587  			if i != numTokens-1 {
   588  				sb.WriteString(".")
   589  			}
   590  		}
   591  		subj := sb.String()
   592  		// Avoid dupes since will cause check to fail after we delete messages.
   593  		if _, ok := smap[subj]; ok {
   594  			continue
   595  		}
   596  		smap[subj] = struct{}{}
   597  		old, updated := st.Insert(b(subj), 22)
   598  		require_True(t, old == nil)
   599  		require_False(t, updated)
   600  		require_Equal(t, st.Size(), len(smap))
   601  		check()
   602  	}
   603  }
   604  
   605  func TestSubjectTreeMetaSize(t *testing.T) {
   606  	var base meta
   607  	require_Equal(t, unsafe.Sizeof(base), 28)
   608  }
   609  
   610  func b(s string) []byte {
   611  	return []byte(s)
   612  }
   613  
   614  func TestSubjectTreeMatchAllPerf(t *testing.T) {
   615  	if !*runResults {
   616  		t.Skip()
   617  	}
   618  	st := NewSubjectTree[int]()
   619  
   620  	for i := 0; i < 1_000_000; i++ {
   621  		subj := fmt.Sprintf("subj.%d.%d", rand.Intn(100)+1, i)
   622  		st.Insert(b(subj), 22)
   623  	}
   624  
   625  	for _, f := range [][]byte{
   626  		[]byte(">"),
   627  		[]byte("subj.>"),
   628  		[]byte("subj.*.*"),
   629  		[]byte("*.*.*"),
   630  		[]byte("subj.1.*"),
   631  		[]byte("subj.1.>"),
   632  		[]byte("subj.*.1"),
   633  		[]byte("*.*.1"),
   634  	} {
   635  		start := time.Now()
   636  		count := 0
   637  		st.Match(f, func(_ []byte, _ *int) {
   638  			count++
   639  		})
   640  		t.Logf("Match %q took %s and matched %d entries", f, time.Since(start), count)
   641  	}
   642  }