node_test.gno

16.09 Kb ยท 795 lines
  1package cow
  2
  3import (
  4	"fmt"
  5	"sort"
  6	"strings"
  7	"testing"
  8)
  9
 10func TestTraverseByOffset(t *testing.T) {
 11	const testStrings = `Alfa
 12Alfred
 13Alpha
 14Alphabet
 15Beta
 16Beth
 17Book
 18Browser`
 19	tt := []struct {
 20		name string
 21		desc bool
 22	}{
 23		{"ascending", false},
 24		{"descending", true},
 25	}
 26
 27	for _, tt := range tt {
 28		t.Run(tt.name, func(t *testing.T) {
 29			sl := strings.Split(testStrings, "\n")
 30
 31			// sort a first time in the order opposite to how we'll be traversing
 32			// the tree, to ensure that we are not just iterating through with
 33			// insertion order.
 34			sort.Strings(sl)
 35			if !tt.desc {
 36				reverseSlice(sl)
 37			}
 38
 39			r := NewNode(sl[0], nil)
 40			for _, v := range sl[1:] {
 41				r, _ = r.Set(v, nil)
 42			}
 43
 44			// then sort sl in the order we'll be traversing it, so that we can
 45			// compare the result with sl.
 46			reverseSlice(sl)
 47
 48			var result []string
 49			for i := 0; i < len(sl); i++ {
 50				r.TraverseByOffset(i, 1, tt.desc, true, func(n *Node) bool {
 51					result = append(result, n.Key())
 52					return false
 53				})
 54			}
 55
 56			if !slicesEqual(sl, result) {
 57				t.Errorf("want %v got %v", sl, result)
 58			}
 59
 60			for l := 2; l <= len(sl); l++ {
 61				// "slices"
 62				for i := 0; i <= len(sl); i++ {
 63					max := i + l
 64					if max > len(sl) {
 65						max = len(sl)
 66					}
 67					exp := sl[i:max]
 68					actual := []string{}
 69
 70					r.TraverseByOffset(i, l, tt.desc, true, func(tr *Node) bool {
 71						actual = append(actual, tr.Key())
 72						return false
 73					})
 74					if !slicesEqual(exp, actual) {
 75						t.Errorf("want %v got %v", exp, actual)
 76					}
 77				}
 78			}
 79		})
 80	}
 81}
 82
 83func TestHas(t *testing.T) {
 84	tests := []struct {
 85		name     string
 86		input    []string
 87		hasKey   string
 88		expected bool
 89	}{
 90		{
 91			"has key in non-empty tree",
 92			[]string{"C", "A", "B", "E", "D"},
 93			"B",
 94			true,
 95		},
 96		{
 97			"does not have key in non-empty tree",
 98			[]string{"C", "A", "B", "E", "D"},
 99			"F",
100			false,
101		},
102		{
103			"has key in single-node tree",
104			[]string{"A"},
105			"A",
106			true,
107		},
108		{
109			"does not have key in single-node tree",
110			[]string{"A"},
111			"B",
112			false,
113		},
114		{
115			"does not have key in empty tree",
116			[]string{},
117			"A",
118			false,
119		},
120	}
121
122	for _, tt := range tests {
123		t.Run(tt.name, func(t *testing.T) {
124			var tree *Node
125			for _, key := range tt.input {
126				tree, _ = tree.Set(key, nil)
127			}
128
129			result := tree.Has(tt.hasKey)
130
131			if result != tt.expected {
132				t.Errorf("Expected %v, got %v", tt.expected, result)
133			}
134		})
135	}
136}
137
138func TestGet(t *testing.T) {
139	tests := []struct {
140		name         string
141		input        []string
142		getKey       string
143		expectIdx    int
144		expectVal    any
145		expectExists bool
146	}{
147		{
148			"get existing key",
149			[]string{"C", "A", "B", "E", "D"},
150			"B",
151			1,
152			nil,
153			true,
154		},
155		{
156			"get non-existent key (smaller)",
157			[]string{"C", "A", "B", "E", "D"},
158			"@",
159			0,
160			nil,
161			false,
162		},
163		{
164			"get non-existent key (larger)",
165			[]string{"C", "A", "B", "E", "D"},
166			"F",
167			5,
168			nil,
169			false,
170		},
171		{
172			"get from empty tree",
173			[]string{},
174			"A",
175			0,
176			nil,
177			false,
178		},
179	}
180
181	for _, tt := range tests {
182		t.Run(tt.name, func(t *testing.T) {
183			var tree *Node
184			for _, key := range tt.input {
185				tree, _ = tree.Set(key, nil)
186			}
187
188			idx, val, exists := tree.Get(tt.getKey)
189
190			if idx != tt.expectIdx {
191				t.Errorf("Expected index %d, got %d", tt.expectIdx, idx)
192			}
193
194			if val != tt.expectVal {
195				t.Errorf("Expected value %v, got %v", tt.expectVal, val)
196			}
197
198			if exists != tt.expectExists {
199				t.Errorf("Expected exists %t, got %t", tt.expectExists, exists)
200			}
201		})
202	}
203}
204
205func TestGetByIndex(t *testing.T) {
206	tests := []struct {
207		name        string
208		input       []string
209		idx         int
210		expectKey   string
211		expectVal   any
212		expectPanic bool
213	}{
214		{
215			"get by valid index",
216			[]string{"C", "A", "B", "E", "D"},
217			2,
218			"C",
219			nil,
220			false,
221		},
222		{
223			"get by valid index (smallest)",
224			[]string{"C", "A", "B", "E", "D"},
225			0,
226			"A",
227			nil,
228			false,
229		},
230		{
231			"get by valid index (largest)",
232			[]string{"C", "A", "B", "E", "D"},
233			4,
234			"E",
235			nil,
236			false,
237		},
238		{
239			"get by invalid index (negative)",
240			[]string{"C", "A", "B", "E", "D"},
241			-1,
242			"",
243			nil,
244			true,
245		},
246		{
247			"get by invalid index (out of range)",
248			[]string{"C", "A", "B", "E", "D"},
249			5,
250			"",
251			nil,
252			true,
253		},
254	}
255
256	for _, tt := range tests {
257		t.Run(tt.name, func(t *testing.T) {
258			var tree *Node
259			for _, key := range tt.input {
260				tree, _ = tree.Set(key, nil)
261			}
262
263			if tt.expectPanic {
264				defer func() {
265					if r := recover(); r == nil {
266						t.Errorf("Expected a panic but didn't get one")
267					}
268				}()
269			}
270
271			key, val := tree.GetByIndex(tt.idx)
272
273			if !tt.expectPanic {
274				if key != tt.expectKey {
275					t.Errorf("Expected key %s, got %s", tt.expectKey, key)
276				}
277
278				if val != tt.expectVal {
279					t.Errorf("Expected value %v, got %v", tt.expectVal, val)
280				}
281			}
282		})
283	}
284}
285
286func TestRemove(t *testing.T) {
287	tests := []struct {
288		name      string
289		input     []string
290		removeKey string
291		expected  []string
292	}{
293		{
294			"remove leaf node",
295			[]string{"C", "A", "B", "D"},
296			"B",
297			[]string{"A", "C", "D"},
298		},
299		{
300			"remove node with one child",
301			[]string{"C", "A", "B", "D"},
302			"A",
303			[]string{"B", "C", "D"},
304		},
305		{
306			"remove node with two children",
307			[]string{"C", "A", "B", "E", "D"},
308			"C",
309			[]string{"A", "B", "D", "E"},
310		},
311		{
312			"remove root node",
313			[]string{"C", "A", "B", "E", "D"},
314			"C",
315			[]string{"A", "B", "D", "E"},
316		},
317		{
318			"remove non-existent key",
319			[]string{"C", "A", "B", "E", "D"},
320			"F",
321			[]string{"A", "B", "C", "D", "E"},
322		},
323	}
324
325	for _, tt := range tests {
326		t.Run(tt.name, func(t *testing.T) {
327			var tree *Node
328			for _, key := range tt.input {
329				tree, _ = tree.Set(key, nil)
330			}
331
332			tree, _, _, _ = tree.Remove(tt.removeKey)
333
334			result := make([]string, 0)
335			tree.Iterate("", "", func(n *Node) bool {
336				result = append(result, n.Key())
337				return false
338			})
339
340			if !slicesEqual(tt.expected, result) {
341				t.Errorf("want %v got %v", tt.expected, result)
342			}
343		})
344	}
345}
346
347func TestTraverse(t *testing.T) {
348	tests := []struct {
349		name     string
350		input    []string
351		expected []string
352	}{
353		{
354			"empty tree",
355			[]string{},
356			[]string{},
357		},
358		{
359			"single node tree",
360			[]string{"A"},
361			[]string{"A"},
362		},
363		{
364			"small tree",
365			[]string{"C", "A", "B", "E", "D"},
366			[]string{"A", "B", "C", "D", "E"},
367		},
368		{
369			"large tree",
370			[]string{"H", "D", "L", "B", "F", "J", "N", "A", "C", "E", "G", "I", "K", "M", "O"},
371			[]string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"},
372		},
373	}
374
375	for _, tt := range tests {
376		t.Run(tt.name, func(t *testing.T) {
377			var tree *Node
378			for _, key := range tt.input {
379				tree, _ = tree.Set(key, nil)
380			}
381
382			t.Run("iterate", func(t *testing.T) {
383				var result []string
384				tree.Iterate("", "", func(n *Node) bool {
385					result = append(result, n.Key())
386					return false
387				})
388				if !slicesEqual(tt.expected, result) {
389					t.Errorf("want %v got %v", tt.expected, result)
390				}
391			})
392
393			t.Run("ReverseIterate", func(t *testing.T) {
394				var result []string
395				tree.ReverseIterate("", "", func(n *Node) bool {
396					result = append(result, n.Key())
397					return false
398				})
399				expected := make([]string, len(tt.expected))
400				copy(expected, tt.expected)
401				for i, j := 0, len(expected)-1; i < j; i, j = i+1, j-1 {
402					expected[i], expected[j] = expected[j], expected[i]
403				}
404				if !slicesEqual(expected, result) {
405					t.Errorf("want %v got %v", expected, result)
406				}
407			})
408
409			t.Run("TraverseInRange", func(t *testing.T) {
410				var result []string
411				start, end := "C", "M"
412				tree.TraverseInRange(start, end, true, true, func(n *Node) bool {
413					result = append(result, n.Key())
414					return false
415				})
416				expected := make([]string, 0)
417				for _, key := range tt.expected {
418					if key >= start && key < end {
419						expected = append(expected, key)
420					}
421				}
422				if !slicesEqual(expected, result) {
423					t.Errorf("want %v got %v", expected, result)
424				}
425			})
426		})
427	}
428}
429
430func TestRotateWhenHeightDiffers(t *testing.T) {
431	tests := []struct {
432		name     string
433		input    []string
434		expected []string
435	}{
436		{
437			"right rotation when left subtree is higher",
438			[]string{"E", "C", "A", "B", "D"},
439			[]string{"A", "B", "C", "E", "D"},
440		},
441		{
442			"left rotation when right subtree is higher",
443			[]string{"A", "C", "E", "D", "F"},
444			[]string{"A", "C", "D", "E", "F"},
445		},
446		{
447			"left-right rotation",
448			[]string{"E", "A", "C", "B", "D"},
449			[]string{"A", "B", "C", "E", "D"},
450		},
451		{
452			"right-left rotation",
453			[]string{"A", "E", "C", "B", "D"},
454			[]string{"A", "B", "C", "E", "D"},
455		},
456	}
457
458	for _, tt := range tests {
459		t.Run(tt.name, func(t *testing.T) {
460			var tree *Node
461			for _, key := range tt.input {
462				tree, _ = tree.Set(key, nil)
463			}
464
465			// perform rotation or balance
466			tree = tree.balance()
467
468			// check tree structure
469			var result []string
470			tree.Iterate("", "", func(n *Node) bool {
471				result = append(result, n.Key())
472				return false
473			})
474
475			if !slicesEqual(tt.expected, result) {
476				t.Errorf("want %v got %v", tt.expected, result)
477			}
478		})
479	}
480}
481
482func TestRotateAndBalance(t *testing.T) {
483	tests := []struct {
484		name     string
485		input    []string
486		expected []string
487	}{
488		{
489			"right rotation",
490			[]string{"A", "B", "C", "D", "E"},
491			[]string{"A", "B", "C", "D", "E"},
492		},
493		{
494			"left rotation",
495			[]string{"E", "D", "C", "B", "A"},
496			[]string{"A", "B", "C", "D", "E"},
497		},
498		{
499			"left-right rotation",
500			[]string{"C", "A", "E", "B", "D"},
501			[]string{"A", "B", "C", "D", "E"},
502		},
503		{
504			"right-left rotation",
505			[]string{"C", "E", "A", "D", "B"},
506			[]string{"A", "B", "C", "D", "E"},
507		},
508	}
509
510	for _, tt := range tests {
511		t.Run(tt.name, func(t *testing.T) {
512			var tree *Node
513			for _, key := range tt.input {
514				tree, _ = tree.Set(key, nil)
515			}
516
517			tree = tree.balance()
518
519			var result []string
520			tree.Iterate("", "", func(n *Node) bool {
521				result = append(result, n.Key())
522				return false
523			})
524
525			if !slicesEqual(tt.expected, result) {
526				t.Errorf("want %v got %v", tt.expected, result)
527			}
528		})
529	}
530}
531
532func slicesEqual(w1, w2 []string) bool {
533	if len(w1) != len(w2) {
534		return false
535	}
536	for i := 0; i < len(w1); i++ {
537		if w1[0] != w2[0] {
538			return false
539		}
540	}
541	return true
542}
543
544func maxint8(a, b int8) int8 {
545	if a > b {
546		return a
547	}
548	return b
549}
550
551func reverseSlice(ss []string) {
552	for i := 0; i < len(ss)/2; i++ {
553		j := len(ss) - 1 - i
554		ss[i], ss[j] = ss[j], ss[i]
555	}
556}
557
558func TestNodeStructuralSharing(t *testing.T) {
559	t.Run("unmodified paths remain shared", func(t *testing.T) {
560		root := NewNode("B", 2)
561		root, _ = root.Set("A", 1)
562		root, _ = root.Set("C", 3)
563
564		originalRight := root.rightNode
565		newRoot, _ := root.Set("A", 10)
566
567		if newRoot.rightNode != originalRight {
568			t.Error("Unmodified right subtree should remain shared")
569		}
570	})
571
572	t.Run("multiple modifications reuse shared structure", func(t *testing.T) {
573		// Create initial tree
574		root := NewNode("B", 2)
575		root, _ = root.Set("A", 1)
576		root, _ = root.Set("C", 3)
577
578		// Store original nodes
579		originalRight := root.rightNode
580
581		// First modification
582		mod1, _ := root.Set("A", 10)
583
584		// Second modification
585		mod2, _ := mod1.Set("C", 30)
586
587		// Check sharing in first modification
588		if mod1.rightNode != originalRight {
589			t.Error("First modification should share unmodified right subtree")
590		}
591
592		// Check that second modification creates new right node
593		if mod2.rightNode == originalRight {
594			t.Error("Second modification should create new right node")
595		}
596	})
597}
598
599func TestNodeCopyOnWrite(t *testing.T) {
600	t.Run("copy preserves structure", func(t *testing.T) {
601		root := NewNode("B", 2)
602		root, _ = root.Set("A", 1)
603		root, _ = root.Set("C", 3)
604
605		// Only copy non-leaf nodes
606		if !root.IsLeaf() {
607			copied := root._copy()
608			if copied == root {
609				t.Error("Copy should create new instance")
610			}
611
612			// Create temporary trees to use Equal method
613			original := &Tree{node: root}
614			copiedTree := &Tree{node: copied}
615			if !original.Equal(copiedTree) {
616				t.Error("Copied node should preserve structure")
617			}
618		}
619	})
620
621	t.Run("removal copy pattern", func(t *testing.T) {
622		// Create a more complex tree to test removal
623		root := NewNode("B", 2)
624		root, _ = root.Set("A", 1)
625		root, _ = root.Set("C", 3)
626		root, _ = root.Set("D", 4) // Add this to ensure proper tree structure
627
628		// Store references to original nodes
629		originalRight := root.rightNode
630		originalRightRight := originalRight.rightNode
631
632		// Remove "A" which should only affect the left subtree
633		newRoot, _, _, _ := root.Remove("A")
634
635		// Verify right subtree remains unchanged and shared
636		if newRoot.rightNode != originalRight {
637			t.Error("Right subtree should remain shared during removal of left node")
638		}
639
640		// Also verify deeper nodes remain shared
641		if newRoot.rightNode.rightNode != originalRightRight {
642			t.Error("Deep right subtree should remain shared during removal")
643		}
644
645		// Verify original tree is unchanged
646		if _, _, exists := root.Get("A"); !exists {
647			t.Error("Original tree should remain unchanged")
648		}
649	})
650
651	t.Run("copy leaf node panic", func(t *testing.T) {
652		leaf := NewNode("A", 1)
653
654		defer func() {
655			if r := recover(); r == nil {
656				t.Error("Expected panic when copying leaf node")
657			}
658		}()
659
660		// This should panic with our specific message
661		leaf._copy()
662	})
663}
664
665func TestNodeEqual(t *testing.T) {
666	tests := []struct {
667		name     string
668		node1    func() *Node
669		node2    func() *Node
670		expected bool
671	}{
672		{
673			name:     "nil nodes",
674			node1:    func() *Node { return nil },
675			node2:    func() *Node { return nil },
676			expected: true,
677		},
678		{
679			name:     "one nil node",
680			node1:    func() *Node { return NewNode("A", 1) },
681			node2:    func() *Node { return nil },
682			expected: false,
683		},
684		{
685			name:     "single leaf nodes equal",
686			node1:    func() *Node { return NewNode("A", 1) },
687			node2:    func() *Node { return NewNode("A", 1) },
688			expected: true,
689		},
690		{
691			name:     "single leaf nodes different key",
692			node1:    func() *Node { return NewNode("A", 1) },
693			node2:    func() *Node { return NewNode("B", 1) },
694			expected: false,
695		},
696		{
697			name:     "single leaf nodes different value",
698			node1:    func() *Node { return NewNode("A", 1) },
699			node2:    func() *Node { return NewNode("A", 2) },
700			expected: false,
701		},
702		{
703			name: "complex trees equal",
704			node1: func() *Node {
705				n, _ := NewNode("B", 2).Set("A", 1)
706				n, _ = n.Set("C", 3)
707				return n
708			},
709			node2: func() *Node {
710				n, _ := NewNode("B", 2).Set("A", 1)
711				n, _ = n.Set("C", 3)
712				return n
713			},
714			expected: true,
715		},
716		{
717			name: "complex trees different structure",
718			node1: func() *Node {
719				// Create a tree with structure:
720				//     B
721				//    / \
722				//   A   D
723				n := NewNode("B", 2)
724				n, _ = n.Set("A", 1)
725				n, _ = n.Set("D", 4)
726				return n
727			},
728			node2: func() *Node {
729				// Create a tree with structure:
730				//     C
731				//    / \
732				//   A   D
733				n := NewNode("C", 3)
734				n, _ = n.Set("A", 1)
735				n, _ = n.Set("D", 4)
736				return n
737			},
738			expected: false, // These trees should be different
739		},
740		{
741			name: "complex trees same structure despite different insertion order",
742			node1: func() *Node {
743				n, _ := NewNode("B", 2).Set("A", 1)
744				n, _ = n.Set("C", 3)
745				return n
746			},
747			node2: func() *Node {
748				n, _ := NewNode("A", 1).Set("B", 2)
749				n, _ = n.Set("C", 3)
750				return n
751			},
752			expected: true,
753		},
754		{
755			name: "truly different structures",
756			node1: func() *Node {
757				n, _ := NewNode("B", 2).Set("A", 1)
758				return n // Tree with just two nodes
759			},
760			node2: func() *Node {
761				n, _ := NewNode("B", 2).Set("C", 3)
762				return n // Different two-node tree
763			},
764			expected: false,
765		},
766	}
767
768	for _, tt := range tests {
769		t.Run(tt.name, func(t *testing.T) {
770			node1 := tt.node1()
771			node2 := tt.node2()
772			result := node1.Equal(node2)
773			if result != tt.expected {
774				t.Errorf("Expected Equal to return %v for %s", tt.expected, tt.name)
775				println("\nComparison failed:")
776				println("Tree 1:")
777				printTree(node1, 0)
778				println("Tree 2:")
779				printTree(node2, 0)
780			}
781		})
782	}
783}
784
785// Helper function to print tree structure
786func printTree(node *Node, level int) {
787	if node == nil {
788		return
789	}
790	indent := strings.Repeat("  ", level)
791	println(fmt.Sprintf("%sKey: %s, Value: %v, Height: %d, Size: %d",
792		indent, node.key, node.value, node.height, node.size))
793	printTree(node.leftNode, level+1)
794	printTree(node.rightNode, level+1)
795}