github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/libs/db/internal/dbtest/dbtest.go (about)

     1  package dbtest
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"math/rand"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	lldb "github.com/lazyledger/lazyledger-core/libs/db"
    15  )
    16  
    17  //----------------------------------------
    18  // Helper functions.
    19  
    20  func Valid(t *testing.T, itr lldb.Iterator, expected bool) {
    21  	valid := itr.Valid()
    22  	require.Equal(t, expected, valid)
    23  }
    24  
    25  func Next(t *testing.T, itr lldb.Iterator, expected bool) {
    26  	itr.Next()
    27  	// assert.NoError(t, err) TODO: look at fixing this
    28  	valid := itr.Valid()
    29  	require.Equal(t, expected, valid)
    30  }
    31  
    32  func NextPanics(t *testing.T, itr lldb.Iterator) {
    33  	assert.Panics(t, func() { itr.Next() }, "checkNextPanics expected an error but didn't")
    34  }
    35  
    36  func Domain(t *testing.T, itr lldb.Iterator, start, end []byte) {
    37  	ds, de := itr.Domain()
    38  	assert.Equal(t, start, ds, "checkDomain domain start incorrect")
    39  	assert.Equal(t, end, de, "checkDomain domain end incorrect")
    40  }
    41  
    42  func Item(t *testing.T, itr lldb.Iterator, key []byte, value []byte) {
    43  	v := itr.Value()
    44  
    45  	k := itr.Key()
    46  
    47  	assert.Exactly(t, key, k)
    48  	assert.Exactly(t, value, v)
    49  }
    50  
    51  func Invalid(t *testing.T, itr lldb.Iterator) {
    52  	Valid(t, itr, false)
    53  	KeyPanics(t, itr)
    54  	ValuePanics(t, itr)
    55  	NextPanics(t, itr)
    56  }
    57  
    58  func KeyPanics(t *testing.T, itr lldb.Iterator) {
    59  	assert.Panics(t, func() { itr.Key() }, "checkKeyPanics expected panic but didn't")
    60  }
    61  
    62  func Value(t *testing.T, db lldb.DB, key []byte, valueWanted []byte) {
    63  	valueGot, err := db.Get(key)
    64  	assert.NoError(t, err)
    65  	assert.Equal(t, valueWanted, valueGot)
    66  }
    67  
    68  func ValuePanics(t *testing.T, itr lldb.Iterator) {
    69  	assert.Panics(t, func() { itr.Value() })
    70  }
    71  
    72  func CleanupDBDir(dir, name string) {
    73  	err := os.RemoveAll(filepath.Join(dir, name) + ".db")
    74  	if err != nil {
    75  		panic(err)
    76  	}
    77  }
    78  
    79  const strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters
    80  
    81  // RandStr constructs a random alphanumeric string of given length.
    82  func RandStr(length int) string {
    83  	chars := []byte{}
    84  MAIN_LOOP:
    85  	for {
    86  		val := rand.Int63() // nolint:gosec // G404: Use of weak random number generator
    87  		for i := 0; i < 10; i++ {
    88  			v := int(val & 0x3f) // rightmost 6 bits
    89  			if v >= 62 {         // only 62 characters in strChars
    90  				val >>= 6
    91  				continue
    92  			} else {
    93  				chars = append(chars, strChars[v])
    94  				if len(chars) == length {
    95  					break MAIN_LOOP
    96  				}
    97  				val >>= 6
    98  			}
    99  		}
   100  	}
   101  
   102  	return string(chars)
   103  }
   104  
   105  func Int642Bytes(i int64) []byte {
   106  	buf := make([]byte, 8)
   107  	binary.BigEndian.PutUint64(buf, uint64(i))
   108  	return buf
   109  }
   110  
   111  func Bytes2Int64(buf []byte) int64 {
   112  	return int64(binary.BigEndian.Uint64(buf))
   113  }
   114  
   115  func BenchmarkRangeScans(b *testing.B, db lldb.DB, dbSize int64) {
   116  	b.StopTimer()
   117  
   118  	rangeSize := int64(10000)
   119  	if dbSize < rangeSize {
   120  		b.Errorf("db size %v cannot be less than range size %v", dbSize, rangeSize)
   121  	}
   122  
   123  	for i := int64(0); i < dbSize; i++ {
   124  		bytes := Int642Bytes(i)
   125  		err := db.Set(bytes, bytes)
   126  		if err != nil {
   127  			// require.NoError() is very expensive (according to profiler), so check manually
   128  			b.Fatal(b, err)
   129  		}
   130  	}
   131  	b.StartTimer()
   132  
   133  	for i := 0; i < b.N; i++ {
   134  
   135  		start := rand.Int63n(dbSize - rangeSize) // nolint: gosec
   136  		end := start + rangeSize
   137  		iter, err := db.Iterator(Int642Bytes(start), Int642Bytes(end))
   138  		require.NoError(b, err)
   139  		count := 0
   140  		for ; iter.Valid(); iter.Next() {
   141  			count++
   142  		}
   143  		iter.Close()
   144  		require.EqualValues(b, rangeSize, count)
   145  	}
   146  }
   147  
   148  func BenchmarkRandomReadsWrites(b *testing.B, db lldb.DB) {
   149  	b.StopTimer()
   150  
   151  	// create dummy data
   152  	const numItems = int64(1000000)
   153  	internal := map[int64]int64{}
   154  	for i := 0; i < int(numItems); i++ {
   155  		internal[int64(i)] = int64(0)
   156  	}
   157  
   158  	// fmt.Println("ok, starting")
   159  	b.StartTimer()
   160  
   161  	for i := 0; i < b.N; i++ {
   162  		// Write something
   163  		{
   164  			idx := rand.Int63n(numItems) // nolint: gosec
   165  			internal[idx]++
   166  			val := internal[idx]
   167  			idxBytes := Int642Bytes(idx)
   168  			valBytes := Int642Bytes(val)
   169  			// fmt.Printf("Set %X -> %X\n", idxBytes, valBytes)
   170  			err := db.Set(idxBytes, valBytes)
   171  			if err != nil {
   172  				// require.NoError() is very expensive (according to profiler), so check manually
   173  				b.Fatal(b, err)
   174  			}
   175  		}
   176  
   177  		// Read something
   178  		{
   179  			idx := rand.Int63n(numItems) // nolint: gosec
   180  			valExp := internal[idx]
   181  			idxBytes := Int642Bytes(idx)
   182  			valBytes, err := db.Get(idxBytes)
   183  			if err != nil {
   184  				// require.NoError() is very expensive (according to profiler), so check manually
   185  				b.Fatal(b, err)
   186  			}
   187  			// fmt.Printf("Get %X -> %X\n", idxBytes, valBytes)
   188  			if valExp == 0 {
   189  				if !bytes.Equal(valBytes, nil) {
   190  					b.Errorf("Expected %v for %v, got %X", nil, idx, valBytes)
   191  					break
   192  				}
   193  			} else {
   194  				if len(valBytes) != 8 {
   195  					b.Errorf("Expected length 8 for %v, got %X", idx, valBytes)
   196  					break
   197  				}
   198  				valGot := Bytes2Int64(valBytes)
   199  				if valExp != valGot {
   200  					b.Errorf("Expected %v for %v, got %v", valExp, idx, valGot)
   201  					break
   202  				}
   203  			}
   204  		}
   205  
   206  	}
   207  }