github.com/bcskill/bcschain/v3@v3.4.9-beta2/ethdb/file_segment_test.go (about)

     1  package ethdb_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"math"
     7  	"math/rand"
     8  	"os"
     9  	"reflect"
    10  	"testing"
    11  	"testing/quick"
    12  
    13  	"github.com/bcskill/bcschain/v3/common"
    14  
    15  	"github.com/bcskill/bcschain/v3/ethdb"
    16  )
    17  
    18  func TestFileSegment_Get(t *testing.T) {
    19  	t.Run("OK", func(t *testing.T) {
    20  		path := MustTempFile()
    21  		defer os.Remove(path)
    22  
    23  		// Encode key/values to file segment.
    24  		enc := ethdb.NewFileSegmentEncoder(path)
    25  		if err := enc.Open(); err != nil {
    26  			t.Fatal(err)
    27  		} else if err := enc.EncodeKeyValue([]byte("foo"), []byte("bar")); err != nil {
    28  			t.Fatal(err)
    29  		} else if err := enc.Flush(); err != nil {
    30  			t.Fatal(err)
    31  		} else if err := enc.Close(); err != nil {
    32  			t.Fatal(err)
    33  		}
    34  
    35  		// Open as file segment.
    36  		s := ethdb.NewFileSegment("test", path)
    37  		if err := s.Open(); err != nil {
    38  			t.Fatal(err)
    39  		}
    40  		defer s.Close()
    41  
    42  		// Fetch existing keys.
    43  		if v, err := s.Get([]byte("foo")); err != nil {
    44  			t.Fatal(err)
    45  		} else if string(v) != "bar" {
    46  			t.Fatalf("unexpected value: %q", v)
    47  		}
    48  
    49  		// Fetch unknown key.
    50  		if v, err := s.Get([]byte("no_such_key")); err != common.ErrNotFound {
    51  			t.Fatalf("unexpected error: %s", err)
    52  		} else if v != nil {
    53  			t.Fatalf("expected nil value, got %q", v)
    54  		}
    55  
    56  		// Close file segment.
    57  		if err := s.Close(); err != nil {
    58  			t.Fatal(err)
    59  		}
    60  	})
    61  }
    62  
    63  // Ensure ethdb.FileSegment can fetch keys using randomized test data.
    64  func TestFileSegment_Quick(t *testing.T) {
    65  	if testing.Short() {
    66  		t.Skip("short")
    67  	}
    68  
    69  	const maxCount = 10000
    70  	const maxKeyLen = 2000
    71  	const maxValueLen = 10000
    72  
    73  	quick.Check(func(keys, values [][]byte) bool {
    74  		path := MustTempFile()
    75  		defer os.Remove(path)
    76  
    77  		// Write data to file.
    78  		if err := EncodeToFileSegment(path, keys, values); err != nil {
    79  			t.Fatal(err)
    80  		}
    81  
    82  		// Open as file.
    83  		s := ethdb.NewFileSegment("test", path)
    84  		if err := s.Open(); err != nil {
    85  			t.Fatal(err)
    86  		}
    87  		defer s.Close()
    88  
    89  		// Verify all key/value pairs exist.
    90  		for i := range keys {
    91  			if v, err := s.Get(keys[i]); err != nil {
    92  				t.Fatal(err)
    93  			} else if !bytes.Equal(values[i], v) {
    94  				t.Fatalf("value mismatch: (key=%q) expected %q, got %q", keys[i], values[i], v)
    95  			}
    96  		}
    97  
    98  		// Verify we can iterate them in order.
    99  		itr, i := s.Iterator(), 0
   100  		for ; itr.Next(); i++ {
   101  			if !bytes.Equal(itr.Key(), keys[i]) {
   102  				t.Fatalf("iterator key mismatch:\nexpected %x\ngot %x", keys[i], itr.Key())
   103  			} else if !bytes.Equal(itr.Value(), values[i]) {
   104  				t.Fatal("iterator value mismatch")
   105  			}
   106  		}
   107  		if i != len(keys) {
   108  			t.Fatal("short iterator")
   109  		}
   110  
   111  		return true
   112  	}, &quick.Config{
   113  		MaxCount: 10,
   114  		Values: func(args []reflect.Value, rand *rand.Rand) {
   115  			n := rand.Intn(maxCount-1) + 1
   116  			args[0] = reflect.ValueOf(generateKeys(n, 1, maxKeyLen-1, rand))
   117  			args[1] = reflect.ValueOf(generateValues(n, 0, maxValueLen, rand))
   118  		},
   119  	})
   120  }
   121  
   122  func BenchmarkFileSegment_Get(b *testing.B) {
   123  	path := MustTempFile()
   124  	defer os.Remove(path)
   125  
   126  	// Encode key/value pairs.
   127  	const n = 100000
   128  	keys, values := make([][]byte, n), make([][]byte, n)
   129  	for i := 0; i < n; i++ {
   130  		keys[i] = make([]byte, 32)
   131  		binary.BigEndian.PutUint64(keys[i], uint64(i))
   132  		values[i] = make([]byte, 1024)
   133  	}
   134  	EncodeToFileSegment(path, keys, values)
   135  
   136  	// Determine random access pattern.
   137  	perm := rand.Perm(n)
   138  
   139  	// Open as file segment.
   140  	s := ethdb.NewFileSegment("test", path)
   141  	if err := s.Open(); err != nil {
   142  		b.Fatal(err)
   143  	}
   144  	defer s.Close()
   145  
   146  	b.ResetTimer()
   147  	b.ReportAllocs()
   148  
   149  	// Lookup key/value pairs.
   150  	for i := 0; i < b.N; i++ {
   151  		key := keys[perm[i%len(perm)]]
   152  		if v, err := s.Get(key); err != nil {
   153  			b.Fatal(err)
   154  		} else if v == nil {
   155  			b.Fatal("key not found")
   156  		}
   157  	}
   158  }
   159  
   160  // EncodeToFileSegment encodes a set of key/value pairs to an ethdb.FileSegment at path.
   161  func EncodeToFileSegment(path string, keys, values [][]byte) error {
   162  	// Build file segment.
   163  	enc := ethdb.NewFileSegmentEncoder(path)
   164  	if err := enc.Open(); err != nil {
   165  		return err
   166  	}
   167  	defer enc.Close()
   168  
   169  	// Write all keys.
   170  	for i := range keys {
   171  		if err := enc.EncodeKeyValue(keys[i], values[i]); err != nil {
   172  			return err
   173  		}
   174  	}
   175  
   176  	// Flush all data.
   177  	if err := enc.Flush(); err != nil {
   178  		return err
   179  	} else if err := enc.Close(); err != nil {
   180  		return err
   181  	}
   182  	return nil
   183  }
   184  
   185  // generateKeys returns a set of n unique, randomly generated keys.
   186  func generateKeys(n, min, max int, rand *rand.Rand) [][]byte {
   187  	a := make([][]byte, n)
   188  	for i, m := 0, make(map[string]struct{}); i < len(a); i++ {
   189  		a[i] = generateBytes(min, max, rand)
   190  		if _, ok := m[string(a[i])]; ok {
   191  			i--
   192  			continue
   193  		}
   194  		m[string(a[i])] = struct{}{}
   195  	}
   196  	return a
   197  }
   198  
   199  // generateValues returns a set of n randomly generated values.
   200  func generateValues(n, min, max int, rand *rand.Rand) [][]byte {
   201  	a := make([][]byte, n)
   202  	for i := range a {
   203  		a[i] = generateBytes(min, max, rand)
   204  	}
   205  	return a
   206  }
   207  
   208  // generateBytes returns a randomly generated byte slice between min & max length.
   209  func generateBytes(min, max int, rand *rand.Rand) []byte {
   210  	b := make([]byte, rand.Intn(max-min)+min)
   211  	for i := range b {
   212  		b[i] = byte(rand.Intn(math.MaxInt8))
   213  	}
   214  	return b
   215  }