Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor/test #9

Merged
merged 13 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ on:

jobs:
test:
permissions:
contents: write
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: ./go.mod
- name: Generate mocks
run: |
go install go.uber.org/mock/mockgen@v0.4.0
make mockgen
- name: Setup
run: make setup
- name: Test
run: make test

Expand Down
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ lint:
clean:
rm -rf .coverage
rm -rf ./pkg/**/mock
rm -rf .tmp/**
setup:
make import-tools
make mockgen
coverage:
mkdir -p .coverage
go test -coverprofile=.coverage/coverage.out $(PKG)
Expand All @@ -23,3 +27,6 @@ mockgen:
done' sh {} +
fmt:
go fmt $(PKG)
import-tools:
go install gotest.tools/gotestsum@v1.12.0
go install go.uber.org/mock/mockgen@v0.4.0
39 changes: 30 additions & 9 deletions pkg/buffer/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ import (
"github.com/kj455/db/pkg/log"
)

const (
INIT_TX_NUM = -1
INIT_LSN = -1
)

type ReadPage interface {
GetInt(offset int) uint32
GetBytes(offset int) []byte
GetString(offset int) string
}

type WritePage interface {
SetInt(offset int, value uint32)
SetBytes(offset int, value []byte)
SetString(offset int, value string)
}

type ReadWritePage interface {
ReadPage
WritePage
}

type BufferImpl struct {
fileMgr file.FileMgr
logMgr log.LogMgr
Expand All @@ -22,18 +44,17 @@ func NewBuffer(fm file.FileMgr, lm log.LogMgr, blockSize int) *BufferImpl {
fileMgr: fm,
logMgr: lm,
contents: file.NewPage(blockSize),
block: nil,
pins: 0,
txNum: -1,
lsn: -1,
txNum: INIT_TX_NUM,
lsn: INIT_LSN,
}
}

func (b *BufferImpl) Contents() file.ReadPage {
func (b *BufferImpl) Contents() ReadPage {
return b.contents
}

func (b *BufferImpl) WriteContents(txNum, lsn int, write func(p file.ReadWritePage)) {
func (b *BufferImpl) WriteContents(txNum, lsn int, write func(p ReadWritePage)) {
b.setModified(txNum, lsn)
write(b.contents)
}
Expand All @@ -52,7 +73,7 @@ func (b *BufferImpl) ModifyingTx() int {

func (b *BufferImpl) AssignToBlock(block file.BlockId) error {
if err := b.Flush(); err != nil {
return fmt.Errorf("buffer: failed to flush: %w", err)
return err
}
if err := b.fileMgr.Read(block, b.contents); err != nil {
return fmt.Errorf("buffer: failed to read block: %w", err)
Expand All @@ -63,7 +84,7 @@ func (b *BufferImpl) AssignToBlock(block file.BlockId) error {
}

func (b *BufferImpl) Flush() error {
if b.txNum < 0 {
if b.txNum == INIT_TX_NUM {
return nil
}
if err := b.logMgr.Flush(b.lsn); err != nil {
Expand All @@ -72,7 +93,7 @@ func (b *BufferImpl) Flush() error {
if err := b.fileMgr.Write(b.block, b.contents); err != nil {
return fmt.Errorf("buffer: failed to write block: %w", err)
}
b.txNum = -1
b.txNum = INIT_TX_NUM
return nil
}

Expand All @@ -86,7 +107,7 @@ func (b *BufferImpl) Unpin() {

func (b *BufferImpl) setModified(txNum, lsn int) {
b.txNum = txNum
if lsn >= 0 {
if lsn > INIT_LSN {
b.lsn = lsn
}
}
203 changes: 91 additions & 112 deletions pkg/buffer/buffer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,130 +4,109 @@ import (
"testing"

"github.com/kj455/db/pkg/file"
fmock "github.com/kj455/db/pkg/file/mock"
lmock "github.com/kj455/db/pkg/log/mock"
tmock "github.com/kj455/db/pkg/time/mock"
"github.com/kj455/db/pkg/log"
"github.com/kj455/db/pkg/testutil"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
)

type mocks struct {
fileMgr *fmock.MockFileMgr
page *fmock.MockPage
block *fmock.MockBlockId
logMgr *lmock.MockLogMgr
time *tmock.MockTime
}

func newMocks(ctrl *gomock.Controller) *mocks {
return &mocks{
fileMgr: fmock.NewMockFileMgr(ctrl),
page: fmock.NewMockPage(ctrl),
block: fmock.NewMockBlockId(ctrl),
logMgr: lmock.NewMockLogMgr(ctrl),
time: tmock.NewMockTime(ctrl),
}
}

func TestNewBuffer(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

fm := fmock.NewMockFileMgr(ctrl)
lm := lmock.NewMockLogMgr(ctrl)

b := NewBuffer(fm, lm, 400)

assert.NotNil(t, b)
assert.Equal(t, fm, b.fileMgr)
assert.Equal(t, lm, b.logMgr)
assert.NotNil(t, b.contents)
assert.Nil(t, b.block)
assert.Equal(t, 0, b.pins)
assert.Equal(t, -1, b.txNum)
assert.Equal(t, -1, b.lsn)
}

func TestBuffer_IsPinned(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

fm := fmock.NewMockFileMgr(ctrl)
lm := lmock.NewMockLogMgr(ctrl)

b := NewBuffer(fm, lm, 400)

assert.False(t, b.IsPinned())
b.pins++
assert.True(t, b.IsPinned())
}

func TestBuffer_WriteContents(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

fm := fmock.NewMockFileMgr(ctrl)
lm := lmock.NewMockLogMgr(ctrl)

b := NewBuffer(fm, lm, 400)

b.WriteContents(1, 2, func(p file.ReadWritePage) {
p.SetInt(0, 1)
const (
blockSize = 400
logFileName = "test_buffer_write_contents"
txNum = 1
lsn = 2
)
dir, _, cleanup := testutil.SetupFile(logFileName)
defer cleanup()
fileMgr := file.NewFileMgr(dir, blockSize)
logMgr, err := log.NewLogMgr(fileMgr, logFileName)
assert.NoError(t, err)
buf := NewBuffer(fileMgr, logMgr, blockSize)

buf.WriteContents(txNum, lsn, func(p ReadWritePage) {
p.SetInt(100, 200)
})
assert.Equal(t, uint32(1), b.contents.GetInt(0))
assert.Equal(t, 1, b.txNum)
assert.Equal(t, 2, b.lsn)

assert.Equal(t, uint32(200), buf.contents.GetInt(100))
assert.Equal(t, txNum, buf.ModifyingTx())
assert.Equal(t, lsn, buf.lsn)
}

func TestBuffer_AssignToBlock(t *testing.T) {
func TestBuffer_Flush(t *testing.T) {
t.Parallel()
const (
blockSize = 400
tx = 1
lsn = 2
)
tests := []struct {
name string
setup func(m *mocks, b *BufferImpl)
expect func(res error, b *BufferImpl)
}{
{
name: "assign",
setup: func(m *mocks, b *BufferImpl) {
m.fileMgr.EXPECT().Read(m.block, gomock.Any()).Return(nil)
},
expect: func(res error, b *BufferImpl) {
assert.Nil(t, res)
assert.Equal(t, 0, b.pins)
assert.Equal(t, -1, b.txNum)
},
},
{
name: "flush and assign",
setup: func(m *mocks, b *BufferImpl) {
b.txNum = tx
b.lsn = lsn
m.logMgr.EXPECT().Flush(lsn).Return(nil)
m.fileMgr.EXPECT().Write(nil, gomock.Any()).Return(nil)
m.fileMgr.EXPECT().Read(m.block, gomock.Any()).Return(nil)
},
expect: func(res error, b *BufferImpl) {
assert.Nil(t, res)
assert.Equal(t, 0, b.pins)
assert.Equal(t, -1, b.txNum)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
t.Run("skip flush", func(t *testing.T) {
t.Parallel()
const logFileName = "test_buffer_flush_skip"
dir, _, cleanup := testutil.SetupFile(logFileName)
defer cleanup()
fileMgr := file.NewFileMgr(dir, blockSize)
logMgr, err := log.NewLogMgr(fileMgr, logFileName)
assert.NoError(t, err)
buf := NewBuffer(fileMgr, logMgr, blockSize)

buf.Flush()

assert.Equal(t, INIT_TX_NUM, buf.ModifyingTx())
assert.Equal(t, INIT_LSN, buf.lsn)
})
t.Run("flush", func(t *testing.T) {
t.Parallel()
const logFileName = "test_buffer_flush"
dir, _, cleanup := testutil.SetupFile(logFileName)
defer cleanup()
fileMgr := file.NewFileMgr(dir, blockSize)
logMgr, err := log.NewLogMgr(fileMgr, logFileName)
assert.NoError(t, err)
buf := NewBuffer(fileMgr, logMgr, blockSize)
buf.block = file.NewBlockId(logFileName, 0)
// setup not flushed buffer
buf.logMgr.Append([]byte("test"))
buf.WriteContents(tx, lsn, func(p ReadWritePage) {
p.SetInt(100, 200)
})

m := newMocks(ctrl)
b := NewBuffer(m.fileMgr, m.logMgr, blockSize)
tt.setup(m, b)
buf.Flush()

err := b.AssignToBlock(m.block)
tt.expect(err, b)
})
}
assert.Equal(t, INIT_TX_NUM, buf.ModifyingTx())
assert.Equal(t, lsn, buf.lsn)
iter, err := logMgr.Iterator()
assert.NoError(t, err)
assert.True(t, iter.HasNext())
record, err := iter.Next()
assert.NoError(t, err)
assert.Equal(t, []byte("test"), record)
})
}

func TestBuffer_AssignToBlock(t *testing.T) {
const (
blockSize = 400
blockNum = 0
tx = 1
lsn = 2
logFileName = "test_buffer_assign_to_block"
)
dir, _, cleanup := testutil.SetupFile(logFileName)
defer cleanup()
fileMgr := file.NewFileMgr(dir, blockSize)
logMgr, err := log.NewLogMgr(fileMgr, logFileName)
assert.NoError(t, err)
buf := NewBuffer(fileMgr, logMgr, blockSize)
buf.pins = 99
// setup block content
page := file.NewPage(blockSize)
page.SetInt(100, 200)
block := file.NewBlockId(logFileName, blockNum)
fileMgr.Write(block, page)

buf.AssignToBlock(block)

assert.Equal(t, block, buf.Block())
assert.Equal(t, 0, buf.pins)
assert.Equal(t, uint32(200), buf.Contents().GetInt(100))
}
4 changes: 2 additions & 2 deletions pkg/buffer/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import "github.com/kj455/db/pkg/file"
type Buffer interface {
Block() file.BlockId
IsPinned() bool
Contents() file.ReadPage
WriteContents(txNum, lsn int, write func(p file.ReadWritePage))
Contents() ReadPage
WriteContents(txNum, lsn int, write func(p ReadWritePage))
ModifyingTx() int
AssignToBlock(block file.BlockId) error
Flush() error
Expand Down
Loading
Loading