github.com/sunvim/utils@v0.1.0/radix/radix_test.go (about)

     1  package radix
     2  
     3  import (
     4  	crand "crypto/rand"
     5  	"fmt"
     6  	"reflect"
     7  	"sort"
     8  	"testing"
     9  )
    10  
    11  func TestRadix(t *testing.T) {
    12  	var min, max string
    13  	inp := make(map[string]interface{})
    14  	for i := 0; i < 1000; i++ {
    15  		gen := generateUUID()
    16  		inp[gen] = i
    17  		if gen < min || i == 0 {
    18  			min = gen
    19  		}
    20  		if gen > max || i == 0 {
    21  			max = gen
    22  		}
    23  	}
    24  
    25  	r := NewFromMap(inp)
    26  	if r.Len() != len(inp) {
    27  		t.Fatalf("bad length: %v %v", r.Len(), len(inp))
    28  	}
    29  
    30  	r.Walk(func(k string, v interface{}) bool {
    31  		println(k)
    32  		return false
    33  	})
    34  
    35  	for k, v := range inp {
    36  		out, ok := r.Get(k)
    37  		if !ok {
    38  			t.Fatalf("missing key: %v", k)
    39  		}
    40  		if out != v {
    41  			t.Fatalf("value mis-match: %v %v", out, v)
    42  		}
    43  	}
    44  
    45  	// Check min and max
    46  	outMin, _, _ := r.Minimum()
    47  	if outMin != min {
    48  		t.Fatalf("bad minimum: %v %v", outMin, min)
    49  	}
    50  	outMax, _, _ := r.Maximum()
    51  	if outMax != max {
    52  		t.Fatalf("bad maximum: %v %v", outMax, max)
    53  	}
    54  
    55  	for k, v := range inp {
    56  		out, ok := r.Delete(k)
    57  		if !ok {
    58  			t.Fatalf("missing key: %v", k)
    59  		}
    60  		if out != v {
    61  			t.Fatalf("value mis-match: %v %v", out, v)
    62  		}
    63  	}
    64  	if r.Len() != 0 {
    65  		t.Fatalf("bad length: %v", r.Len())
    66  	}
    67  }
    68  
    69  func TestRoot(t *testing.T) {
    70  	r := New()
    71  	_, ok := r.Delete("")
    72  	if ok {
    73  		t.Fatalf("bad")
    74  	}
    75  	_, ok = r.Insert("", true)
    76  	if ok {
    77  		t.Fatalf("bad")
    78  	}
    79  	val, ok := r.Get("")
    80  	if !ok || val != true {
    81  		t.Fatalf("bad: %v", val)
    82  	}
    83  	val, ok = r.Delete("")
    84  	if !ok || val != true {
    85  		t.Fatalf("bad: %v", val)
    86  	}
    87  }
    88  
    89  func TestDelete(t *testing.T) {
    90  
    91  	r := New()
    92  
    93  	s := []string{"", "A", "AB"}
    94  
    95  	for _, ss := range s {
    96  		r.Insert(ss, true)
    97  	}
    98  
    99  	for _, ss := range s {
   100  		_, ok := r.Delete(ss)
   101  		if !ok {
   102  			t.Fatalf("bad %q", ss)
   103  		}
   104  	}
   105  }
   106  
   107  func TestDeletePrefix(t *testing.T) {
   108  	type exp struct {
   109  		inp        []string
   110  		prefix     string
   111  		out        []string
   112  		numDeleted int
   113  	}
   114  
   115  	cases := []exp{
   116  		{[]string{"", "A", "AB", "ABC", "R", "S"}, "A", []string{"", "R", "S"}, 3},
   117  		{[]string{"", "A", "AB", "ABC", "R", "S"}, "ABC", []string{"", "A", "AB", "R", "S"}, 1},
   118  		{[]string{"", "A", "AB", "ABC", "R", "S"}, "", []string{}, 6},
   119  		{[]string{"", "A", "AB", "ABC", "R", "S"}, "S", []string{"", "A", "AB", "ABC", "R"}, 1},
   120  		{[]string{"", "A", "AB", "ABC", "R", "S"}, "SS", []string{"", "A", "AB", "ABC", "R", "S"}, 0},
   121  	}
   122  
   123  	for _, test := range cases {
   124  		r := New()
   125  		for _, ss := range test.inp {
   126  			r.Insert(ss, true)
   127  		}
   128  
   129  		deleted := r.DeletePrefix(test.prefix)
   130  		if deleted != test.numDeleted {
   131  			t.Fatalf("Bad delete, expected %v to be deleted but got %v", test.numDeleted, deleted)
   132  		}
   133  
   134  		out := []string{}
   135  		fn := func(s string, v interface{}) bool {
   136  			out = append(out, s)
   137  			return false
   138  		}
   139  		r.Walk(fn)
   140  
   141  		if !reflect.DeepEqual(out, test.out) {
   142  			t.Fatalf("mis-match: %v %v", out, test.out)
   143  		}
   144  	}
   145  }
   146  
   147  func TestLongestPrefix(t *testing.T) {
   148  	r := New()
   149  
   150  	keys := []string{
   151  		"",
   152  		"foo",
   153  		"foobar",
   154  		"foobarbaz",
   155  		"foobarbazzip",
   156  		"foozip",
   157  	}
   158  	for _, k := range keys {
   159  		r.Insert(k, nil)
   160  	}
   161  	if r.Len() != len(keys) {
   162  		t.Fatalf("bad len: %v %v", r.Len(), len(keys))
   163  	}
   164  
   165  	type exp struct {
   166  		inp string
   167  		out string
   168  	}
   169  	cases := []exp{
   170  		{"a", ""},
   171  		{"abc", ""},
   172  		{"fo", ""},
   173  		{"foo", "foo"},
   174  		{"foob", "foo"},
   175  		{"foobar", "foobar"},
   176  		{"foobarba", "foobar"},
   177  		{"foobarbaz", "foobarbaz"},
   178  		{"foobarbazzi", "foobarbaz"},
   179  		{"foobarbazzip", "foobarbazzip"},
   180  		{"foozi", "foo"},
   181  		{"foozip", "foozip"},
   182  		{"foozipzap", "foozip"},
   183  	}
   184  	for _, test := range cases {
   185  		m, _, ok := r.LongestPrefix(test.inp)
   186  		if !ok {
   187  			t.Fatalf("no match: %v", test)
   188  		}
   189  		if m != test.out {
   190  			t.Fatalf("mis-match: %v %v", m, test)
   191  		}
   192  	}
   193  }
   194  
   195  func TestWalkPrefix(t *testing.T) {
   196  	r := New()
   197  
   198  	keys := []string{
   199  		"foobar",
   200  		"foo/bar/baz",
   201  		"foo/baz/bar",
   202  		"foo/zip/zap",
   203  		"zipzap",
   204  	}
   205  	for _, k := range keys {
   206  		r.Insert(k, nil)
   207  	}
   208  	if r.Len() != len(keys) {
   209  		t.Fatalf("bad len: %v %v", r.Len(), len(keys))
   210  	}
   211  
   212  	type exp struct {
   213  		inp string
   214  		out []string
   215  	}
   216  	cases := []exp{
   217  		{
   218  			"f",
   219  			[]string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"},
   220  		},
   221  		{
   222  			"foo",
   223  			[]string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"},
   224  		},
   225  		{
   226  			"foob",
   227  			[]string{"foobar"},
   228  		},
   229  		{
   230  			"foo/",
   231  			[]string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"},
   232  		},
   233  		{
   234  			"foo/b",
   235  			[]string{"foo/bar/baz", "foo/baz/bar"},
   236  		},
   237  		{
   238  			"foo/ba",
   239  			[]string{"foo/bar/baz", "foo/baz/bar"},
   240  		},
   241  		{
   242  			"foo/bar",
   243  			[]string{"foo/bar/baz"},
   244  		},
   245  		{
   246  			"foo/bar/baz",
   247  			[]string{"foo/bar/baz"},
   248  		},
   249  		{
   250  			"foo/bar/bazoo",
   251  			[]string{},
   252  		},
   253  		{
   254  			"z",
   255  			[]string{"zipzap"},
   256  		},
   257  	}
   258  
   259  	for _, test := range cases {
   260  		out := []string{}
   261  		fn := func(s string, v interface{}) bool {
   262  			out = append(out, s)
   263  			return false
   264  		}
   265  		r.WalkPrefix(test.inp, fn)
   266  		sort.Strings(out)
   267  		sort.Strings(test.out)
   268  		if !reflect.DeepEqual(out, test.out) {
   269  			t.Fatalf("mis-match: %v %v", out, test.out)
   270  		}
   271  	}
   272  }
   273  
   274  func TestWalkPath(t *testing.T) {
   275  	r := New()
   276  
   277  	keys := []string{
   278  		"foo",
   279  		"foo/bar",
   280  		"foo/bar/baz",
   281  		"foo/baz/bar",
   282  		"foo/zip/zap",
   283  		"zipzap",
   284  	}
   285  	for _, k := range keys {
   286  		r.Insert(k, nil)
   287  	}
   288  	if r.Len() != len(keys) {
   289  		t.Fatalf("bad len: %v %v", r.Len(), len(keys))
   290  	}
   291  
   292  	type exp struct {
   293  		inp string
   294  		out []string
   295  	}
   296  	cases := []exp{
   297  		{
   298  			"f",
   299  			[]string{},
   300  		},
   301  		{
   302  			"foo",
   303  			[]string{"foo"},
   304  		},
   305  		{
   306  			"foo/",
   307  			[]string{"foo"},
   308  		},
   309  		{
   310  			"foo/ba",
   311  			[]string{"foo"},
   312  		},
   313  		{
   314  			"foo/bar",
   315  			[]string{"foo", "foo/bar"},
   316  		},
   317  		{
   318  			"foo/bar/baz",
   319  			[]string{"foo", "foo/bar", "foo/bar/baz"},
   320  		},
   321  		{
   322  			"foo/bar/bazoo",
   323  			[]string{"foo", "foo/bar", "foo/bar/baz"},
   324  		},
   325  		{
   326  			"z",
   327  			[]string{},
   328  		},
   329  	}
   330  
   331  	for _, test := range cases {
   332  		out := []string{}
   333  		fn := func(s string, v interface{}) bool {
   334  			out = append(out, s)
   335  			return false
   336  		}
   337  		r.WalkPath(test.inp, fn)
   338  		sort.Strings(out)
   339  		sort.Strings(test.out)
   340  		if !reflect.DeepEqual(out, test.out) {
   341  			t.Fatalf("mis-match: %v %v", out, test.out)
   342  		}
   343  	}
   344  }
   345  
   346  // generateUUID is used to generate a random UUID
   347  func generateUUID() string {
   348  	buf := make([]byte, 16)
   349  	if _, err := crand.Read(buf); err != nil {
   350  		panic(fmt.Errorf("failed to read random bytes: %v", err))
   351  	}
   352  
   353  	return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
   354  		buf[0:4],
   355  		buf[4:6],
   356  		buf[6:8],
   357  		buf[8:10],
   358  		buf[10:16])
   359  }