github.com/lbryio/lbcd@v0.22.119/database/internal/treap/immutable_test.go (about)

     1  // Copyright (c) 2015-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package treap
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/sha256"
    10  	"testing"
    11  )
    12  
    13  // TestImmutableEmpty ensures calling functions on an empty immutable treap
    14  // works as expected.
    15  func TestImmutableEmpty(t *testing.T) {
    16  	t.Parallel()
    17  
    18  	// Ensure the treap length is the expected value.
    19  	testTreap := NewImmutable()
    20  	if gotLen := testTreap.Len(); gotLen != 0 {
    21  		t.Fatalf("Len: unexpected length - got %d, want %d", gotLen, 0)
    22  	}
    23  
    24  	// Ensure the reported size is 0.
    25  	if gotSize := testTreap.Size(); gotSize != 0 {
    26  		t.Fatalf("Size: unexpected byte size - got %d, want 0",
    27  			gotSize)
    28  	}
    29  
    30  	// Ensure there are no errors with requesting keys from an empty treap.
    31  	key := serializeUint32(0)
    32  	if gotVal := testTreap.Has(key); gotVal {
    33  		t.Fatalf("Has: unexpected result - got %v, want false", gotVal)
    34  	}
    35  	if gotVal := testTreap.Get(key); gotVal != nil {
    36  		t.Fatalf("Get: unexpected result - got %x, want nil", gotVal)
    37  	}
    38  
    39  	// Ensure there are no panics when deleting keys from an empty treap.
    40  	testTreap.Delete(key)
    41  
    42  	// Ensure the number of keys iterated by ForEach on an empty treap is
    43  	// zero.
    44  	var numIterated int
    45  	testTreap.ForEach(func(k, v []byte) bool {
    46  		numIterated++
    47  		return true
    48  	})
    49  	if numIterated != 0 {
    50  		t.Fatalf("ForEach: unexpected iterate count - got %d, want 0",
    51  			numIterated)
    52  	}
    53  }
    54  
    55  // TestImmutableSequential ensures that putting keys into an immutable treap in
    56  // sequential order works as expected.
    57  func TestImmutableSequential(t *testing.T) {
    58  	t.Parallel()
    59  
    60  	// Insert a bunch of sequential keys while checking several of the treap
    61  	// functions work as expected.
    62  	expectedSize := uint64(0)
    63  	numItems := 1000
    64  	testTreap := NewImmutable()
    65  	for i := 0; i < numItems; i++ {
    66  		key := serializeUint32(uint32(i))
    67  		testTreap = testTreap.Put(key, key)
    68  
    69  		// Ensure the treap length is the expected value.
    70  		if gotLen := testTreap.Len(); gotLen != i+1 {
    71  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
    72  				i, gotLen, i+1)
    73  		}
    74  
    75  		// Ensure the treap has the key.
    76  		if !testTreap.Has(key) {
    77  			t.Fatalf("Has #%d: key %q is not in treap", i, key)
    78  		}
    79  
    80  		// Get the key from the treap and ensure it is the expected
    81  		// value.
    82  		if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) {
    83  			t.Fatalf("Get #%d: unexpected value - got %x, want %x",
    84  				i, gotVal, key)
    85  		}
    86  
    87  		// Ensure the expected size is reported.
    88  		expectedSize += (nodeFieldsSize + 8)
    89  		if gotSize := testTreap.Size(); gotSize != expectedSize {
    90  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
    91  				"want %d", i, gotSize, expectedSize)
    92  		}
    93  	}
    94  
    95  	// Ensure the all keys are iterated by ForEach in order.
    96  	var numIterated int
    97  	testTreap.ForEach(func(k, v []byte) bool {
    98  		wantKey := serializeUint32(uint32(numIterated))
    99  
   100  		// Ensure the key is as expected.
   101  		if !bytes.Equal(k, wantKey) {
   102  			t.Fatalf("ForEach #%d: unexpected key - got %x, want %x",
   103  				numIterated, k, wantKey)
   104  		}
   105  
   106  		// Ensure the value is as expected.
   107  		if !bytes.Equal(v, wantKey) {
   108  			t.Fatalf("ForEach #%d: unexpected value - got %x, want %x",
   109  				numIterated, v, wantKey)
   110  		}
   111  
   112  		numIterated++
   113  		return true
   114  	})
   115  
   116  	// Ensure all items were iterated.
   117  	if numIterated != numItems {
   118  		t.Fatalf("ForEach: unexpected iterate count - got %d, want %d",
   119  			numIterated, numItems)
   120  	}
   121  
   122  	// Delete the keys one-by-one while checking several of the treap
   123  	// functions work as expected.
   124  	for i := 0; i < numItems; i++ {
   125  		key := serializeUint32(uint32(i))
   126  		testTreap = testTreap.Delete(key)
   127  
   128  		// Ensure the treap length is the expected value.
   129  		if gotLen := testTreap.Len(); gotLen != numItems-i-1 {
   130  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
   131  				i, gotLen, numItems-i-1)
   132  		}
   133  
   134  		// Ensure the treap no longer has the key.
   135  		if testTreap.Has(key) {
   136  			t.Fatalf("Has #%d: key %q is in treap", i, key)
   137  		}
   138  
   139  		// Get the key that no longer exists from the treap and ensure
   140  		// it is nil.
   141  		if gotVal := testTreap.Get(key); gotVal != nil {
   142  			t.Fatalf("Get #%d: unexpected value - got %x, want nil",
   143  				i, gotVal)
   144  		}
   145  
   146  		// Ensure the expected size is reported.
   147  		expectedSize -= (nodeFieldsSize + 8)
   148  		if gotSize := testTreap.Size(); gotSize != expectedSize {
   149  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
   150  				"want %d", i, gotSize, expectedSize)
   151  		}
   152  	}
   153  }
   154  
   155  // TestImmutableReverseSequential ensures that putting keys into an immutable
   156  // treap in reverse sequential order works as expected.
   157  func TestImmutableReverseSequential(t *testing.T) {
   158  	t.Parallel()
   159  
   160  	// Insert a bunch of sequential keys while checking several of the treap
   161  	// functions work as expected.
   162  	expectedSize := uint64(0)
   163  	numItems := 1000
   164  	testTreap := NewImmutable()
   165  	for i := 0; i < numItems; i++ {
   166  		key := serializeUint32(uint32(numItems - i - 1))
   167  		testTreap = testTreap.Put(key, key)
   168  
   169  		// Ensure the treap length is the expected value.
   170  		if gotLen := testTreap.Len(); gotLen != i+1 {
   171  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
   172  				i, gotLen, i+1)
   173  		}
   174  
   175  		// Ensure the treap has the key.
   176  		if !testTreap.Has(key) {
   177  			t.Fatalf("Has #%d: key %q is not in treap", i, key)
   178  		}
   179  
   180  		// Get the key from the treap and ensure it is the expected
   181  		// value.
   182  		if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) {
   183  			t.Fatalf("Get #%d: unexpected value - got %x, want %x",
   184  				i, gotVal, key)
   185  		}
   186  
   187  		// Ensure the expected size is reported.
   188  		expectedSize += (nodeFieldsSize + 8)
   189  		if gotSize := testTreap.Size(); gotSize != expectedSize {
   190  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
   191  				"want %d", i, gotSize, expectedSize)
   192  		}
   193  	}
   194  
   195  	// Ensure the all keys are iterated by ForEach in order.
   196  	var numIterated int
   197  	testTreap.ForEach(func(k, v []byte) bool {
   198  		wantKey := serializeUint32(uint32(numIterated))
   199  
   200  		// Ensure the key is as expected.
   201  		if !bytes.Equal(k, wantKey) {
   202  			t.Fatalf("ForEach #%d: unexpected key - got %x, want %x",
   203  				numIterated, k, wantKey)
   204  		}
   205  
   206  		// Ensure the value is as expected.
   207  		if !bytes.Equal(v, wantKey) {
   208  			t.Fatalf("ForEach #%d: unexpected value - got %x, want %x",
   209  				numIterated, v, wantKey)
   210  		}
   211  
   212  		numIterated++
   213  		return true
   214  	})
   215  
   216  	// Ensure all items were iterated.
   217  	if numIterated != numItems {
   218  		t.Fatalf("ForEach: unexpected iterate count - got %d, want %d",
   219  			numIterated, numItems)
   220  	}
   221  
   222  	// Delete the keys one-by-one while checking several of the treap
   223  	// functions work as expected.
   224  	for i := 0; i < numItems; i++ {
   225  		// Intentionally use the reverse order they were inserted here.
   226  		key := serializeUint32(uint32(i))
   227  		testTreap = testTreap.Delete(key)
   228  
   229  		// Ensure the treap length is the expected value.
   230  		if gotLen := testTreap.Len(); gotLen != numItems-i-1 {
   231  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
   232  				i, gotLen, numItems-i-1)
   233  		}
   234  
   235  		// Ensure the treap no longer has the key.
   236  		if testTreap.Has(key) {
   237  			t.Fatalf("Has #%d: key %q is in treap", i, key)
   238  		}
   239  
   240  		// Get the key that no longer exists from the treap and ensure
   241  		// it is nil.
   242  		if gotVal := testTreap.Get(key); gotVal != nil {
   243  			t.Fatalf("Get #%d: unexpected value - got %x, want nil",
   244  				i, gotVal)
   245  		}
   246  
   247  		// Ensure the expected size is reported.
   248  		expectedSize -= (nodeFieldsSize + 8)
   249  		if gotSize := testTreap.Size(); gotSize != expectedSize {
   250  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
   251  				"want %d", i, gotSize, expectedSize)
   252  		}
   253  	}
   254  }
   255  
   256  // TestImmutableUnordered ensures that putting keys into an immutable treap in
   257  // no paritcular order works as expected.
   258  func TestImmutableUnordered(t *testing.T) {
   259  	t.Parallel()
   260  
   261  	// Insert a bunch of out-of-order keys while checking several of the
   262  	// treap functions work as expected.
   263  	expectedSize := uint64(0)
   264  	numItems := 1000
   265  	testTreap := NewImmutable()
   266  	for i := 0; i < numItems; i++ {
   267  		// Hash the serialized int to generate out-of-order keys.
   268  		hash := sha256.Sum256(serializeUint32(uint32(i)))
   269  		key := hash[:]
   270  		testTreap = testTreap.Put(key, key)
   271  
   272  		// Ensure the treap length is the expected value.
   273  		if gotLen := testTreap.Len(); gotLen != i+1 {
   274  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
   275  				i, gotLen, i+1)
   276  		}
   277  
   278  		// Ensure the treap has the key.
   279  		if !testTreap.Has(key) {
   280  			t.Fatalf("Has #%d: key %q is not in treap", i, key)
   281  		}
   282  
   283  		// Get the key from the treap and ensure it is the expected
   284  		// value.
   285  		if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) {
   286  			t.Fatalf("Get #%d: unexpected value - got %x, want %x",
   287  				i, gotVal, key)
   288  		}
   289  
   290  		// Ensure the expected size is reported.
   291  		expectedSize += nodeFieldsSize + uint64(len(key)+len(key))
   292  		if gotSize := testTreap.Size(); gotSize != expectedSize {
   293  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
   294  				"want %d", i, gotSize, expectedSize)
   295  		}
   296  	}
   297  
   298  	// Delete the keys one-by-one while checking several of the treap
   299  	// functions work as expected.
   300  	for i := 0; i < numItems; i++ {
   301  		// Hash the serialized int to generate out-of-order keys.
   302  		hash := sha256.Sum256(serializeUint32(uint32(i)))
   303  		key := hash[:]
   304  		testTreap = testTreap.Delete(key)
   305  
   306  		// Ensure the treap length is the expected value.
   307  		if gotLen := testTreap.Len(); gotLen != numItems-i-1 {
   308  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
   309  				i, gotLen, numItems-i-1)
   310  		}
   311  
   312  		// Ensure the treap no longer has the key.
   313  		if testTreap.Has(key) {
   314  			t.Fatalf("Has #%d: key %q is in treap", i, key)
   315  		}
   316  
   317  		// Get the key that no longer exists from the treap and ensure
   318  		// it is nil.
   319  		if gotVal := testTreap.Get(key); gotVal != nil {
   320  			t.Fatalf("Get #%d: unexpected value - got %x, want nil",
   321  				i, gotVal)
   322  		}
   323  
   324  		// Ensure the expected size is reported.
   325  		expectedSize -= (nodeFieldsSize + 64)
   326  		if gotSize := testTreap.Size(); gotSize != expectedSize {
   327  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
   328  				"want %d", i, gotSize, expectedSize)
   329  		}
   330  	}
   331  }
   332  
   333  // TestImmutableDuplicatePut ensures that putting a duplicate key into an
   334  // immutable treap works as expected.
   335  func TestImmutableDuplicatePut(t *testing.T) {
   336  	t.Parallel()
   337  
   338  	expectedVal := []byte("testval")
   339  	expectedSize := uint64(0)
   340  	numItems := 1000
   341  	testTreap := NewImmutable()
   342  	for i := 0; i < numItems; i++ {
   343  		key := serializeUint32(uint32(i))
   344  		testTreap = testTreap.Put(key, key)
   345  		expectedSize += nodeFieldsSize + uint64(len(key)+len(key))
   346  
   347  		// Put a duplicate key with the the expected final value.
   348  		testTreap = testTreap.Put(key, expectedVal)
   349  
   350  		// Ensure the key still exists and is the new value.
   351  		if gotVal := testTreap.Has(key); !gotVal {
   352  			t.Fatalf("Has: unexpected result - got %v, want true",
   353  				gotVal)
   354  		}
   355  		if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, expectedVal) {
   356  			t.Fatalf("Get: unexpected result - got %x, want %x",
   357  				gotVal, expectedVal)
   358  		}
   359  
   360  		// Ensure the expected size is reported.
   361  		expectedSize -= uint64(len(key))
   362  		expectedSize += uint64(len(expectedVal))
   363  		if gotSize := testTreap.Size(); gotSize != expectedSize {
   364  			t.Fatalf("Size: unexpected byte size - got %d, want %d",
   365  				gotSize, expectedSize)
   366  		}
   367  	}
   368  }
   369  
   370  // TestImmutableNilValue ensures that putting a nil value into an immutable
   371  // treap results in a key being added with an empty byte slice.
   372  func TestImmutableNilValue(t *testing.T) {
   373  	t.Parallel()
   374  
   375  	key := serializeUint32(0)
   376  
   377  	// Put the key with a nil value.
   378  	testTreap := NewImmutable()
   379  	testTreap = testTreap.Put(key, nil)
   380  
   381  	// Ensure the key exists and is an empty byte slice.
   382  	if gotVal := testTreap.Has(key); !gotVal {
   383  		t.Fatalf("Has: unexpected result - got %v, want true", gotVal)
   384  	}
   385  	if gotVal := testTreap.Get(key); gotVal == nil {
   386  		t.Fatalf("Get: unexpected result - got nil, want empty slice")
   387  	}
   388  	if gotVal := testTreap.Get(key); len(gotVal) != 0 {
   389  		t.Fatalf("Get: unexpected result - got %x, want empty slice",
   390  			gotVal)
   391  	}
   392  }
   393  
   394  // TestImmutableForEachStopIterator ensures that returning false from the ForEach
   395  // callback on an immutable treap stops iteration early.
   396  func TestImmutableForEachStopIterator(t *testing.T) {
   397  	t.Parallel()
   398  
   399  	// Insert a few keys.
   400  	numItems := 10
   401  	testTreap := NewImmutable()
   402  	for i := 0; i < numItems; i++ {
   403  		key := serializeUint32(uint32(i))
   404  		testTreap = testTreap.Put(key, key)
   405  	}
   406  
   407  	// Ensure ForEach exits early on false return by caller.
   408  	var numIterated int
   409  	testTreap.ForEach(func(k, v []byte) bool {
   410  		numIterated++
   411  		return numIterated != numItems/2
   412  	})
   413  	if numIterated != numItems/2 {
   414  		t.Fatalf("ForEach: unexpected iterate count - got %d, want %d",
   415  			numIterated, numItems/2)
   416  	}
   417  }
   418  
   419  // TestImmutableSnapshot ensures that immutable treaps are actually immutable by
   420  // keeping a reference to the previous treap, performing a mutation, and then
   421  // ensuring the referenced treap does not have the mutation applied.
   422  func TestImmutableSnapshot(t *testing.T) {
   423  	t.Parallel()
   424  
   425  	// Insert a bunch of sequential keys while checking several of the treap
   426  	// functions work as expected.
   427  	expectedSize := uint64(0)
   428  	numItems := 1000
   429  	testTreap := NewImmutable()
   430  	for i := 0; i < numItems; i++ {
   431  		treapSnap := testTreap
   432  
   433  		key := serializeUint32(uint32(i))
   434  		testTreap = testTreap.Put(key, key)
   435  
   436  		// Ensure the length of the treap snapshot is the expected
   437  		// value.
   438  		if gotLen := treapSnap.Len(); gotLen != i {
   439  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
   440  				i, gotLen, i)
   441  		}
   442  
   443  		// Ensure the treap snapshot does not have the key.
   444  		if treapSnap.Has(key) {
   445  			t.Fatalf("Has #%d: key %q is in treap", i, key)
   446  		}
   447  
   448  		// Get the key that doesn't exist in the treap snapshot and
   449  		// ensure it is nil.
   450  		if gotVal := treapSnap.Get(key); gotVal != nil {
   451  			t.Fatalf("Get #%d: unexpected value - got %x, want nil",
   452  				i, gotVal)
   453  		}
   454  
   455  		// Ensure the expected size is reported.
   456  		if gotSize := treapSnap.Size(); gotSize != expectedSize {
   457  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
   458  				"want %d", i, gotSize, expectedSize)
   459  		}
   460  		expectedSize += (nodeFieldsSize + 8)
   461  	}
   462  
   463  	// Delete the keys one-by-one while checking several of the treap
   464  	// functions work as expected.
   465  	for i := 0; i < numItems; i++ {
   466  		treapSnap := testTreap
   467  
   468  		key := serializeUint32(uint32(i))
   469  		testTreap = testTreap.Delete(key)
   470  
   471  		// Ensure the length of the treap snapshot is the expected
   472  		// value.
   473  		if gotLen := treapSnap.Len(); gotLen != numItems-i {
   474  			t.Fatalf("Len #%d: unexpected length - got %d, want %d",
   475  				i, gotLen, numItems-i)
   476  		}
   477  
   478  		// Ensure the treap snapshot still has the key.
   479  		if !treapSnap.Has(key) {
   480  			t.Fatalf("Has #%d: key %q is not in treap", i, key)
   481  		}
   482  
   483  		// Get the key from the treap snapshot and ensure it is still
   484  		// the expected value.
   485  		if gotVal := treapSnap.Get(key); !bytes.Equal(gotVal, key) {
   486  			t.Fatalf("Get #%d: unexpected value - got %x, want %x",
   487  				i, gotVal, key)
   488  		}
   489  
   490  		// Ensure the expected size is reported.
   491  		if gotSize := treapSnap.Size(); gotSize != expectedSize {
   492  			t.Fatalf("Size #%d: unexpected byte size - got %d, "+
   493  				"want %d", i, gotSize, expectedSize)
   494  		}
   495  		expectedSize -= (nodeFieldsSize + 8)
   496  	}
   497  }