Test FieldBuffer

This commit is contained in:
Asdine El Hrychy
2019-08-29 19:13:30 +01:00
parent a3d23f0d7d
commit afbb55a2af
5 changed files with 191 additions and 72 deletions

11
doc.go
View File

@@ -4,7 +4,7 @@ Genji supports various engines that write data on-disk, like BoltDB or Badger, a
It provides a complete framework with multiple APIs that can be used to manipulate, manage, read and write data. It provides a complete framework with multiple APIs that can be used to manipulate, manage, read and write data.
Genji tables are schemaless and can be manipulated using the table package, which is a low-level functional API Genji tables are schemaless and can be manipulated using the table package, which is a low-level streaming API
or by using the query package which is a powerful SQL like query engine. or by using the query package which is a powerful SQL like query engine.
Tables can be mapped to Go structures without reflection: Genji relies on code generation to translate data to and from Go structures. Tables can be mapped to Go structures without reflection: Genji relies on code generation to translate data to and from Go structures.
@@ -49,8 +49,7 @@ Field, Record, and Table
Genji defines its own semantic to describe data. Genji defines its own semantic to describe data.
Data stored in Genji being schemaless, the usual SQL triplet "column", "row", "table" wasn't chosen Data stored in Genji being schemaless, the usual SQL triplet "column", "row", "table" wasn't chosen
for the vocabulary of this library. Also, Genji is a database written in Go for Go, and its primary goal for the vocabulary of this library. Also, Genji is a database written in Go for Go, and its primary goal
is to map structures and maps to tables, though it's not limited to that. The semantics were to be as close as possible is to map structures and maps to tables, though it's not limited to that.
to what people are used to in this language.
That's why the triplet "field", "record" and "table" was chosen. That's why the triplet "field", "record" and "table" was chosen.
@@ -116,10 +115,8 @@ The genji command line can be used as follows to generate the code:
This will generate a file named user.genji.go containing the following types and methods This will generate a file named user.genji.go containing the following types and methods
// user.genji.go
// The User type gets new methods that implement some Genji interfaces. // The User type gets new methods that implement some Genji interfaces.
func (u *User) Field(name string) (field.Field, error) {} func (u *User) GetField(name string) (field.Field, error) {}
func (u *User) Iterate(fn func(field.Field) error) error {} func (u *User) Iterate(fn func(field.Field) error) error {}
func (u *User) ScanRecord(rec record.Record) error {} func (u *User) ScanRecord(rec record.Record) error {}
func (u *User) PrimaryKey() ([]byte, error) {} func (u *User) PrimaryKey() ([]byte, error) {}
@@ -133,5 +130,7 @@ This will generate a file named user.genji.go containing the following types and
} }
func NewUserFields() UserFields {} func NewUserFields() UserFields {}
The User type now implements all the interfaces needed to interact correctly with the database APIs.
See the examples in this page to see how it can be used.
*/ */
package genji package genji

View File

