Skip to content

Commit

Permalink
colblk: ensure test coverage of UintBuilder array doubling loop
Browse files Browse the repository at this point in the history
Previously the body of the array doubling loop in UintBuilder.Set was
unexercised:

```
for n2 <= row {
	n2 <<= 1 /* double the size */
}
```

In part due to the initialization that precedes the loop:
```
n2 := max(b.array.n<<1, 32)
```

This commit adds a datadriven test case that exercises it, and a new
TestUintsRandomized randomized test that is reasonably likely to exercise it as
well.
  • Loading branch information
jbowens committed Nov 8, 2024
1 parent 2da617a commit 920763b
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
60 changes: 60 additions & 0 deletions sstable/colblk/testdata/uints
Original file line number Diff line number Diff line change
Expand Up @@ -694,3 +694,63 @@ uints
├── 098-099: x 00 # data[97] = 0
├── 099-100: x 00 # data[98] = 0
└── 100-101: x 00 # data[99] = 0

# Test a case where we must grow the array backing the column by doubling its
# size (its grown to at least 32 elements by default, so it's necessary to set
# an index >= 32).

init default-zero zero-struct
----

write
43:1
----

finish rows=44
----
uints
├── 00-01: x 01 # encoding: 1b
├── 01-02: x 00 # data[0] = 0
├── 02-03: x 00 # data[1] = 0
├── 03-04: x 00 # data[2] = 0
├── 04-05: x 00 # data[3] = 0
├── 05-06: x 00 # data[4] = 0
├── 06-07: x 00 # data[5] = 0
├── 07-08: x 00 # data[6] = 0
├── 08-09: x 00 # data[7] = 0
├── 09-10: x 00 # data[8] = 0
├── 10-11: x 00 # data[9] = 0
├── 11-12: x 00 # data[10] = 0
├── 12-13: x 00 # data[11] = 0
├── 13-14: x 00 # data[12] = 0
├── 14-15: x 00 # data[13] = 0
├── 15-16: x 00 # data[14] = 0
├── 16-17: x 00 # data[15] = 0
├── 17-18: x 00 # data[16] = 0
├── 18-19: x 00 # data[17] = 0
├── 19-20: x 00 # data[18] = 0
├── 20-21: x 00 # data[19] = 0
├── 21-22: x 00 # data[20] = 0
├── 22-23: x 00 # data[21] = 0
├── 23-24: x 00 # data[22] = 0
├── 24-25: x 00 # data[23] = 0
├── 25-26: x 00 # data[24] = 0
├── 26-27: x 00 # data[25] = 0
├── 27-28: x 00 # data[26] = 0
├── 28-29: x 00 # data[27] = 0
├── 29-30: x 00 # data[28] = 0
├── 30-31: x 00 # data[29] = 0
├── 31-32: x 00 # data[30] = 0
├── 32-33: x 00 # data[31] = 0
├── 33-34: x 00 # data[32] = 0
├── 34-35: x 00 # data[33] = 0
├── 35-36: x 00 # data[34] = 0
├── 36-37: x 00 # data[35] = 0
├── 37-38: x 00 # data[36] = 0
├── 38-39: x 00 # data[37] = 0
├── 39-40: x 00 # data[38] = 0
├── 40-41: x 00 # data[39] = 0
├── 41-42: x 00 # data[40] = 0
├── 42-43: x 00 # data[41] = 0
├── 43-44: x 00 # data[42] = 0
└── 44-45: x 01 # data[43] = 1
71 changes: 71 additions & 0 deletions sstable/colblk/uints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import (
"bytes"
"fmt"
"math"
"math/rand/v2"
"strconv"
"strings"
"testing"
"time"

"github.com/cockroachdb/crlib/crbytes"
"github.com/cockroachdb/datadriven"
"github.com/cockroachdb/pebble/internal/binfmt"
"github.com/cockroachdb/pebble/internal/treeprinter"
"github.com/stretchr/testify/require"
)

func TestByteWidth(t *testing.T) {
Expand Down Expand Up @@ -101,6 +104,9 @@ func TestUints(t *testing.T) {
out.Reset()
switch td.Cmd {
case "init":
if td.HasArg("zero-struct") {
b = UintBuilder{}
}
defaultZero := td.HasArg("default-zero")
b.init(defaultZero)
return ""
Expand Down Expand Up @@ -162,3 +168,68 @@ func TestUints(t *testing.T) {
}
})
}

func TestUintsRandomized(t *testing.T) {
seed := uint64(time.Now().UnixNano())
t.Logf("Seed: %d", seed)

type config struct {
defaultZero bool
rowsSet int
rowsFinish int
maxValue uint64
probNonZero float64
}

runTest := func(t *testing.T, cfg config) {
var b UintBuilder
b.init(cfg.defaultZero)
rng := rand.New(rand.NewPCG(0, seed))
vals := make([]uint64, max(cfg.rowsSet, cfg.rowsFinish))
for i := 0; i < cfg.rowsSet; i++ {
if rng.Float64() < cfg.probNonZero {
vals[i] = rng.Uint64N(cfg.maxValue)
}
if vals[i] != 0 || !cfg.defaultZero {
b.Set(i, vals[i])
}
}
sz := b.Size(cfg.rowsFinish, 0)
buf := crbytes.AllocAligned(int(sz) + 1 /* extra padding byte for pointer safety */)
off := b.Finish(0, cfg.rowsFinish, 0, buf)
require.Equal(t, sz, off)

uu, endOff := DecodeUnsafeUints(buf, 0, cfg.rowsFinish)
require.Equal(t, endOff, off)
for i := 0; i < cfg.rowsFinish; i++ {
if uu.At(i) != vals[i] {
t.Fatalf("At(%d) = %d, want %d", i, uu.At(i), vals[i])
}
}
}

rng := rand.New(rand.NewPCG(0, seed))
for i := 0; i < 20; i++ {
rowsSet := rng.IntN(10000)
cfg := config{
defaultZero: rng.Float64() < 0.5,
rowsSet: rowsSet,
rowsFinish: rowsSet,
maxValue: math.MaxUint64 >> rng.Uint64N(63),
probNonZero: rng.Float64(),
}
if p := rng.Float64(); p < 0.1 && cfg.defaultZero {
cfg.rowsFinish = rng.IntN(10000)
} else if p < 0.2 && cfg.rowsSet > 0 {
cfg.rowsFinish = cfg.rowsSet - 1
} else if p < 0.3 && cfg.defaultZero {
cfg.rowsFinish = cfg.rowsSet + 1
}
t.Run(
fmt.Sprintf("defaultZero=%t,rows=%d,finish=%d,max=%d,probNonZero=%.2f",
cfg.defaultZero, cfg.rowsSet, cfg.rowsFinish, cfg.maxValue, cfg.probNonZero),
func(t *testing.T) { runTest(t, cfg) },
)
}

}

0 comments on commit 920763b

Please sign in to comment.