// 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 ( "runtime" "testing" "github.com/satori/go.uuid" ) // Tests ----------------------------------------------------------------------- // overhead is allowed tolerance for Go's runtime/GC to increase the allocated memory // (to avoid failing tests on insignificant growth amounts) const overhead = 4000 func TestTrie_InsertDense(t *testing.T) { trie := NewTrie() data := []testData{ {"aba", 0, success}, {"abb", 1, success}, {"abc", 2, success}, {"abd", 3, success}, {"abe", 4, success}, {"abf", 5, success}, {"abg", 6, success}, {"abh", 7, success}, {"abi", 8, success}, {"abj", 9, success}, {"abk", 0, success}, {"abl", 1, success}, {"abm", 2, success}, {"abn", 3, success}, {"abo", 4, success}, {"abp", 5, success}, {"abq", 6, success}, {"abr", 7, success}, {"abs", 8, success}, {"abt", 9, success}, {"abu", 0, success}, {"abv", 1, success}, {"abw", 2, success}, {"abx", 3, success}, {"aby", 4, success}, {"abz", 5, 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_InsertDensePreceeding(t *testing.T) { trie := NewTrie() start := byte(70) // create a dense node for i := byte(0); i <= DefaultMaxChildrenPerSparseNode; i++ { if !trie.Insert(Prefix([]byte{start + i}), true) { t.Errorf("insert failed, prefix=%v", start+i) } } // insert some preceeding keys for i := byte(1); i < start; i *= i + 1 { if !trie.Insert(Prefix([]byte{start - i}), true) { t.Errorf("insert failed, prefix=%v", start-i) } } } func TestTrie_InsertDenseDuplicatePrefixes(t *testing.T) { trie := NewTrie() data := []testData{ {"aba", 0, success}, {"abb", 1, success}, {"abc", 2, success}, {"abd", 3, success}, {"abe", 4, success}, {"abf", 5, success}, {"abg", 6, success}, {"abh", 7, success}, {"abi", 8, success}, {"abj", 9, success}, {"abk", 0, success}, {"abl", 1, success}, {"abm", 2, success}, {"abn", 3, success}, {"abo", 4, success}, {"abp", 5, success}, {"abq", 6, success}, {"abr", 7, success}, {"abs", 8, success}, {"abt", 9, success}, {"abu", 0, success}, {"abv", 1, success}, {"abw", 2, success}, {"abx", 3, success}, {"aby", 4, success}, {"abz", 5, success}, {"aba", 0, failure}, {"abb", 1, failure}, {"abc", 2, failure}, {"abd", 3, failure}, {"abe", 4, 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_DeleteDense(t *testing.T) { trie := NewTrie() data := []testData{ {"aba", 0, success}, {"abb", 1, success}, {"abc", 2, success}, {"abd", 3, success}, {"abe", 4, success}, {"abf", 5, success}, {"abg", 6, success}, {"abh", 7, success}, {"abi", 8, success}, {"abj", 9, success}, {"abk", 0, success}, {"abl", 1, success}, {"abm", 2, success}, {"abn", 3, success}, {"abo", 4, success}, {"abp", 5, success}, {"abq", 6, success}, {"abr", 7, success}, {"abs", 8, success}, {"abt", 9, success}, {"abu", 0, success}, {"abv", 1, success}, {"abw", 2, success}, {"abx", 3, success}, {"aby", 4, success}, {"abz", 5, 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("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 TestTrie_DeleteLeakageDense(t *testing.T) { trie := NewTrie() genTestData := func() *testData { // Generate a random hash as a key. key := uuid.NewV4() return &testData{key: key.String(), value: "v", retVal: success} } testSize := 100 data := make([]*testData, 0, testSize) for i := 0; i < testSize; i++ { data = append(data, genTestData()) } oldBytes := heapAllocatedBytes() // repeat insertion/deletion for 10K times to catch possible memory issues for i := 0; i < 10000; i++ { for _, v := range data { 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 { 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 (%d to %d)\n", newBytes-oldBytes, oldBytes, newBytes) } if numChildren := trie.children.length(); numChildren != 0 { t.Errorf("Trie is not empty: %v children found", numChildren) } } func heapAllocatedBytes() uint64 { runtime.GC() ms := runtime.MemStats{} runtime.ReadMemStats(&ms) return ms.Alloc }