@@ -25,6 +25,11 @@ type Scanner interface {
// FieldBuffer is slice of fields which implements the Record interface. // FieldBuffer is slice of fields which implements the Record interface.
type FieldBuffer []field.Field type FieldBuffer []field.Field
// NewFieldBuffer creates a FieldBuffer with the given fields.
func NewFieldBuffer(fields ...field.Field) FieldBuffer {
return FieldBuffer(fields)
}
// Add a field to the buffer. // Add a field to the buffer.
func (fb *FieldBuffer) Add(f field.Field) { func (fb *FieldBuffer) Add(f field.Field) {
*fb = append(*fb, f) *fb = append(*fb, f)
@@ -50,10 +55,11 @@ func (fb FieldBuffer) GetField(name string) (field.Field, error) {
} }
// Set replaces a field if it already exists or creates one if not. // Set replaces a field if it already exists or creates one if not.
func (fb FieldBuffer) Set(f field.Field) { func (fb *FieldBuffer) Set(f field.Field) {
for i := range fb { s := *fb
if fb[i].Name == f.Name { for i := range s {
fb[i] = f if s[i].Name == f.Name {
(*fb)[i] = f
return return
} }
} }
@@ -87,7 +93,7 @@ func (fb *FieldBuffer) Delete(name string) error {
return fmt.Errorf("field %q not found", name) return fmt.Errorf("field %q not found", name)
} }
// Replace the field with the given by f. // Replace the field with the given name by f.
func (fb *FieldBuffer) Replace(name string, f field.Field) error { func (fb *FieldBuffer) Replace(name string, f field.Field) error {
s := *fb s := *fb
for i := range s { for i := range s {
@@ -98,7 +104,7 @@ func (fb *FieldBuffer) Replace(name string, f field.Field) error {
} }
} }
return fmt.Errorf("field %q not found", name) return fmt.Errorf("field %q not found", f.Name)
} }
// DumpRecord is helper that dumps the content of a record into the given writer. // DumpRecord is helper that dumps the content of a record into the given writer.

View File

@@ -11,20 +11,120 @@ import (
var _ record.Record = new(record.FieldBuffer) var _ record.Record = new(record.FieldBuffer)
func TestFieldBuffer(t *testing.T) { func TestFieldBuffer(t *testing.T) {
b := record.FieldBuffer([]field.Field{ buf := record.NewFieldBuffer(
field.NewInt64("a", 10), field.NewInt64("a", 10),
field.NewString("b", "hello"), field.NewString("b", "hello"),
)
t.Run("Iterate", func(t *testing.T) {
var i int
err := buf.Iterate(func(f field.Field) error {
require.NotEmpty(t, f)
require.Equal(t, f, buf[i])
i++
return nil
})
require.NoError(t, err)
require.Equal(t, 2, i)
}) })
var i int t.Run("Add", func(t *testing.T) {
err := b.Iterate(func(f field.Field) error { buf := record.NewFieldBuffer(
require.NotEmpty(t, f) field.NewInt64("a", 10),
require.Equal(t, f, b[i]) field.NewString("b", "hello"),
i++ )
return nil
c := field.NewBool("c", true)
buf.Add(c)
require.Len(t, buf, 3)
require.Equal(t, buf[2], c)
})
t.Run("ScanRecord", func(t *testing.T) {
buf1 := record.NewFieldBuffer(
field.NewInt64("a", 10),
field.NewString("b", "hello"),
)
buf2 := record.NewFieldBuffer(
field.NewInt64("a", 20),
field.NewString("b", "bye"),
field.NewBool("c", true),
)
err := buf1.ScanRecord(buf2)
require.NoError(t, err)
require.Equal(t, record.NewFieldBuffer(
field.NewInt64("a", 10),
field.NewString("b", "hello"),
field.NewInt64("a", 20),
field.NewString("b", "bye"),
field.NewBool("c", true),
), buf1)
})
t.Run("GetField", func(t *testing.T) {
f, err := buf.GetField("a")
require.NoError(t, err)
require.Equal(t, field.NewInt64("a", 10), f)
f, err = buf.GetField("not existing")
require.Error(t, err)
require.Zero(t, f)
})
t.Run("Set", func(t *testing.T) {
buf1 := record.NewFieldBuffer(
field.NewInt64("a", 10),
field.NewString("b", "hello"),
)
buf1.Set(field.NewInt64("a", 11))
require.Equal(t, field.NewInt64("a", 11), buf1[0])
buf1.Set(field.NewInt64("c", 12))
require.Len(t, buf1, 3)
require.Equal(t, field.NewInt64("c", 12), buf1[2])
})
t.Run("Delete", func(t *testing.T) {
buf1 := record.NewFieldBuffer(
field.NewInt64("a", 10),
field.NewString("b", "hello"),
)
err := buf1.Delete("a")
require.NoError(t, err)
require.Len(t, buf1, 1)
require.Equal(t, record.NewFieldBuffer(
field.NewString("b", "hello"),
), buf1)
err = buf1.Delete("b")
require.NoError(t, err)
require.Len(t, buf1, 0)
err = buf1.Delete("b")
require.Error(t, err)
})
t.Run("Replace", func(t *testing.T) {
buf1 := record.NewFieldBuffer(
field.NewInt64("a", 10),
field.NewString("b", "hello"),
)
err := buf1.Replace("a", field.NewInt64("c", 10))
require.NoError(t, err)
require.Equal(t, record.NewFieldBuffer(
field.NewInt64("c", 10),
field.NewString("b", "hello"),
), buf1)
err = buf1.Replace("d", field.NewInt64("c", 11))
require.Error(t, err)
}) })
require.NoError(t, err)
require.Equal(t, 2, i)
} }
func TestNewFromMap(t *testing.T) { func TestNewFromMap(t *testing.T) {

View File

@@ -1,9 +1,8 @@
package genji_test package table_test
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"log"
"testing" "testing"
"time" "time"
@@ -12,32 +11,12 @@ import (
"github.com/asdine/genji/field" "github.com/asdine/genji/field"
"github.com/asdine/genji/record" "github.com/asdine/genji/record"
"github.com/asdine/genji/table" "github.com/asdine/genji/table"
"github.com/asdine/genji/table/tabletest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func tableBuilder(t testing.TB) func() (table.Table, func()) { var _ table.Reader = (*genji.Table)(nil)
return func() (table.Table, func()) {
db, err := genji.New(memory.NewEngine())
require.NoError(t, err)
tx, err := db.Begin(true) func TestDump(t *testing.T) {
require.NoError(t, err)
tb, err := tx.CreateTable("test")
require.NoError(t, err)
return tb, func() {
tx.Rollback()
}
}
}
func TestTable(t *testing.T) {
tabletest.TestSuite(t, tableBuilder(t))
}
func TestTableString(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
expected string expected string
@@ -79,30 +58,3 @@ name(String): "John 2", age(Int): 12
}) })
} }
} }
func ExampleDB() {
ng := memory.NewEngine()
db, err := genji.New(ng)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Update(func(tx *genji.Tx) error {
t, err := tx.CreateTable("Table")
if err != nil {
return err
}
r := record.FieldBuffer{
field.NewString("Name", "foo"),
field.NewInt("Age", 10),
}
_, err = t.Insert(r)
return err
})
if err != nil {
log.Fatal(err)
}
}

