ceremonyclient/pebble/metamorphic/generator_test.go

225 lines
5.8 KiB
Go
Raw Permalink Normal View History

2024-01-03 07:31:42 +00:00
// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
// of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
package metamorphic
import (
"fmt"
"testing"
"time"
"github.com/cockroachdb/pebble"
"github.com/cockroachdb/pebble/internal/randvar"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"golang.org/x/exp/rand"
)
func TestGenerator(t *testing.T) {
rng := randvar.NewRand()
g := newGenerator(rng, defaultConfig(), newKeyManager(1 /* numInstances */))
g.newBatch()
g.newBatch()
g.newBatch()
require.EqualValues(t, 3, len(g.liveBatches))
require.EqualValues(t, 0, len(g.batches))
g.newIndexedBatch()
g.newIndexedBatch()
g.newIndexedBatch()
require.EqualValues(t, 6, len(g.liveBatches))
require.EqualValues(t, 3, len(g.batches))
require.EqualValues(t, 3, len(g.readers))
g.newIter()
g.newIter()
g.newIter()
require.EqualValues(t, 3, len(g.liveIters))
g.batchAbort()
g.batchAbort()
g.batchAbort()
g.batchAbort()
g.batchAbort()
g.batchAbort()
require.EqualValues(t, 0, len(g.liveBatches))
require.EqualValues(t, 0, len(g.batches))
require.EqualValues(t, 0, len(g.iters))
require.EqualValues(t, 1, len(g.liveReaders))
require.EqualValues(t, 0, len(g.readers))
require.EqualValues(t, 0, len(g.liveSnapshots))
require.EqualValues(t, 0, len(g.snapshots))
require.EqualValues(t, 1, len(g.liveWriters))
g.randIter(g.iterClose)()
g.randIter(g.iterClose)()
g.randIter(g.iterClose)()
require.EqualValues(t, 0, len(g.liveIters))
if testing.Verbose() {
t.Logf("\n%s", g)
}
g = newGenerator(rng, defaultConfig(), newKeyManager(1 /* numInstances */))
g.newSnapshot()
g.newSnapshot()
g.newSnapshot()
require.EqualValues(t, 3, len(g.liveSnapshots))
require.EqualValues(t, 3, len(g.snapshots))
g.newIter()
g.newIter()
g.newIter()
g.snapshotClose()
g.snapshotClose()
g.snapshotClose()
require.EqualValues(t, 0, len(g.liveBatches))
require.EqualValues(t, 0, len(g.batches))
require.EqualValues(t, 0, len(g.iters))
require.EqualValues(t, 1, len(g.liveReaders))
require.EqualValues(t, 0, len(g.readers))
require.EqualValues(t, 0, len(g.liveSnapshots))
require.EqualValues(t, 0, len(g.snapshots))
require.EqualValues(t, 1, len(g.liveWriters))
g.randIter(g.iterClose)()
g.randIter(g.iterClose)()
g.randIter(g.iterClose)()
require.EqualValues(t, 0, len(g.liveIters))
if testing.Verbose() {
t.Logf("\n%s", g)
}
g = newGenerator(rng, defaultConfig(), newKeyManager(1 /* numInstances */))
g.newIndexedBatch()
g.newIndexedBatch()
g.newIndexedBatch()
g.newIter()
g.newIter()
g.newIter()
g.writerApply()
g.writerApply()
g.writerApply()
g.randIter(g.iterClose)()
g.randIter(g.iterClose)()
g.randIter(g.iterClose)()
require.EqualValues(t, 0, len(g.liveBatches))
require.EqualValues(t, 0, len(g.batches))
require.EqualValues(t, 0, len(g.liveIters))
require.EqualValues(t, 0, len(g.iters))
require.EqualValues(t, 1, len(g.liveReaders))
require.EqualValues(t, 0, len(g.readers))
require.EqualValues(t, 0, len(g.liveSnapshots))
require.EqualValues(t, 0, len(g.snapshots))
require.EqualValues(t, 1, len(g.liveWriters))
if testing.Verbose() {
t.Logf("\n%s", g)
}
}
func TestGeneratorRandom(t *testing.T) {
seed := uint64(time.Now().UnixNano())
ops := randvar.NewUniform(1000, 10000)
cfgs := []string{"default", "multiInstance"}
generateFromSeed := func(cfg config) string {
rng := rand.New(rand.NewSource(seed))
count := ops.Uint64(rng)
return formatOps(generate(rng, count, cfg, newKeyManager(cfg.numInstances)))
}
for i := range cfgs {
t.Run(fmt.Sprintf("config=%s", cfgs[i]), func(t *testing.T) {
cfg := defaultConfig
if cfgs[i] == "multiInstance" {
cfg = func() config {
cfg := multiInstanceConfig()
cfg.numInstances = 2
return cfg
}
}
// Ensure that generate doesn't use any other source of randomness other
// than rng.
referenceOps := generateFromSeed(cfg())
for i := 0; i < 10; i++ {
regeneratedOps := generateFromSeed(cfg())
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(referenceOps),
B: difflib.SplitLines(regeneratedOps),
Context: 1,
})
require.NoError(t, err)
if len(diff) > 0 {
t.Fatalf("Diff:\n%s", diff)
}
}
if testing.Verbose() {
t.Logf("\nOps:\n%s", referenceOps)
}
})
}
}
func TestGenerateRandKeyToReadInRange(t *testing.T) {
rng := randvar.NewRand()
g := newGenerator(rng, defaultConfig(), newKeyManager(1 /* numInstances */))
// Seed 100 initial keys.
for i := 0; i < 100; i++ {
_ = g.randKeyToWrite(1.0)
}
for i := 0; i < 100; i++ {
a := g.randKeyToRead(0.01)
b := g.randKeyToRead(0.01)
// Ensure unique prefixes; required by randKeyToReadInRange.
for g.equal(g.prefix(a), g.prefix(b)) {
b = g.randKeyToRead(0.01)
}
if v := g.cmp(a, b); v > 0 {
a, b = b, a
}
kr := pebble.KeyRange{Start: a, End: b}
for j := 0; j < 10; j++ {
k := g.randKeyToReadInRange(0.05, kr)
if g.cmp(k, a) < 0 {
t.Errorf("generated random key %q outside range %s", k, kr)
} else if g.cmp(k, b) >= 0 {
t.Errorf("generated random key %q outside range %s", k, kr)
}
}
}
}
func TestGenerateDisjointKeyRanges(t *testing.T) {
rng := randvar.NewRand()
g := newGenerator(rng, defaultConfig(), newKeyManager(1 /* numInstances */))
for i := 0; i < 10; i++ {
keyRanges := g.generateDisjointKeyRanges(5)
for j := range keyRanges {
t.Logf("%d: %d: %s", i, j, keyRanges[j])
for k := range keyRanges {
if j == k {
continue
}
if g.cmp(keyRanges[j].End, keyRanges[k].Start) <= 0 {
require.Less(t, j, k)
continue
}
if g.cmp(keyRanges[j].Start, keyRanges[k].End) >= 0 {
require.Greater(t, j, k)
continue
}
t.Fatalf("generated key ranges %q and %q overlap", keyRanges[j], keyRanges[k])
}
}
}
}