github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/snowman/bootstrap/interval/tree_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package interval
     5  
     6  import (
     7  	"math"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/database"
    13  	"github.com/MetalBlockchain/metalgo/database/memdb"
    14  )
    15  
    16  func newTree(require *require.Assertions, db database.Database, intervals []*Interval) *Tree {
    17  	tree, err := NewTree(db)
    18  	require.NoError(err)
    19  
    20  	for _, toAdd := range intervals {
    21  		for i := toAdd.LowerBound; i <= toAdd.UpperBound; i++ {
    22  			require.NoError(tree.Add(db, i))
    23  		}
    24  	}
    25  	return tree
    26  }
    27  
    28  func TestTreeAdd(t *testing.T) {
    29  	tests := []struct {
    30  		name        string
    31  		toAdd       []*Interval
    32  		expected    []*Interval
    33  		expectedLen uint64
    34  	}{
    35  		{
    36  			name: "single addition",
    37  			toAdd: []*Interval{
    38  				{
    39  					LowerBound: 10,
    40  					UpperBound: 10,
    41  				},
    42  			},
    43  			expected: []*Interval{
    44  				{
    45  					LowerBound: 10,
    46  					UpperBound: 10,
    47  				},
    48  			},
    49  			expectedLen: 1,
    50  		},
    51  		{
    52  			name: "extend above",
    53  			toAdd: []*Interval{
    54  				{
    55  					LowerBound: 10,
    56  					UpperBound: 11,
    57  				},
    58  			},
    59  			expected: []*Interval{
    60  				{
    61  					LowerBound: 10,
    62  					UpperBound: 11,
    63  				},
    64  			},
    65  			expectedLen: 2,
    66  		},
    67  		{
    68  			name: "extend below",
    69  			toAdd: []*Interval{
    70  				{
    71  					LowerBound: 11,
    72  					UpperBound: 11,
    73  				},
    74  				{
    75  					LowerBound: 10,
    76  					UpperBound: 10,
    77  				},
    78  			},
    79  			expected: []*Interval{
    80  				{
    81  					LowerBound: 10,
    82  					UpperBound: 11,
    83  				},
    84  			},
    85  			expectedLen: 2,
    86  		},
    87  		{
    88  			name: "merge",
    89  			toAdd: []*Interval{
    90  				{
    91  					LowerBound: 10,
    92  					UpperBound: 10,
    93  				},
    94  				{
    95  					LowerBound: 12,
    96  					UpperBound: 12,
    97  				},
    98  				{
    99  					LowerBound: 11,
   100  					UpperBound: 11,
   101  				},
   102  			},
   103  			expected: []*Interval{
   104  				{
   105  					LowerBound: 10,
   106  					UpperBound: 12,
   107  				},
   108  			},
   109  			expectedLen: 3,
   110  		},
   111  		{
   112  			name: "ignore duplicate",
   113  			toAdd: []*Interval{
   114  				{
   115  					LowerBound: 10,
   116  					UpperBound: 11,
   117  				},
   118  				{
   119  					LowerBound: 11,
   120  					UpperBound: 11,
   121  				},
   122  			},
   123  			expected: []*Interval{
   124  				{
   125  					LowerBound: 10,
   126  					UpperBound: 11,
   127  				},
   128  			},
   129  			expectedLen: 2,
   130  		},
   131  	}
   132  	for _, test := range tests {
   133  		t.Run(test.name, func(t *testing.T) {
   134  			require := require.New(t)
   135  
   136  			db := memdb.New()
   137  			treeFromAdditions := newTree(require, db, test.toAdd)
   138  			require.Equal(test.expected, treeFromAdditions.Flatten())
   139  			require.Equal(test.expectedLen, treeFromAdditions.Len())
   140  
   141  			treeFromDB := newTree(require, db, nil)
   142  			require.Equal(test.expected, treeFromDB.Flatten())
   143  			require.Equal(test.expectedLen, treeFromDB.Len())
   144  		})
   145  	}
   146  }
   147  
   148  func TestTreeRemove(t *testing.T) {
   149  	tests := []struct {
   150  		name        string
   151  		toAdd       []*Interval
   152  		toRemove    []*Interval
   153  		expected    []*Interval
   154  		expectedLen uint64
   155  	}{
   156  		{
   157  			name: "single removal",
   158  			toAdd: []*Interval{
   159  				{
   160  					LowerBound: 10,
   161  					UpperBound: 10,
   162  				},
   163  			},
   164  			toRemove: []*Interval{
   165  				{
   166  					LowerBound: 10,
   167  					UpperBound: 10,
   168  				},
   169  			},
   170  			expected:    []*Interval{},
   171  			expectedLen: 0,
   172  		},
   173  		{
   174  			name: "reduce above",
   175  			toAdd: []*Interval{
   176  				{
   177  					LowerBound: 10,
   178  					UpperBound: 11,
   179  				},
   180  			},
   181  			toRemove: []*Interval{
   182  				{
   183  					LowerBound: 11,
   184  					UpperBound: 11,
   185  				},
   186  			},
   187  			expected: []*Interval{
   188  				{
   189  					LowerBound: 10,
   190  					UpperBound: 10,
   191  				},
   192  			},
   193  			expectedLen: 1,
   194  		},
   195  		{
   196  			name: "reduce below",
   197  			toAdd: []*Interval{
   198  				{
   199  					LowerBound: 10,
   200  					UpperBound: 11,
   201  				},
   202  			},
   203  			toRemove: []*Interval{
   204  				{
   205  					LowerBound: 10,
   206  					UpperBound: 10,
   207  				},
   208  			},
   209  			expected: []*Interval{
   210  				{
   211  					LowerBound: 11,
   212  					UpperBound: 11,
   213  				},
   214  			},
   215  			expectedLen: 1,
   216  		},
   217  		{
   218  			name: "split",
   219  			toAdd: []*Interval{
   220  				{
   221  					LowerBound: 10,
   222  					UpperBound: 12,
   223  				},
   224  			},
   225  			toRemove: []*Interval{
   226  				{
   227  					LowerBound: 11,
   228  					UpperBound: 11,
   229  				},
   230  			},
   231  			expected: []*Interval{
   232  				{
   233  					LowerBound: 10,
   234  					UpperBound: 10,
   235  				},
   236  				{
   237  					LowerBound: 12,
   238  					UpperBound: 12,
   239  				},
   240  			},
   241  			expectedLen: 2,
   242  		},
   243  		{
   244  			name: "ignore missing",
   245  			toAdd: []*Interval{
   246  				{
   247  					LowerBound: 10,
   248  					UpperBound: 10,
   249  				},
   250  			},
   251  			toRemove: []*Interval{
   252  				{
   253  					LowerBound: 11,
   254  					UpperBound: 11,
   255  				},
   256  			},
   257  			expected: []*Interval{
   258  				{
   259  					LowerBound: 10,
   260  					UpperBound: 10,
   261  				},
   262  			},
   263  			expectedLen: 1,
   264  		},
   265  	}
   266  	for _, test := range tests {
   267  		t.Run(test.name, func(t *testing.T) {
   268  			require := require.New(t)
   269  
   270  			db := memdb.New()
   271  			treeFromModifications := newTree(require, db, test.toAdd)
   272  			for _, toRemove := range test.toRemove {
   273  				for i := toRemove.LowerBound; i <= toRemove.UpperBound; i++ {
   274  					require.NoError(treeFromModifications.Remove(db, i))
   275  				}
   276  			}
   277  			require.Equal(test.expected, treeFromModifications.Flatten())
   278  			require.Equal(test.expectedLen, treeFromModifications.Len())
   279  
   280  			treeFromDB := newTree(require, db, nil)
   281  			require.Equal(test.expected, treeFromDB.Flatten())
   282  			require.Equal(test.expectedLen, treeFromDB.Len())
   283  		})
   284  	}
   285  }
   286  
   287  func TestTreeContains(t *testing.T) {
   288  	tests := []struct {
   289  		name     string
   290  		tree     []*Interval
   291  		height   uint64
   292  		expected bool
   293  	}{
   294  		{
   295  			name: "below",
   296  			tree: []*Interval{
   297  				{
   298  					LowerBound: 10,
   299  					UpperBound: 10,
   300  				},
   301  			},
   302  			height:   9,
   303  			expected: false,
   304  		},
   305  		{
   306  			name: "above",
   307  			tree: []*Interval{
   308  				{
   309  					LowerBound: 10,
   310  					UpperBound: 10,
   311  				},
   312  			},
   313  			height:   11,
   314  			expected: false,
   315  		},
   316  		{
   317  			name: "equal both",
   318  			tree: []*Interval{
   319  				{
   320  					LowerBound: 10,
   321  					UpperBound: 10,
   322  				},
   323  			},
   324  			height:   10,
   325  			expected: true,
   326  		},
   327  		{
   328  			name: "equal lower",
   329  			tree: []*Interval{
   330  				{
   331  					LowerBound: 10,
   332  					UpperBound: 11,
   333  				},
   334  			},
   335  			height:   10,
   336  			expected: true,
   337  		},
   338  		{
   339  			name: "equal upper",
   340  			tree: []*Interval{
   341  				{
   342  					LowerBound: 9,
   343  					UpperBound: 10,
   344  				},
   345  			},
   346  			height:   10,
   347  			expected: true,
   348  		},
   349  		{
   350  			name: "inside",
   351  			tree: []*Interval{
   352  				{
   353  					LowerBound: 9,
   354  					UpperBound: 11,
   355  				},
   356  			},
   357  			height:   10,
   358  			expected: true,
   359  		},
   360  	}
   361  	for _, test := range tests {
   362  		t.Run(test.name, func(t *testing.T) {
   363  			require := require.New(t)
   364  
   365  			tree := newTree(require, memdb.New(), test.tree)
   366  			require.Equal(test.expected, tree.Contains(test.height))
   367  		})
   368  	}
   369  }
   370  
   371  func TestTreeLenOverflow(t *testing.T) {
   372  	require := require.New(t)
   373  
   374  	db := memdb.New()
   375  	require.NoError(PutInterval(db, math.MaxUint64, 0))
   376  
   377  	tree, err := NewTree(db)
   378  	require.NoError(err)
   379  	require.Zero(tree.Len())
   380  	require.True(tree.Contains(0))
   381  	require.True(tree.Contains(math.MaxUint64 / 2))
   382  	require.True(tree.Contains(math.MaxUint64))
   383  
   384  	require.NoError(tree.Remove(db, 5))
   385  	require.Equal(uint64(math.MaxUint64), tree.Len())
   386  
   387  	require.NoError(tree.Add(db, 5))
   388  	require.Zero(tree.Len())
   389  }