62
table_test.go Normal file
View File

@@ -0,0 +1,62 @@
package genji_test
import (
"log"
"testing"
"github.com/asdine/genji"
"github.com/asdine/genji/engine/memory"
"github.com/asdine/genji/field"
"github.com/asdine/genji/record"
"github.com/asdine/genji/table"
"github.com/asdine/genji/table/tabletest"
"github.com/stretchr/testify/require"
)
func tableBuilder(t testing.TB) func() (table.Table, func()) {
return func() (table.Table, func()) {
db, err := genji.New(memory.NewEngine())
require.NoError(t, err)
tx, err := db.Begin(true)
require.NoError(t, err)
tb, err := tx.CreateTable("test")
require.NoError(t, err)
return tb, func() {
tx.Rollback()
}
}
}
func TestTable(t *testing.T) {
tabletest.TestSuite(t, tableBuilder(t))
}
func ExampleDB() {
ng := memory.NewEngine()
db, err := genji.New(ng)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Update(func(tx *genji.Tx) error {
t, err := tx.CreateTable("Table")
if err != nil {
return err
}
r := record.FieldBuffer{
field.NewString("Name", "foo"),
field.NewInt("Age", 10),
}
_, err = t.Insert(r)
return err
})
if err != nil {
log.Fatal(err)
}
}