| package radix |
| |
| import ( |
| crand "crypto/rand" |
| "fmt" |
| "reflect" |
| "sort" |
| "testing" |
| ) |
| |
| func TestRadix(t *testing.T) { |
| var min, max string |
| inp := make(map[string]interface{}) |
| 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)) |
| } |
| |
| r.Walk(func(k string, v interface{}) bool { |
| println(k) |
| return false |
| }) |
| |
| for k, v := range inp { |
| out, ok := r.Get(k) |
| if !ok { |
| t.Fatalf("missing key: %v", k) |
| } |
| if out != v { |
| t.Fatalf("value mis-match: %v %v", out, v) |
| } |
| } |
| |
| // Check min and max |
| outMin, _, _ := r.Minimum() |
| if outMin != min { |
| t.Fatalf("bad minimum: %v %v", outMin, min) |
| } |
| outMax, _, _ := r.Maximum() |
| if outMax != max { |
| t.Fatalf("bad maximum: %v %v", outMax, max) |
| } |
| |
| for k, v := range inp { |
| out, ok := r.Delete(k) |
| if !ok { |
| t.Fatalf("missing key: %v", k) |
| } |
| if out != v { |
| t.Fatalf("value mis-match: %v %v", out, v) |
| } |
| } |
| if r.Len() != 0 { |
| t.Fatalf("bad length: %v", r.Len()) |
| } |
| } |
| |
| func TestRoot(t *testing.T) { |
| r := New() |
| _, ok := r.Delete("") |
| if ok { |
| t.Fatalf("bad") |
| } |
| _, ok = r.Insert("", true) |
| if ok { |
| t.Fatalf("bad") |
| } |
| val, ok := r.Get("") |
| if !ok || val != true { |
| t.Fatalf("bad: %v", val) |
| } |
| val, ok = r.Delete("") |
| if !ok || val != true { |
| t.Fatalf("bad: %v", val) |
| } |
| } |
| |
| func TestDelete(t *testing.T) { |
| |
| r := New() |
| |
| s := []string{"", "A", "AB"} |
| |
| for _, ss := range s { |
| r.Insert(ss, true) |
| } |
| |
| for _, ss := range s { |
| _, ok := r.Delete(ss) |
| if !ok { |
| t.Fatalf("bad %q", ss) |
| } |
| } |
| } |
| |
| func TestLongestPrefix(t *testing.T) { |
| r := New() |
| |
| keys := []string{ |
| "", |
| "foo", |
| "foobar", |
| "foobarbaz", |
| "foobarbazzip", |
| "foozip", |
| } |
| for _, k := range keys { |
| r.Insert(k, nil) |
| } |
| if r.Len() != len(keys) { |
| t.Fatalf("bad len: %v %v", r.Len(), len(keys)) |
| } |
| |
| type exp struct { |
| inp string |
| out string |
| } |
| cases := []exp{ |
| {"a", ""}, |
| {"abc", ""}, |
| {"fo", ""}, |
| {"foo", "foo"}, |
| {"foob", "foo"}, |
| {"foobar", "foobar"}, |
| {"foobarba", "foobar"}, |
| {"foobarbaz", "foobarbaz"}, |
| {"foobarbazzi", "foobarbaz"}, |
| {"foobarbazzip", "foobarbazzip"}, |
| {"foozi", "foo"}, |
| {"foozip", "foozip"}, |
| {"foozipzap", "foozip"}, |
| } |
| for _, test := range cases { |
| m, _, ok := r.LongestPrefix(test.inp) |
| if !ok { |
| t.Fatalf("no match: %v", test) |
| } |
| if m != test.out { |
| t.Fatalf("mis-match: %v %v", m, test) |
| } |
| } |
| } |
| |
| func TestWalkPrefix(t *testing.T) { |
| r := New() |
| |
| keys := []string{ |
| "foobar", |
| "foo/bar/baz", |
| "foo/baz/bar", |
| "foo/zip/zap", |
| "zipzap", |
| } |
| for _, k := range keys { |
| r.Insert(k, nil) |
| } |
| if r.Len() != len(keys) { |
| t.Fatalf("bad len: %v %v", r.Len(), len(keys)) |
| } |
| |
| type exp struct { |
| inp string |
| out []string |
| } |
| cases := []exp{ |
| { |
| "f", |
| []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, |
| }, |
| { |
| "foo", |
| []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, |
| }, |
| { |
| "foob", |
| []string{"foobar"}, |
| }, |
| { |
| "foo/", |
| []string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, |
| }, |
| { |
| "foo/b", |
| []string{"foo/bar/baz", "foo/baz/bar"}, |
| }, |
| { |
| "foo/ba", |
| []string{"foo/bar/baz", "foo/baz/bar"}, |
| }, |
| { |
| "foo/bar", |
| []string{"foo/bar/baz"}, |
| }, |
| { |
| "foo/bar/baz", |
| []string{"foo/bar/baz"}, |
| }, |
| { |
| "foo/bar/bazoo", |
| []string{}, |
| }, |
| { |
| "z", |
| []string{"zipzap"}, |
| }, |
| } |
| |
| for _, test := range cases { |
| out := []string{} |
| fn := func(s string, v interface{}) bool { |
| out = append(out, s) |
| return false |
| } |
| r.WalkPrefix(test.inp, fn) |
| sort.Strings(out) |
| sort.Strings(test.out) |
| if !reflect.DeepEqual(out, test.out) { |
| t.Fatalf("mis-match: %v %v", out, test.out) |
| } |
| } |
| } |
| |
| func TestWalkPath(t *testing.T) { |
| r := New() |
| |
| keys := []string{ |
| "foo", |
| "foo/bar", |
| "foo/bar/baz", |
| "foo/baz/bar", |
| "foo/zip/zap", |
| "zipzap", |
| } |
| for _, k := range keys { |
| r.Insert(k, nil) |
| } |
| if r.Len() != len(keys) { |
| t.Fatalf("bad len: %v %v", r.Len(), len(keys)) |
| } |
| |
| type exp struct { |
| inp string |
| out []string |
| } |
| cases := []exp{ |
| { |
| "f", |
| []string{}, |
| }, |
| { |
| "foo", |
| []string{"foo"}, |
| }, |
| { |
| "foo/", |
| []string{"foo"}, |
| }, |
| { |
| "foo/ba", |
| []string{"foo"}, |
| }, |
| { |
| "foo/bar", |
| []string{"foo", "foo/bar"}, |
| }, |
| { |
| "foo/bar/baz", |
| []string{"foo", "foo/bar", "foo/bar/baz"}, |
| }, |
| { |
| "foo/bar/bazoo", |
| []string{"foo", "foo/bar", "foo/bar/baz"}, |
| }, |
| { |
| "z", |
| []string{}, |
| }, |
| } |
| |
| for _, test := range cases { |
| out := []string{} |
| fn := func(s string, v interface{}) bool { |
| out = append(out, s) |
| return false |
| } |
| r.WalkPath(test.inp, fn) |
| sort.Strings(out) |
| sort.Strings(test.out) |
| if !reflect.DeepEqual(out, test.out) { |
| t.Fatalf("mis-match: %v %v", out, test.out) |
| } |
| } |
| } |
| |
| // generateUUID is used to generate a random UUID |
| func generateUUID() string { |
| buf := make([]byte, 16) |
| if _, err := crand.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]) |
| } |