github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/avl/node_test.gno (about)

     1  package avl
     2  
     3  import (
     4  	"sort"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  func TestTraverseByOffset(t *testing.T) {
    10  	const testStrings = `Alfa
    11  Alfred
    12  Alpha
    13  Alphabet
    14  Beta
    15  Beth
    16  Book
    17  Browser`
    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  
    82  func 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  
   137  func 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  
   204  func 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  
   285  func 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  
   346  func 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  
   429  func 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  
   481  func 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  
   531  func 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  
   543  func maxint8(a, b int8) int8 {
   544  	if a > b {
   545  		return a
   546  	}
   547  	return b
   548  }
   549  
   550  func 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  }