Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

185 lines
4.9 KiB
Go

// Copyright 2021 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tree
import (
"context"
"fmt"
"testing"
"github.com/dolthub/go-mysql-server/sql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/store/prolly/message"
"github.com/dolthub/dolt/go/store/val"
)
func TestNodeCursor(t *testing.T) {
t.Run("new cursor at item", func(t *testing.T) {
testNewCursorAtItem(t, 10)
testNewCursorAtItem(t, 100)
testNewCursorAtItem(t, 1000)
testNewCursorAtItem(t, 10_000)
})
t.Run("get ordinal at item", func(t *testing.T) {
counts := []int{10, 100, 1000, 10_000}
for _, c := range counts {
t.Run(fmt.Sprintf("%d", c), func(t *testing.T) {
testGetOrdinalOfCursor(t, c)
})
}
})
t.Run("retreat past beginning", func(t *testing.T) {
ctx := sql.NewEmptyContext()
root, _, ns := randomTree(t, 10_000)
assert.NotNil(t, root)
before, err := newCursorAtStart(ctx, ns, root)
assert.NoError(t, err)
err = before.retreat(ctx)
assert.NoError(t, err)
assert.False(t, before.Valid())
start, err := newCursorAtStart(ctx, ns, root)
assert.NoError(t, err)
assert.True(t, start.compare(before) > 0, "start is after before")
assert.True(t, before.compare(start) < 0, "before is before start")
// Backwards iteration...
end, err := newCursorAtEnd(ctx, ns, root)
assert.NoError(t, err)
i := 0
for end.compare(before) > 0 {
i++
err = end.retreat(ctx)
assert.NoError(t, err)
}
assert.Equal(t, 10_000/2, i)
})
}
func testNewCursorAtItem(t *testing.T, count int) {
root, items, ns := randomTree(t, count)
assert.NotNil(t, root)
ctx := sql.NewEmptyContext()
for i := range items {
key, value := items[i][0], items[i][1]
cur, err := newCursorAtKey(ctx, ns, root, val.Tuple(key), keyDesc)
require.NoError(t, err)
assert.Equal(t, key, cur.CurrentKey())
assert.Equal(t, value, cur.currentValue())
}
validateTreeItems(t, ns, root, items)
}
func testGetOrdinalOfCursor(t *testing.T, count int) {
tuples, desc := AscendingUintTuples(count)
ctx := sql.NewEmptyContext()
ns := NewTestNodeStore()
serializer := message.NewProllyMapSerializer(desc, ns.Pool())
chkr, err := newEmptyChunker(ctx, ns, serializer)
require.NoError(t, err)
for _, item := range tuples {
err = chkr.AddPair(ctx, Item(item[0]), Item(item[1]))
assert.NoError(t, err)
}
nd, err := chkr.Done(ctx)
assert.NoError(t, err)
for i := 0; i < len(tuples); i++ {
curr, err := newCursorAtKey(ctx, ns, nd, tuples[i][0], desc)
require.NoError(t, err)
ord, err := getOrdinalOfCursor(curr)
require.NoError(t, err)
assert.Equal(t, uint64(i), ord)
}
b := val.NewTupleBuilder(desc, ns)
b.PutUint32(0, uint32(len(tuples)))
aboveItem, err := b.Build(context.Background(), sharedPool)
require.NoError(t, err)
curr, err := newCursorAtKey(ctx, ns, nd, aboveItem, desc)
require.NoError(t, err)
ord, err := getOrdinalOfCursor(curr)
require.NoError(t, err)
require.Equal(t, uint64(len(tuples)), ord)
// A cursor past the end should return an ordinal count equal to number of
// nodes.
curr, err = newCursorPastEnd(ctx, ns, nd)
require.NoError(t, err)
ord, err = getOrdinalOfCursor(curr)
require.NoError(t, err)
require.Equal(t, uint64(len(tuples)), ord)
}
func randomTree(t *testing.T, count int) (*Node, [][2]Item, NodeStore) {
ctx := sql.NewEmptyContext()
ns := NewTestNodeStore()
serializer := message.NewProllyMapSerializer(valDesc, ns.Pool())
chkr, err := newEmptyChunker(ctx, ns, serializer)
require.NoError(t, err)
items := randomTupleItemPairs(count/2, ns)
for _, item := range items {
err = chkr.AddPair(ctx, Item(item[0]), Item(item[1]))
assert.NoError(t, err)
}
nd, err := chkr.Done(ctx)
assert.NoError(t, err)
return nd, items, ns
}
var keyDesc = val.NewTupleDescriptor(
val.Type{Enc: val.Int64Enc, Nullable: false},
)
var valDesc = val.NewTupleDescriptor(
val.Type{Enc: val.Int64Enc, Nullable: true},
val.Type{Enc: val.Int64Enc, Nullable: true},
val.Type{Enc: val.Int64Enc, Nullable: true},
val.Type{Enc: val.Int64Enc, Nullable: true},
)
func randomTupleItemPairs(count int, ns NodeStore) (items [][2]Item) {
ctx := sql.NewEmptyContext()
tups, err := RandomTuplePairs(ctx, count, keyDesc, valDesc, ns)
if err != nil {
panic(err)
}
items = make([][2]Item, count)
if len(tups) != len(items) {
panic("mismatch")
}
for i := range items {
items[i][0] = Item(tups[i][0])
items[i][1] = Item(tups[i][1])
}
return
}