Search Apps Documentation Source Content File Folder Download Copy

node_test.gno

10.25 Kb ยท 555 lines
  1package avl
  2
  3import (
  4	"sort"
  5	"strings"
  6	"testing"
  7)
  8
  9func TestTraverseByOffset(t *testing.T) {
 10	const testStrings = `Alfa
 11Alfred
 12Alpha
 13Alphabet
 14Beta
 15Beth
 16Book
 17Browser`
 18	tt := []struct {
 19		name string
 20		desc bool
 21	}{
 22		{"ascending", false},
 23		{"descending", true},
 24	}
 25
 26	for _, tt := range tt {
 27		t.Run(tt.name, func(t *testing.T) {
 28			sl := strings.Split(testStrings, "\n")
 29
 30			// sort a first time in the order opposite to how we'll be traversing
 31			// the tree, to ensure that we are not just iterating through with
 32			// insertion order.
 33			sort.Strings(sl)
 34			if !tt.desc {
 35				reverseSlice(sl)
 36			}
 37
 38			r := NewNode(sl[0], nil)
 39			for _, v := range sl[1:] {
 40				r, _ = r.Set(v, nil)
 41			}
 42
 43			// then sort sl in the order we'll be traversing it, so that we can
 44			// compare the result with sl.
 45			reverseSlice(sl)
 46
 47			var result []string
 48			for i := 0; i < len(sl); i++ {
 49				r.TraverseByOffset(i, 1, tt.desc, true, func(n *Node) bool {
 50					result = append(result, n.Key())
 51					return false
 52				})
 53			}
 54
 55			if !slicesEqual(sl, result) {
 56				t.Errorf("want %v got %v", sl, result)
 57			}
 58
 59			for l := 2; l <= len(sl); l++ {
 60				// "slices"
 61				for i := 0; i <= len(sl); i++ {
 62					max := i + l
 63					if max > len(sl) {
 64						max = len(sl)
 65					}
 66					exp := sl[i:max]
 67					actual := []string{}
 68
 69					r.TraverseByOffset(i, l, tt.desc, true, func(tr *Node) bool {
 70						actual = append(actual, tr.Key())
 71						return false
 72					})
 73					if !slicesEqual(exp, actual) {
 74						t.Errorf("want %v got %v", exp, actual)
 75					}
 76				}
 77			}
 78		})
 79	}
 80}
 81
 82func TestHas(t *testing.T) {
 83	tests := []struct {
 84		name     string
 85		input    []string
 86		hasKey   string
 87		expected bool
 88	}{
 89		{
 90			"has key in non-empty tree",
 91			[]string{"C", "A", "B", "E", "D"},
 92			"B",
 93			true,
 94		},
 95		{
 96			"does not have key in non-empty tree",
 97			[]string{"C", "A", "B", "E", "D"},
 98			"F",
 99			false,
100		},
101		{
102			"has key in single-node tree",
103			[]string{"A"},
104			"A",
105			true,
106		},
107		{
108			"does not have key in single-node tree",
109			[]string{"A"},
110			"B",
111			false,
112		},
113		{
114			"does not have key in empty tree",
115			[]string{},
116			"A",
117			false,
118		},
119	}
120
121	for _, tt := range tests {
122		t.Run(tt.name, func(t *testing.T) {
123			var tree *Node
124			for _, key := range tt.input {
125				tree, _ = tree.Set(key, nil)
126			}
127
128			result := tree.Has(tt.hasKey)
129
130			if result != tt.expected {
131				t.Errorf("Expected %v, got %v", tt.expected, result)
132			}
133		})
134	}
135}
136
137func TestGet(t *testing.T) {
138	tests := []struct {
139		name         string
140		input        []string
141		getKey       string
142		expectIdx    int
143		expectVal    interface{}
144		expectExists bool
145	}{
146		{
147			"get existing key",
148			[]string{"C", "A", "B", "E", "D"},
149			"B",
150			1,
151			nil,
152			true,
153		},
154		{
155			"get non-existent key (smaller)",
156			[]string{"C", "A", "B", "E", "D"},
157			"@",
158			0,
159			nil,
160			false,
161		},
162		{
163			"get non-existent key (larger)",
164			[]string{"C", "A", "B", "E", "D"},
165			"F",
166			5,
167			nil,
168			false,
169		},
170		{
171			"get from empty tree",
172			[]string{},
173			"A",
174			0,
175			nil,
176			false,
177		},
178	}
179
180	for _, tt := range tests {
181		t.Run(tt.name, func(t *testing.T) {
182			var tree *Node
183			for _, key := range tt.input {
184				tree, _ = tree.Set(key, nil)
185			}
186
187			idx, val, exists := tree.Get(tt.getKey)
188
189			if idx != tt.expectIdx {
190				t.Errorf("Expected index %d, got %d", tt.expectIdx, idx)
191			}
192
193			if val != tt.expectVal {
194				t.Errorf("Expected value %v, got %v", tt.expectVal, val)
195			}
196
197			if exists != tt.expectExists {
198				t.Errorf("Expected exists %t, got %t", tt.expectExists, exists)
199			}
200		})
201	}
202}
203
204func TestGetByIndex(t *testing.T) {
205	tests := []struct {
206		name        string
207		input       []string
208		idx         int
209		expectKey   string
210		expectVal   interface{}
211		expectPanic bool
212	}{
213		{
214			"get by valid index",
215			[]string{"C", "A", "B", "E", "D"},
216			2,
217			"C",
218			nil,
219			false,
220		},
221		{
222			"get by valid index (smallest)",
223			[]string{"C", "A", "B", "E", "D"},
224			0,
225			"A",
226			nil,
227			false,
228		},
229		{
230			"get by valid index (largest)",
231			[]string{"C", "A", "B", "E", "D"},
232			4,
233			"E",
234			nil,
235			false,
236		},
237		{
238			"get by invalid index (negative)",
239			[]string{"C", "A", "B", "E", "D"},
240			-1,
241			"",
242			nil,
243			true,
244		},
245		{
246			"get by invalid index (out of range)",
247			[]string{"C", "A", "B", "E", "D"},
248			5,
249			"",
250			nil,
251			true,
252		},
253	}
254
255	for _, tt := range tests {
256		t.Run(tt.name, func(t *testing.T) {
257			var tree *Node
258			for _, key := range tt.input {
259				tree, _ = tree.Set(key, nil)
260			}
261
262			if tt.expectPanic {
263				defer func() {
264					if r := recover(); r == nil {
265						t.Errorf("Expected a panic but didn't get one")
266					}
267				}()
268			}
269
270			key, val := tree.GetByIndex(tt.idx)
271
272			if !tt.expectPanic {
273				if key != tt.expectKey {
274					t.Errorf("Expected key %s, got %s", tt.expectKey, key)
275				}
276
277				if val != tt.expectVal {
278					t.Errorf("Expected value %v, got %v", tt.expectVal, val)
279				}
280			}
281		})
282	}
283}
284
285func TestRemove(t *testing.T) {
286	tests := []struct {
287		name      string
288		input     []string
289		removeKey string
290		expected  []string
291	}{
292		{
293			"remove leaf node",
294			[]string{"C", "A", "B", "D"},
295			"B",
296			[]string{"A", "C", "D"},
297		},
298		{
299			"remove node with one child",
300			[]string{"C", "A", "B", "D"},
301			"A",
302			[]string{"B", "C", "D"},
303		},
304		{
305			"remove node with two children",
306			[]string{"C", "A", "B", "E", "D"},
307			"C",
308			[]string{"A", "B", "D", "E"},
309		},
310		{
311			"remove root node",
312			[]string{"C", "A", "B", "E", "D"},
313			"C",
314			[]string{"A", "B", "D", "E"},
315		},
316		{
317			"remove non-existent key",
318			[]string{"C", "A", "B", "E", "D"},
319			"F",
320			[]string{"A", "B", "C", "D", "E"},
321		},
322	}
323
324	for _, tt := range tests {
325		t.Run(tt.name, func(t *testing.T) {
326			var tree *Node
327			for _, key := range tt.input {
328				tree, _ = tree.Set(key, nil)
329			}
330
331			tree, _, _, _ = tree.Remove(tt.removeKey)
332
333			result := make([]string, 0)
334			tree.Iterate("", "", func(n *Node) bool {
335				result = append(result, n.Key())
336				return false
337			})
338
339			if !slicesEqual(tt.expected, result) {
340				t.Errorf("want %v got %v", tt.expected, result)
341			}
342		})
343	}
344}
345
346func TestTraverse(t *testing.T) {
347	tests := []struct {
348		name     string
349		input    []string
350		expected []string
351	}{
352		{
353			"empty tree",
354			[]string{},
355			[]string{},
356		},
357		{
358			"single node tree",
359			[]string{"A"},
360			[]string{"A"},
361		},
362		{
363			"small tree",
364			[]string{"C", "A", "B", "E", "D"},
365			[]string{"A", "B", "C", "D", "E"},
366		},
367		{
368			"large tree",
369			[]string{"H", "D", "L", "B", "F", "J", "N", "A", "C", "E", "G", "I", "K", "M", "O"},
370			[]string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"},
371		},
372	}
373
374	for _, tt := range tests {
375		t.Run(tt.name, func(t *testing.T) {
376			var tree *Node
377			for _, key := range tt.input {
378				tree, _ = tree.Set(key, nil)
379			}
380
381			t.Run("iterate", func(t *testing.T) {
382				var result []string
383				tree.Iterate("", "", func(n *Node) bool {
384					result = append(result, n.Key())
385					return false
386				})
387				if !slicesEqual(tt.expected, result) {
388					t.Errorf("want %v got %v", tt.expected, result)
389				}
390			})
391
392			t.Run("ReverseIterate", func(t *testing.T) {
393				var result []string
394				tree.ReverseIterate("", "", func(n *Node) bool {
395					result = append(result, n.Key())
396					return false
397				})
398				expected := make([]string, len(tt.expected))
399				copy(expected, tt.expected)
400				for i, j := 0, len(expected)-1; i < j; i, j = i+1, j-1 {
401					expected[i], expected[j] = expected[j], expected[i]
402				}
403				if !slicesEqual(expected, result) {
404					t.Errorf("want %v got %v", expected, result)
405				}
406			})
407
408			t.Run("TraverseInRange", func(t *testing.T) {
409				var result []string
410				start, end := "C", "M"
411				tree.TraverseInRange(start, end, true, true, func(n *Node) bool {
412					result = append(result, n.Key())
413					return false
414				})
415				expected := make([]string, 0)
416				for _, key := range tt.expected {
417					if key >= start && key < end {
418						expected = append(expected, key)
419					}
420				}
421				if !slicesEqual(expected, result) {
422					t.Errorf("want %v got %v", expected, result)
423				}
424			})
425		})
426	}
427}
428
429func TestRotateWhenHeightDiffers(t *testing.T) {
430	tests := []struct {
431		name     string
432		input    []string
433		expected []string
434	}{
435		{
436			"right rotation when left subtree is higher",
437			[]string{"E", "C", "A", "B", "D"},
438			[]string{"A", "B", "C", "E", "D"},
439		},
440		{
441			"left rotation when right subtree is higher",
442			[]string{"A", "C", "E", "D", "F"},
443			[]string{"A", "C", "D", "E", "F"},
444		},
445		{
446			"left-right rotation",
447			[]string{"E", "A", "C", "B", "D"},
448			[]string{"A", "B", "C", "E", "D"},
449		},
450		{
451			"right-left rotation",
452			[]string{"A", "E", "C", "B", "D"},
453			[]string{"A", "B", "C", "E", "D"},
454		},
455	}
456
457	for _, tt := range tests {
458		t.Run(tt.name, func(t *testing.T) {
459			var tree *Node
460			for _, key := range tt.input {
461				tree, _ = tree.Set(key, nil)
462			}
463
464			// perform rotation or balance
465			tree = tree.balance()
466
467			// check tree structure
468			var result []string
469			tree.Iterate("", "", func(n *Node) bool {
470				result = append(result, n.Key())
471				return false
472			})
473
474			if !slicesEqual(tt.expected, result) {
475				t.Errorf("want %v got %v", tt.expected, result)
476			}
477		})
478	}
479}
480
481func TestRotateAndBalance(t *testing.T) {
482	tests := []struct {
483		name     string
484		input    []string
485		expected []string
486	}{
487		{
488			"right rotation",
489			[]string{"A", "B", "C", "D", "E"},
490			[]string{"A", "B", "C", "D", "E"},
491		},
492		{
493			"left rotation",
494			[]string{"E", "D", "C", "B", "A"},
495			[]string{"A", "B", "C", "D", "E"},
496		},
497		{
498			"left-right rotation",
499			[]string{"C", "A", "E", "B", "D"},
500			[]string{"A", "B", "C", "D", "E"},
501		},
502		{
503			"right-left rotation",
504			[]string{"C", "E", "A", "D", "B"},
505			[]string{"A", "B", "C", "D", "E"},
506		},
507	}
508
509	for _, tt := range tests {
510		t.Run(tt.name, func(t *testing.T) {
511			var tree *Node
512			for _, key := range tt.input {
513				tree, _ = tree.Set(key, nil)
514			}
515
516			tree = tree.balance()
517
518			var result []string
519			tree.Iterate("", "", func(n *Node) bool {
520				result = append(result, n.Key())
521				return false
522			})
523
524			if !slicesEqual(tt.expected, result) {
525				t.Errorf("want %v got %v", tt.expected, result)
526			}
527		})
528	}
529}
530
531func slicesEqual(w1, w2 []string) bool {
532	if len(w1) != len(w2) {
533		return false
534	}
535	for i := 0; i < len(w1); i++ {
536		if w1[0] != w2[0] {
537			return false
538		}
539	}
540	return true
541}
542
543func maxint8(a, b int8) int8 {
544	if a > b {
545		return a
546	}
547	return b
548}
549
550func reverseSlice(ss []string) {
551	for i := 0; i < len(ss)/2; i++ {
552		j := len(ss) - 1 - i
553		ss[i], ss[j] = ss[j], ss[i]
554	}
555}