// Copyright (c) 2015-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package treap import ( "bytes" "encoding/binary" "testing" ) // TestMutableIterator ensures that the general behavior of mutable treap // iterators is as expected including tests for first, last, ordered and reverse // ordered iteration, limiting the range, seeking, and initially unpositioned. func TestMutableIterator(t *testing.T) { t.Parallel() tests := []struct { numKeys int step int startKey []byte limitKey []byte expectedFirst []byte expectedLast []byte seekKey []byte expectedSeek []byte }{ // No range limits. Values are the set (0, 1, 2, ..., 49). // Seek existing value. { numKeys: 50, step: 1, expectedFirst: serializeUint32(0), expectedLast: serializeUint32(49), seekKey: serializeUint32(12), expectedSeek: serializeUint32(12), }, // Limited to range [24, end]. Values are the set // (0, 2, 4, ..., 48). Seek value that doesn't exist and is // greater than largest existing key. { numKeys: 50, step: 2, startKey: serializeUint32(24), expectedFirst: serializeUint32(24), expectedLast: serializeUint32(48), seekKey: serializeUint32(49), expectedSeek: nil, }, // Limited to range [start, 25). Values are the set // (0, 3, 6, ..., 48). Seek value that doesn't exist but is // before an existing value within the range. { numKeys: 50, step: 3, limitKey: serializeUint32(25), expectedFirst: serializeUint32(0), expectedLast: serializeUint32(24), seekKey: serializeUint32(17), expectedSeek: serializeUint32(18), }, // Limited to range [10, 21). Values are the set // (0, 4, ..., 48). Seek value that exists, but is before the // minimum allowed range. { numKeys: 50, step: 4, startKey: serializeUint32(10), limitKey: serializeUint32(21), expectedFirst: serializeUint32(12), expectedLast: serializeUint32(20), seekKey: serializeUint32(4), expectedSeek: nil, }, // Limited by prefix {0,0,0}, range [{0,0,0}, {0,0,1}). // Since it's a bytewise compare, {0,0,0,...} < {0,0,1}. // Seek existing value within the allowed range. { numKeys: 300, step: 1, startKey: []byte{0x00, 0x00, 0x00}, limitKey: []byte{0x00, 0x00, 0x01}, expectedFirst: serializeUint32(0), expectedLast: serializeUint32(255), seekKey: serializeUint32(100), expectedSeek: serializeUint32(100), }, } testLoop: for i, test := range tests { // Insert a bunch of keys. testTreap := NewMutable() for i := 0; i < test.numKeys; i += test.step { key := serializeUint32(uint32(i)) testTreap.Put(key, key) } // Create new iterator limited by the test params. iter := testTreap.Iterator(test.startKey, test.limitKey) // Ensure the first item is accurate. hasFirst := iter.First() if !hasFirst && test.expectedFirst != nil { t.Errorf("First #%d: unexpected exhausted iterator", i) continue } gotKey := iter.Key() if !bytes.Equal(gotKey, test.expectedFirst) { t.Errorf("First.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedFirst) continue } gotVal := iter.Value() if !bytes.Equal(gotVal, test.expectedFirst) { t.Errorf("First.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedFirst) continue } // Ensure the iterator gives the expected items in order. curNum := binary.BigEndian.Uint32(test.expectedFirst) for iter.Next() { curNum += uint32(test.step) // Ensure key is as expected. gotKey := iter.Key() expectedKey := serializeUint32(curNum) if !bytes.Equal(gotKey, expectedKey) { t.Errorf("iter.Key #%d (%d): unexpected key - "+ "got %x, want %x", i, curNum, gotKey, expectedKey) continue testLoop } // Ensure value is as expected. gotVal := iter.Value() if !bytes.Equal(gotVal, expectedKey) { t.Errorf("iter.Value #%d (%d): unexpected "+ "value - got %x, want %x", i, curNum, gotVal, expectedKey) continue testLoop } } // Ensure iterator is exhausted. if iter.Valid() { t.Errorf("Valid #%d: iterator should be exhausted", i) continue } // Ensure the last item is accurate. hasLast := iter.Last() if !hasLast && test.expectedLast != nil { t.Errorf("Last #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedLast) { t.Errorf("Last.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedLast) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedLast) { t.Errorf("Last.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedLast) continue } // Ensure the iterator gives the expected items in reverse // order. curNum = binary.BigEndian.Uint32(test.expectedLast) for iter.Prev() { curNum -= uint32(test.step) // Ensure key is as expected. gotKey := iter.Key() expectedKey := serializeUint32(curNum) if !bytes.Equal(gotKey, expectedKey) { t.Errorf("iter.Key #%d (%d): unexpected key - "+ "got %x, want %x", i, curNum, gotKey, expectedKey) continue testLoop } // Ensure value is as expected. gotVal := iter.Value() if !bytes.Equal(gotVal, expectedKey) { t.Errorf("iter.Value #%d (%d): unexpected "+ "value - got %x, want %x", i, curNum, gotVal, expectedKey) continue testLoop } } // Ensure iterator is exhausted. if iter.Valid() { t.Errorf("Valid #%d: iterator should be exhausted", i) continue } // Seek to the provided key. seekValid := iter.Seek(test.seekKey) if !seekValid && test.expectedSeek != nil { t.Errorf("Seek #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedSeek) { t.Errorf("Seek.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedSeek) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedSeek) { t.Errorf("Seek.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedSeek) continue } // Recreate the iterator and ensure calling Next on it before it // has been positioned gives the first element. iter = testTreap.Iterator(test.startKey, test.limitKey) hasNext := iter.Next() if !hasNext && test.expectedFirst != nil { t.Errorf("Next #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedFirst) { t.Errorf("Next.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedFirst) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedFirst) { t.Errorf("Next.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedFirst) continue } // Recreate the iterator and ensure calling Prev on it before it // has been positioned gives the first element. iter = testTreap.Iterator(test.startKey, test.limitKey) hasPrev := iter.Prev() if !hasPrev && test.expectedLast != nil { t.Errorf("Prev #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedLast) { t.Errorf("Prev.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedLast) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedLast) { t.Errorf("Next.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedLast) continue } } } // TestMutableEmptyIterator ensures that the various functions behave as // expected when a mutable treap is empty. func TestMutableEmptyIterator(t *testing.T) { t.Parallel() // Create iterator against empty treap. testTreap := NewMutable() iter := testTreap.Iterator(nil, nil) // Ensure Valid on empty iterator reports it as exhausted. if iter.Valid() { t.Fatal("Valid: iterator should be exhausted") } // Ensure First and Last on empty iterator report it as exhausted. if iter.First() { t.Fatal("First: iterator should be exhausted") } if iter.Last() { t.Fatal("Last: iterator should be exhausted") } // Ensure Next and Prev on empty iterator report it as exhausted. if iter.Next() { t.Fatal("Next: iterator should be exhausted") } if iter.Prev() { t.Fatal("Prev: iterator should be exhausted") } // Ensure Key and Value on empty iterator are nil. if gotKey := iter.Key(); gotKey != nil { t.Fatalf("Key: should be nil - got %q", gotKey) } if gotVal := iter.Value(); gotVal != nil { t.Fatalf("Value: should be nil - got %q", gotVal) } // Ensure Next and Prev report exhausted after forcing a reseek on an // empty iterator. iter.ForceReseek() if iter.Next() { t.Fatal("Next: iterator should be exhausted") } iter.ForceReseek() if iter.Prev() { t.Fatal("Prev: iterator should be exhausted") } } // TestIteratorUpdates ensures that issuing a call to ForceReseek on an iterator // that had the underlying mutable treap updated works as expected. func TestIteratorUpdates(t *testing.T) { t.Parallel() // Create a new treap with various values inserted in no particular // order. The resulting keys are the set (2, 4, 7, 11, 18, 25). testTreap := NewMutable() testTreap.Put(serializeUint32(7), nil) testTreap.Put(serializeUint32(2), nil) testTreap.Put(serializeUint32(18), nil) testTreap.Put(serializeUint32(11), nil) testTreap.Put(serializeUint32(25), nil) testTreap.Put(serializeUint32(4), nil) // Create an iterator against the treap with a range that excludes the // lowest and highest entries. The limited set is then (4, 7, 11, 18) iter := testTreap.Iterator(serializeUint32(3), serializeUint32(25)) // Delete a key from the middle of the range and notify the iterator to // force a reseek. testTreap.Delete(serializeUint32(11)) iter.ForceReseek() // Ensure that calling Next on the iterator after the forced reseek // gives the expected key. The limited set of keys at this point is // (4, 7, 18) and the iterator has not yet been positioned. if !iter.Next() { t.Fatal("ForceReseek.Next: unexpected exhausted iterator") } wantKey := serializeUint32(4) gotKey := iter.Key() if !bytes.Equal(gotKey, wantKey) { t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", gotKey, wantKey) } // Delete the key the iterator is currently position at and notify the // iterator to force a reseek. testTreap.Delete(serializeUint32(4)) iter.ForceReseek() // Ensure that calling Next on the iterator after the forced reseek // gives the expected key. The limited set of keys at this point is // (7, 18) and the iterator is positioned at a deleted entry before 7. if !iter.Next() { t.Fatal("ForceReseek.Next: unexpected exhausted iterator") } wantKey = serializeUint32(7) gotKey = iter.Key() if !bytes.Equal(gotKey, wantKey) { t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", gotKey, wantKey) } // Add a key before the current key the iterator is position at and // notify the iterator to force a reseek. testTreap.Put(serializeUint32(4), nil) iter.ForceReseek() // Ensure that calling Prev on the iterator after the forced reseek // gives the expected key. The limited set of keys at this point is // (4, 7, 18) and the iterator is positioned at 7. if !iter.Prev() { t.Fatal("ForceReseek.Prev: unexpected exhausted iterator") } wantKey = serializeUint32(4) gotKey = iter.Key() if !bytes.Equal(gotKey, wantKey) { t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", gotKey, wantKey) } // Delete the next key the iterator would ordinarily move to then notify // the iterator to force a reseek. testTreap.Delete(serializeUint32(7)) iter.ForceReseek() // Ensure that calling Next on the iterator after the forced reseek // gives the expected key. The limited set of keys at this point is // (4, 18) and the iterator is positioned at 4. if !iter.Next() { t.Fatal("ForceReseek.Next: unexpected exhausted iterator") } wantKey = serializeUint32(18) gotKey = iter.Key() if !bytes.Equal(gotKey, wantKey) { t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", gotKey, wantKey) } } // TestImmutableIterator ensures that the general behavior of immutable treap // iterators is as expected including tests for first, last, ordered and reverse // ordered iteration, limiting the range, seeking, and initially unpositioned. func TestImmutableIterator(t *testing.T) { t.Parallel() tests := []struct { numKeys int step int startKey []byte limitKey []byte expectedFirst []byte expectedLast []byte seekKey []byte expectedSeek []byte }{ // No range limits. Values are the set (0, 1, 2, ..., 49). // Seek existing value. { numKeys: 50, step: 1, expectedFirst: serializeUint32(0), expectedLast: serializeUint32(49), seekKey: serializeUint32(12), expectedSeek: serializeUint32(12), }, // Limited to range [24, end]. Values are the set // (0, 2, 4, ..., 48). Seek value that doesn't exist and is // greater than largest existing key. { numKeys: 50, step: 2, startKey: serializeUint32(24), expectedFirst: serializeUint32(24), expectedLast: serializeUint32(48), seekKey: serializeUint32(49), expectedSeek: nil, }, // Limited to range [start, 25). Values are the set // (0, 3, 6, ..., 48). Seek value that doesn't exist but is // before an existing value within the range. { numKeys: 50, step: 3, limitKey: serializeUint32(25), expectedFirst: serializeUint32(0), expectedLast: serializeUint32(24), seekKey: serializeUint32(17), expectedSeek: serializeUint32(18), }, // Limited to range [10, 21). Values are the set // (0, 4, ..., 48). Seek value that exists, but is before the // minimum allowed range. { numKeys: 50, step: 4, startKey: serializeUint32(10), limitKey: serializeUint32(21), expectedFirst: serializeUint32(12), expectedLast: serializeUint32(20), seekKey: serializeUint32(4), expectedSeek: nil, }, // Limited by prefix {0,0,0}, range [{0,0,0}, {0,0,1}). // Since it's a bytewise compare, {0,0,0,...} < {0,0,1}. // Seek existing value within the allowed range. { numKeys: 300, step: 1, startKey: []byte{0x00, 0x00, 0x00}, limitKey: []byte{0x00, 0x00, 0x01}, expectedFirst: serializeUint32(0), expectedLast: serializeUint32(255), seekKey: serializeUint32(100), expectedSeek: serializeUint32(100), }, } testLoop: for i, test := range tests { // Insert a bunch of keys. testTreap := NewImmutable() for i := 0; i < test.numKeys; i += test.step { key := serializeUint32(uint32(i)) testTreap = testTreap.Put(key, key) } // Create new iterator limited by the test params. iter := testTreap.Iterator(test.startKey, test.limitKey) // Ensure the first item is accurate. hasFirst := iter.First() if !hasFirst && test.expectedFirst != nil { t.Errorf("First #%d: unexpected exhausted iterator", i) continue } gotKey := iter.Key() if !bytes.Equal(gotKey, test.expectedFirst) { t.Errorf("First.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedFirst) continue } gotVal := iter.Value() if !bytes.Equal(gotVal, test.expectedFirst) { t.Errorf("First.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedFirst) continue } // Ensure the iterator gives the expected items in order. curNum := binary.BigEndian.Uint32(test.expectedFirst) for iter.Next() { curNum += uint32(test.step) // Ensure key is as expected. gotKey := iter.Key() expectedKey := serializeUint32(curNum) if !bytes.Equal(gotKey, expectedKey) { t.Errorf("iter.Key #%d (%d): unexpected key - "+ "got %x, want %x", i, curNum, gotKey, expectedKey) continue testLoop } // Ensure value is as expected. gotVal := iter.Value() if !bytes.Equal(gotVal, expectedKey) { t.Errorf("iter.Value #%d (%d): unexpected "+ "value - got %x, want %x", i, curNum, gotVal, expectedKey) continue testLoop } } // Ensure iterator is exhausted. if iter.Valid() { t.Errorf("Valid #%d: iterator should be exhausted", i) continue } // Ensure the last item is accurate. hasLast := iter.Last() if !hasLast && test.expectedLast != nil { t.Errorf("Last #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedLast) { t.Errorf("Last.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedLast) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedLast) { t.Errorf("Last.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedLast) continue } // Ensure the iterator gives the expected items in reverse // order. curNum = binary.BigEndian.Uint32(test.expectedLast) for iter.Prev() { curNum -= uint32(test.step) // Ensure key is as expected. gotKey := iter.Key() expectedKey := serializeUint32(curNum) if !bytes.Equal(gotKey, expectedKey) { t.Errorf("iter.Key #%d (%d): unexpected key - "+ "got %x, want %x", i, curNum, gotKey, expectedKey) continue testLoop } // Ensure value is as expected. gotVal := iter.Value() if !bytes.Equal(gotVal, expectedKey) { t.Errorf("iter.Value #%d (%d): unexpected "+ "value - got %x, want %x", i, curNum, gotVal, expectedKey) continue testLoop } } // Ensure iterator is exhausted. if iter.Valid() { t.Errorf("Valid #%d: iterator should be exhausted", i) continue } // Seek to the provided key. seekValid := iter.Seek(test.seekKey) if !seekValid && test.expectedSeek != nil { t.Errorf("Seek #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedSeek) { t.Errorf("Seek.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedSeek) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedSeek) { t.Errorf("Seek.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedSeek) continue } // Recreate the iterator and ensure calling Next on it before it // has been positioned gives the first element. iter = testTreap.Iterator(test.startKey, test.limitKey) hasNext := iter.Next() if !hasNext && test.expectedFirst != nil { t.Errorf("Next #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedFirst) { t.Errorf("Next.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedFirst) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedFirst) { t.Errorf("Next.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedFirst) continue } // Recreate the iterator and ensure calling Prev on it before it // has been positioned gives the first element. iter = testTreap.Iterator(test.startKey, test.limitKey) hasPrev := iter.Prev() if !hasPrev && test.expectedLast != nil { t.Errorf("Prev #%d: unexpected exhausted iterator", i) continue } gotKey = iter.Key() if !bytes.Equal(gotKey, test.expectedLast) { t.Errorf("Prev.Key #%d: unexpected key - got %x, "+ "want %x", i, gotKey, test.expectedLast) continue } gotVal = iter.Value() if !bytes.Equal(gotVal, test.expectedLast) { t.Errorf("Next.Value #%d: unexpected value - got %x, "+ "want %x", i, gotVal, test.expectedLast) continue } } } // TestImmutableEmptyIterator ensures that the various functions behave as // expected when an immutable treap is empty. func TestImmutableEmptyIterator(t *testing.T) { t.Parallel() // Create iterator against empty treap. testTreap := NewImmutable() iter := testTreap.Iterator(nil, nil) // Ensure Valid on empty iterator reports it as exhausted. if iter.Valid() { t.Fatal("Valid: iterator should be exhausted") } // Ensure First and Last on empty iterator report it as exhausted. if iter.First() { t.Fatal("First: iterator should be exhausted") } if iter.Last() { t.Fatal("Last: iterator should be exhausted") } // Ensure Next and Prev on empty iterator report it as exhausted. if iter.Next() { t.Fatal("Next: iterator should be exhausted") } if iter.Prev() { t.Fatal("Prev: iterator should be exhausted") } // Ensure Key and Value on empty iterator are nil. if gotKey := iter.Key(); gotKey != nil { t.Fatalf("Key: should be nil - got %q", gotKey) } if gotVal := iter.Value(); gotVal != nil { t.Fatalf("Value: should be nil - got %q", gotVal) } // Ensure calling ForceReseek on an immutable treap iterator does not // cause any issues since it only applies to mutable treap iterators. iter.ForceReseek() if iter.Next() { t.Fatal("Next: iterator should be exhausted") } iter.ForceReseek() if iter.Prev() { t.Fatal("Prev: iterator should be exhausted") } }