influxdb/pkg/radix/tree_test.go

175 lines
3.5 KiB
Go

package radix
import (
"crypto/rand"
"fmt"
"reflect"
"testing"
)
// generateUUID is used to generate a random UUID
func generateUUID() string {
buf := make([]byte, 16)
if _, err := rand.Read(buf); err != nil {
panic(fmt.Errorf("failed to read random bytes: %v", err))
}
return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
buf[0:4],
buf[4:6],
buf[6:8],
buf[8:10],
buf[10:16])
}
func TestRadix(t *testing.T) {
var min, max string
inp := make(map[string]int)
for i := 0; i < 1000; i++ {
gen := generateUUID()
inp[gen] = i
if gen < min || i == 0 {
min = gen
}
if gen > max || i == 0 {
max = gen
}
}
r := NewFromMap(inp)
if r.Len() != len(inp) {
t.Fatalf("bad length: %v %v", r.Len(), len(inp))
}
// Check min and max
outMin, _, _ := r.Minimum()
if string(outMin) != min {
t.Fatalf("bad minimum: %s %v", outMin, min)
}
outMax, _, _ := r.Maximum()
if string(outMax) != max {
t.Fatalf("bad maximum: %s %v", outMax, max)
}
for k, v := range inp {
out, ok := r.Get([]byte(k))
if !ok {
t.Fatalf("missing key: %v", k)
}
if out != v {
t.Fatalf("value mis-match: %v %v", out, v)
}
}
}
func TestDeletePrefix(t *testing.T) {
type exp struct {
inp []string
prefix string
out []string
numDeleted int
}
cases := []exp{
{[]string{"", "A", "AB", "ABC", "R", "S"}, "A", []string{"", "R", "S"}, 3},
{[]string{"", "A", "AB", "ABC", "R", "S"}, "ABC", []string{"", "A", "AB", "R", "S"}, 1},
{[]string{"", "A", "AB", "ABC", "R", "S"}, "", []string{}, 6},
{[]string{"", "A", "AB", "ABC", "R", "S"}, "S", []string{"", "A", "AB", "ABC", "R"}, 1},
{[]string{"", "A", "AB", "ABC", "R", "S"}, "SS", []string{"", "A", "AB", "ABC", "R", "S"}, 0},
}
for _, test := range cases {
r := New()
for _, ss := range test.inp {
r.Insert([]byte(ss), 1)
}
deleted := r.DeletePrefix([]byte(test.prefix))
if deleted != test.numDeleted {
t.Fatalf("Bad delete, expected %v to be deleted but got %v", test.numDeleted, deleted)
}
out := []string{}
fn := func(s []byte, v int) bool {
out = append(out, string(s))
return false
}
recursiveWalk(r.root, fn)
if !reflect.DeepEqual(out, test.out) {
t.Fatalf("mis-match: %v %v", out, test.out)
}
}
}
func TestInsert_Duplicate(t *testing.T) {
r := New()
vv, ok := r.Insert([]byte("cpu"), 1)
if vv != 1 {
t.Fatalf("value mismatch: got %v, exp %v", vv, 1)
}
if !ok {
t.Fatalf("value mismatch: got %v, exp %v", ok, true)
}
// Insert a dup with a different type should fail
vv, ok = r.Insert([]byte("cpu"), 2)
if vv != 1 {
t.Fatalf("value mismatch: got %v, exp %v", vv, 1)
}
if ok {
t.Fatalf("value mismatch: got %v, exp %v", ok, false)
}
}
//
// benchmarks
//
func BenchmarkTree_Insert(b *testing.B) {
t := New()
keys := make([][]byte, 0, 10000)
for i := 0; i < cap(keys); i++ {
k := []byte(fmt.Sprintf("cpu,host=%d", i))
if v, ok := t.Insert(k, 1); v != 1 || !ok {
b.Fatalf("insert failed: %v != 1 || !%v", v, ok)
}
keys = append(keys, k)
}
b.SetBytes(int64(len(keys)))
b.ReportAllocs()
b.ResetTimer()
for j := 0; j < b.N; j++ {
for _, key := range keys {
if v, ok := t.Insert(key, 1); v != 1 || ok {
b.Fatalf("insert failed: %v != 1 || !%v", v, ok)
}
}
}
}
func BenchmarkTree_InsertNew(b *testing.B) {
keys := make([][]byte, 0, 10000)
for i := 0; i < cap(keys); i++ {
k := []byte(fmt.Sprintf("cpu,host=%d", i))
keys = append(keys, k)
}
b.SetBytes(int64(len(keys)))
b.ReportAllocs()
b.ResetTimer()
for j := 0; j < b.N; j++ {
t := New()
for _, key := range keys {
t.Insert(key, 1)
}
}
}