cri-o/vendor/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go

691 lines
17 KiB
Go
Raw Normal View History

// Copyright (c) 2014 The go-patricia AUTHORS
//
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.
package patricia
import (
"bytes"
"errors"
"fmt"
"strings"
"testing"
)
const (
success = true
failure = false
)
type testData struct {
key string
value interface{}
retVal bool
}
// Tests -----------------------------------------------------------------------
func TestTrie_InsertDifferentPrefixes(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success},
{"Honzooooooooooooooo", "Honza Novak", success},
{"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
}
func TestTrie_InsertDuplicatePrefixes(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Pepan", "Pepan Zdepan", failure},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
}
func TestTrie_InsertVariousPrefixes(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Pepin", "Pepin Omacka", success},
{"Honza", "Honza Novak", success},
{"Jenik", "Jenik Poustevnicek", success},
{"Pepan", "Pepan Dupan", failure},
{"Karel", "Karel Pekar", success},
{"Jenik", "Jenik Poustevnicek", failure},
{"Pepanek", "Pepanek Zemlicka", success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
}
func TestTrie_InsertAndMatchPrefix(t *testing.T) {
trie := NewTrie()
t.Log("INSERT prefix=by week")
trie.Insert(Prefix("by week"), 2)
t.Log("INSERT prefix=by")
trie.Insert(Prefix("by"), 1)
if !trie.Match(Prefix("by")) {
t.Error("MATCH prefix=by, expected=true, got=false")
}
}
func TestTrie_SetGet(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Pepin", "Pepin Omacka", success},
{"Honza", "Honza Novak", success},
{"Jenik", "Jenik Poustevnicek", success},
{"Pepan", "Pepan Dupan", failure},
{"Karel", "Karel Pekar", success},
{"Jenik", "Jenik Poustevnicek", failure},
{"Pepanek", "Pepanek Zemlicka", success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
for _, v := range data {
t.Logf("SET %q to 10", v.key)
trie.Set(Prefix(v.key), 10)
}
for _, v := range data {
value := trie.Get(Prefix(v.key))
t.Logf("GET %q => %v", v.key, value)
if value.(int) != 10 {
t.Errorf("Unexpected return value, %v != 10", value)
}
}
if value := trie.Get(Prefix("random crap")); value != nil {
t.Errorf("Unexpected return value, %v != <nil>", value)
}
}
func TestTrie_Match(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Pepin", "Pepin Omacka", success},
{"Honza", "Honza Novak", success},
{"Jenik", "Jenik Poustevnicek", success},
{"Pepan", "Pepan Dupan", failure},
{"Karel", "Karel Pekar", success},
{"Jenik", "Jenik Poustevnicek", failure},
{"Pepanek", "Pepanek Zemlicka", success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
for _, v := range data {
matched := trie.Match(Prefix(v.key))
t.Logf("MATCH %q => %v", v.key, matched)
if !matched {
t.Errorf("Inserted key %q was not matched", v.key)
}
}
if trie.Match(Prefix("random crap")) {
t.Errorf("Key that was not inserted matched: %q", "random crap")
}
}
func TestTrie_MatchFalsePositive(t *testing.T) {
trie := NewTrie()
if ok := trie.Insert(Prefix("A"), 1); !ok {
t.Fatal("INSERT prefix=A, item=1 not ok")
}
resultMatchSubtree := trie.MatchSubtree(Prefix("A extra"))
resultMatch := trie.Match(Prefix("A extra"))
if resultMatchSubtree != false {
t.Error("MatchSubtree returned false positive")
}
if resultMatch != false {
t.Error("Match returned false positive")
}
}
func TestTrie_MatchSubtree(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Pepin", "Pepin Omacka", success},
{"Honza", "Honza Novak", success},
{"Jenik", "Jenik Poustevnicek", success},
{"Pepan", "Pepan Dupan", failure},
{"Karel", "Karel Pekar", success},
{"Jenik", "Jenik Poustevnicek", failure},
{"Pepanek", "Pepanek Zemlicka", success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
for _, v := range data {
key := Prefix(v.key[:3])
matched := trie.MatchSubtree(key)
t.Logf("MATCH_SUBTREE %q => %v", key, matched)
if !matched {
t.Errorf("Subtree %q was not matched", v.key)
}
}
}
func TestTrie_Visit(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepa", 0, success},
{"Pepa Zdepa", 1, success},
{"Pepa Kuchar", 2, success},
{"Honza", 3, success},
{"Jenik", 4, success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
if err := trie.Visit(func(prefix Prefix, item Item) error {
name := data[item.(int)].key
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
if !strings.HasPrefix(string(prefix), name) {
t.Errorf("Unexpected prefix encountered, %q not a prefix of %q", prefix, name)
}
return nil
}); err != nil {
t.Fatal(err)
}
}
func TestTrie_VisitSkipSubtree(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepa", 0, success},
{"Pepa Zdepa", 1, success},
{"Pepa Kuchar", 2, success},
{"Honza", 3, success},
{"Jenik", 4, success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
if err := trie.Visit(func(prefix Prefix, item Item) error {
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
if item.(int) == 0 {
t.Logf("SKIP %q", prefix)
return SkipSubtree
}
if strings.HasPrefix(string(prefix), "Pepa") {
t.Errorf("Unexpected prefix encountered, %q", prefix)
}
return nil
}); err != nil {
t.Fatal(err)
}
}
func TestTrie_VisitReturnError(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepa", 0, success},
{"Pepa Zdepa", 1, success},
{"Pepa Kuchar", 2, success},
{"Honza", 3, success},
{"Jenik", 4, success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
someErr := errors.New("Something exploded")
if err := trie.Visit(func(prefix Prefix, item Item) error {
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
if item.(int) == 3 {
return someErr
}
if item.(int) != 3 {
t.Errorf("Unexpected prefix encountered, %q", prefix)
}
return nil
}); err != nil && err != someErr {
t.Fatal(err)
}
}
func TestTrie_VisitSubtree(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepa", 0, success},
{"Pepa Zdepa", 1, success},
{"Pepa Kuchar", 2, success},
{"Honza", 3, success},
{"Jenik", 4, success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
var counter int
subtreePrefix := []byte("Pep")
t.Log("VISIT Pep")
if err := trie.VisitSubtree(subtreePrefix, func(prefix Prefix, item Item) error {
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
if !bytes.HasPrefix(prefix, subtreePrefix) {
t.Errorf("Unexpected prefix encountered, %q does not extend %q",
prefix, subtreePrefix)
}
if len(prefix) > len(data[item.(int)].key) {
t.Fatalf("Something is rather fishy here, prefix=%q", prefix)
}
counter++
return nil
}); err != nil {
t.Fatal(err)
}
if counter != 3 {
t.Error("Unexpected number of nodes visited")
}
}
func TestTrie_VisitPrefixes(t *testing.T) {
trie := NewTrie()
data := []testData{
{"P", 0, success},
{"Pe", 1, success},
{"Pep", 2, success},
{"Pepa", 3, success},
{"Pepa Zdepa", 4, success},
{"Pepa Kuchar", 5, success},
{"Honza", 6, success},
{"Jenik", 7, success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
var counter int
word := []byte("Pepa")
if err := trie.VisitPrefixes(word, func(prefix Prefix, item Item) error {
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
if !bytes.HasPrefix(word, prefix) {
t.Errorf("Unexpected prefix encountered, %q is not a prefix of %q",
prefix, word)
}
counter++
return nil
}); err != nil {
t.Fatal(err)
}
if counter != 4 {
t.Error("Unexpected number of nodes visited")
}
}
func TestParticiaTrie_Delete(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Honza", "Honza Novak", success},
{"Jenik", "Jenik Poustevnicek", success},
}
for _, v := range data {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
for _, v := range data {
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
}
func TestParticiaTrie_DeleteLeakageSparse(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Honza", "Honza Novak", success},
{"Jenik", "Jenik Poustevnicek", success},
}
oldBytes := heapAllocatedBytes()
for i := 0; i < 10000; i++ {
for _, v := range data {
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
for _, v := range data {
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
}
if newBytes := heapAllocatedBytes(); newBytes > oldBytes+overhead {
t.Logf("Size=%d, Total=%d, Trie state:\n%s\n", trie.size(), trie.total(), trie.dump())
t.Errorf("Heap space leak, grew %d bytes (from %d to %d)\n", newBytes-oldBytes, oldBytes, newBytes)
}
}
func TestParticiaTrie_DeleteNonExistent(t *testing.T) {
trie := NewTrie()
insertData := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Honza", "Honza Novak", success},
{"Jenik", "Jenik Poustevnicek", success},
}
deleteData := []testData{
{"Pepan", "Pepan Zdepan", success},
{"Honza", "Honza Novak", success},
{"Pepan", "Pepan Zdepan", failure},
{"Jenik", "Jenik Poustevnicek", success},
{"Honza", "Honza Novak", failure},
}
for _, v := range insertData {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
for _, v := range deleteData {
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
}
func TestParticiaTrie_DeleteSubtree(t *testing.T) {
trie := NewTrie()
insertData := []testData{
{"P", 0, success},
{"Pe", 1, success},
{"Pep", 2, success},
{"Pepa", 3, success},
{"Pepa Zdepa", 4, success},
{"Pepa Kuchar", 5, success},
{"Honza", 6, success},
{"Jenik", 7, success},
}
deleteData := []testData{
{"Pe", -1, success},
{"Pe", -1, failure},
{"Honzik", -1, failure},
{"Honza", -1, success},
{"Honza", -1, failure},
{"Pep", -1, failure},
{"P", -1, success},
{"Nobody", -1, failure},
{"", -1, success},
}
for _, v := range insertData {
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
for _, v := range deleteData {
t.Logf("DELETE_SUBTREE prefix=%v, success=%v", v.key, v.retVal)
if ok := trie.DeleteSubtree([]byte(v.key)); ok != v.retVal {
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
}
}
}
/*
func TestTrie_Dump(t *testing.T) {
trie := NewTrie()
data := []testData{
{"Honda", nil, success},
{"Honza", nil, success},
{"Jenik", nil, success},
{"Pepan", nil, success},
{"Pepin", nil, success},
}
for i, v := range data {
if _, ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
t.Logf("INSERT %v %v", v.key, v.value)
t.Fatalf("Unexpected return value, expected=%v, got=%v", i, ok)
}
}
dump := `
+--+--+ Hon +--+--+ da
| |
| +--+ za
|
+--+ Jenik
|
+--+ Pep +--+--+ an
|
+--+ in
`
var buf bytes.Buffer
trie.Dump(buf)
if !bytes.Equal(buf.Bytes(), dump) {
t.Logf("DUMP")
t.Fatalf("Unexpected dump generated, expected\n\n%v\ngot\n\n%v", dump, buf.String())
}
}
*/
func TestTrie_compact(t *testing.T) {
trie := NewTrie()
trie.Insert(Prefix("a"), 0)
trie.Insert(Prefix("ab"), 0)
trie.Insert(Prefix("abc"), 0)
trie.Insert(Prefix("abcd"), 0)
trie.Insert(Prefix("abcde"), 0)
trie.Insert(Prefix("abcdef"), 0)
trie.Insert(Prefix("abcdefg"), 0)
trie.Insert(Prefix("abcdefgi"), 0)
trie.Insert(Prefix("abcdefgij"), 0)
trie.Insert(Prefix("abcdefgijk"), 0)
trie.Delete(Prefix("abcdef"))
trie.Delete(Prefix("abcde"))
trie.Delete(Prefix("abcdefg"))
trie.Delete(Prefix("a"))
trie.Delete(Prefix("abc"))
trie.Delete(Prefix("ab"))
trie.Visit(func(prefix Prefix, item Item) error {
// 97 ~~ 'a',
for ch := byte(97); ch <= 107; ch++ {
if c := bytes.Count(prefix, []byte{ch}); c > 1 {
t.Errorf("%q appeared in %q %v times", ch, prefix, c)
}
}
return nil
})
}
func TestTrie_longestCommonPrefixLenght(t *testing.T) {
trie := NewTrie()
trie.prefix = []byte("1234567890")
switch {
case trie.longestCommonPrefixLength([]byte("")) != 0:
t.Fail()
case trie.longestCommonPrefixLength([]byte("12345")) != 5:
t.Fail()
case trie.longestCommonPrefixLength([]byte("123789")) != 3:
t.Fail()
case trie.longestCommonPrefixLength([]byte("12345678901")) != 10:
t.Fail()
}
}
// Examples --------------------------------------------------------------------
func ExampleTrie() {
// Create a new tree.
trie := NewTrie()
// Insert some items.
trie.Insert(Prefix("Pepa Novak"), 1)
trie.Insert(Prefix("Pepa Sindelar"), 2)
trie.Insert(Prefix("Karel Macha"), 3)
trie.Insert(Prefix("Karel Hynek Macha"), 4)
// Just check if some things are present in the tree.
key := Prefix("Pepa Novak")
fmt.Printf("%q present? %v\n", key, trie.Match(key))
key = Prefix("Karel")
fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
// Walk the tree.
trie.Visit(printItem)
// "Karel Hynek Macha": 4
// "Karel Macha": 3
// "Pepa Novak": 1
// "Pepa Sindelar": 2
// Walk a subtree.
trie.VisitSubtree(Prefix("Pepa"), printItem)
// "Pepa Novak": 1
// "Pepa Sindelar": 2
// Modify an item, then fetch it from the tree.
trie.Set(Prefix("Karel Hynek Macha"), 10)
key = Prefix("Karel Hynek Macha")
fmt.Printf("%q: %v\n", key, trie.Get(key))
// "Karel Hynek Macha": 10
// Walk prefixes.
prefix := Prefix("Karel Hynek Macha je kouzelnik")
trie.VisitPrefixes(prefix, printItem)
// "Karel Hynek Macha": 10
// Delete some items.
trie.Delete(Prefix("Pepa Novak"))
trie.Delete(Prefix("Karel Macha"))
// Walk again.
trie.Visit(printItem)
// "Karel Hynek Macha": 10
// "Pepa Sindelar": 2
// Delete a subtree.
trie.DeleteSubtree(Prefix("Pepa"))
// Print what is left.
trie.Visit(printItem)
// "Karel Hynek Macha": 10
// Output:
// "Pepa Novak" present? true
// Anybody called "Karel" here? true
// "Karel Hynek Macha": 4
// "Karel Macha": 3
// "Pepa Novak": 1
// "Pepa Sindelar": 2
// "Pepa Novak": 1
// "Pepa Sindelar": 2
// "Karel Hynek Macha": 10
// "Karel Hynek Macha": 10
// "Karel Hynek Macha": 10
// "Pepa Sindelar": 2
// "Karel Hynek Macha": 10
}
// Helpers ---------------------------------------------------------------------
func printItem(prefix Prefix, item Item) error {
fmt.Printf("%q: %v\n", prefix, item)
return nil
}