Commit b6e2b10f authored by Daniel Martí's avatar Daniel Martí

node/tests: add more extensive scalar kind tests

It covers AssignKind, AssignNode, and AsKind for every combination of
assembler kind and method.

We also verify that a constructed scalar node behaves the same with
AsKind when using its representation, like the old test.

There's effectively a triple loop as a test table, so the subtest name
has up to three components separated by dashes, such as:

	TestSchema/Scalars/Bytes-AssignNode-String

We also use this test as a demo of quicktest instead of go-wish.

Finally, adapt bindnode to pass these tests just like codegen. This was
mainly a bunch of TODOs in the relevant methods.
parent 6042d4d8
......@@ -82,10 +82,16 @@ func fieldNameFromSchema(name string) string {
func inferGoType(typ schema.Type) reflect.Type {
switch typ := typ.(type) {
case *schema.TypeBool:
return goTypeBool
case *schema.TypeInt:
return goTypeInt
case *schema.TypeFloat:
return goTypeFloat
case *schema.TypeString:
return goTypeString
case *schema.TypeBytes:
return goTypeBytes
case *schema.TypeStruct:
fields := typ.Fields()
goFields := make([]reflect.StructField, len(fields))
......@@ -216,8 +222,10 @@ func (w *_prototype) Representation() ipld.NodePrototype {
}
var (
goTypeString = reflect.TypeOf("")
goTypeBool = reflect.TypeOf(false)
goTypeInt = reflect.TypeOf(int(0))
goTypeFloat = reflect.TypeOf(0.0)
goTypeString = reflect.TypeOf("")
goTypeBytes = reflect.TypeOf([]byte{})
goTypeLink = reflect.TypeOf((*ipld.Link)(nil)).Elem()
......@@ -296,9 +304,7 @@ func (w *_node) LookupByString(key string) (ipld.Node, error) {
}
fval := valuesVal.MapIndex(kval)
if !fval.IsValid() { // not found
return nil, ipld.ErrNotExists{
// TODO
}
return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfString(key)}
}
// TODO: Error/panic if fval.IsNil() && !typ.ValueIsNullable()?
// Otherwise we could have two non-equal Go values (nil map,
......@@ -326,15 +332,11 @@ func (w *_node) LookupByString(key string) (ipld.Node, error) {
}
}
if mtyp == nil { // not found
return nil, ipld.ErrNotExists{
// TODO
}
return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfString(key)}
}
haveIdx := int(w.val.FieldByName("Index").Int())
if haveIdx != idx { // mismatching type
return nil, ipld.ErrNotExists{
// TODO
}
return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfString(key)}
}
mval := w.val.FieldByName("Value").Elem()
node := &_node{
......@@ -358,11 +360,8 @@ func (w *_node) LookupByIndex(idx int64) (ipld.Node, error) {
switch typ := w.schemaType.(type) {
case *schema.TypeList:
if idx < 0 || int(idx) >= w.val.Len() {
return nil, ipld.ErrNotExists{
// TODO
}
return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfInt(idx)}
}
// TODO: bounds check
val := w.val.Index(int(idx))
if typ.ValueIsNullable() {
if val.IsNil() {
......@@ -448,7 +447,14 @@ func (w *_node) IsNull() bool {
}
func (w *_node) AsBool() (bool, error) {
panic("TODO: ")
if w.Kind() != ipld.Kind_Bool {
return false, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(),
MethodName: "AsBool",
// TODO
}
}
return w.val.Bool(), nil
}
func (w *_node) AsInt() (int64, error) {
......@@ -463,7 +469,14 @@ func (w *_node) AsInt() (int64, error) {
}
func (w *_node) AsFloat() (float64, error) {
panic("TODO: ")
if w.Kind() != ipld.Kind_Float {
return 0, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(),
MethodName: "AsFloat",
// TODO
}
}
return w.val.Float(), nil
}
func (w *_node) AsString() (string, error) {
......@@ -509,6 +522,7 @@ type _builder struct {
}
func (w *_builder) Build() ipld.Node {
// TODO: should we panic if no Assign call was made, just like codegen?
return &_node{schemaType: w.schemaType, val: w.val}
}
......@@ -615,8 +629,22 @@ func (w *_assembler) AssignNull() error {
return nil
}
func (w *_assembler) AssignBool(bool) error {
panic("TODO: AssignBool")
func (w *_assembler) AssignBool(b bool) error {
if w.kind() != ipld.Kind_Bool {
return ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(),
MethodName: "AssignBool",
AppropriateKind: ipld.KindSet{ipld.Kind_Bool},
ActualKind: w.kind(),
}
}
w.nonPtrVal().SetBool(b)
if w.finish != nil {
if err := w.finish(); err != nil {
return err
}
}
return nil
}
func (w *_assembler) AssignInt(i int64) error {
......@@ -637,8 +665,22 @@ func (w *_assembler) AssignInt(i int64) error {
return nil
}
func (w *_assembler) AssignFloat(float64) error {
panic("TODO: AssignFloat")
func (w *_assembler) AssignFloat(f float64) error {
if w.kind() != ipld.Kind_Float {
return ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(),
MethodName: "AssignFloat",
AppropriateKind: ipld.KindSet{ipld.Kind_Float},
ActualKind: w.kind(),
}
}
w.nonPtrVal().SetFloat(f)
if w.finish != nil {
if err := w.finish(); err != nil {
return err
}
}
return nil
}
func (w *_assembler) AssignString(s string) error {
......@@ -736,12 +778,24 @@ func (w *_assembler) AssignNode(node ipld.Node) error {
}
return am.Finish()
case ipld.Kind_Bool:
b, err := node.AsBool()
if err != nil {
return err
}
return w.AssignBool(b)
case ipld.Kind_Int:
i, err := node.AsInt()
if err != nil {
return err
}
return w.AssignInt(i)
case ipld.Kind_Float:
f, err := node.AsFloat()
if err != nil {
return err
}
return w.AssignFloat(f)
case ipld.Kind_String:
s, err := node.AsString()
if err != nil {
......
......@@ -152,8 +152,10 @@ func (w *_nodeRepr) LookupByString(key string) (ipld.Node, error) {
func (w *_nodeRepr) LookupByNode(key ipld.Node) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(),
MethodName: "LookupByNode", AppropriateKind: ipld.KindSet_JustList, ActualKind: ipld.Kind_Map,
TypeName: w.schemaType.Name().String(),
MethodName: "LookupByNode",
AppropriateKind: ipld.KindSet_JustList,
// TODO
}
}
......@@ -240,31 +242,52 @@ func (w *_nodeRepr) Length() int64 {
}
func (w *_nodeRepr) IsAbsent() bool {
if reprStrategy(w.schemaType) == nil {
return (*_node)(w).IsAbsent()
}
return false
}
func (w *_nodeRepr) IsNull() bool {
if reprStrategy(w.schemaType) == nil {
return (*_node)(w).IsNull()
}
return false
}
func (w *_nodeRepr) AsBool() (bool, error) {
if reprStrategy(w.schemaType) == nil {
return (*_node)(w).AsBool()
}
return false, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(),
MethodName: "AsBool", AppropriateKind: ipld.KindSet_JustBool, ActualKind: ipld.Kind_Map,
TypeName: w.schemaType.Name().String(),
MethodName: "AsBool",
AppropriateKind: ipld.KindSet_JustBool,
// TODO
}
}
func (w *_nodeRepr) AsInt() (int64, error) {
if reprStrategy(w.schemaType) == nil {
return (*_node)(w).AsInt()
}
return 0, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(), MethodName: "AsInt",
AppropriateKind: ipld.KindSet_JustInt, ActualKind: ipld.Kind_Map,
TypeName: w.schemaType.Name().String(),
MethodName: "AsInt",
AppropriateKind: ipld.KindSet_JustInt,
// TODO
}
}
func (w *_nodeRepr) AsFloat() (float64, error) {
if reprStrategy(w.schemaType) == nil {
return (*_node)(w).AsFloat()
}
return 0, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(), MethodName: "AsFloat",
AppropriateKind: ipld.KindSet_JustFloat, ActualKind: ipld.Kind_Map,
TypeName: w.schemaType.Name().String(),
MethodName: "AsFloat",
AppropriateKind: ipld.KindSet_JustFloat,
// TODO
}
}
......@@ -314,16 +337,26 @@ func (w *_nodeRepr) AsString() (string, error) {
}
func (w *_nodeRepr) AsBytes() ([]byte, error) {
if reprStrategy(w.schemaType) == nil {
return (*_node)(w).AsBytes()
}
return nil, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(), MethodName: "AsBytes",
AppropriateKind: ipld.KindSet_JustBytes, ActualKind: ipld.Kind_Map,
TypeName: w.schemaType.Name().String(),
MethodName: "AsBytes",
AppropriateKind: ipld.KindSet_JustBytes,
// TODO
}
}
func (w *_nodeRepr) AsLink() (ipld.Link, error) {
if reprStrategy(w.schemaType) == nil {
return (*_node)(w).AsLink()
}
return nil, ipld.ErrWrongKind{
TypeName: w.schemaType.Name().String(), MethodName: "AsLink",
AppropriateKind: ipld.KindSet_JustLink, ActualKind: ipld.Kind_Map,
TypeName: w.schemaType.Name().String(),
MethodName: "AsLink",
AppropriateKind: ipld.KindSet_JustLink,
// TODO
}
}
......
......@@ -13,7 +13,7 @@ var allSchemaTests = []struct {
{"MapsContainingMaybe", SchemaTestMapsContainingMaybe},
{"MapsContainingMaps", SchemaTestMapsContainingMaps},
{"MapsWithComplexKeys", SchemaTestMapsWithComplexKeys},
{"String", SchemaTestString},
{"Scalars", SchemaTestScalars},
{"RequiredFields", SchemaTestRequiredFields},
{"StructNesting", SchemaTestStructNesting},
{"StructReprStringjoin", SchemaTestStructReprStringjoin},
......
package tests
import (
"fmt"
"testing"
qt "github.com/frankban/quicktest"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
func assignValue(am ipld.NodeAssembler, value interface{}) error {
switch value := value.(type) {
case bool:
return am.AssignBool(value)
case int64:
return am.AssignInt(value)
case float64:
return am.AssignFloat(value)
case string:
return am.AssignString(value)
case []byte:
return am.AssignBytes(value)
default:
panic(fmt.Sprintf("%T", value))
}
}
func SchemaTestScalars(t *testing.T, engine Engine) {
ts := schema.TypeSystem{}
ts.Init()
ts.Accumulate(schema.SpawnBool("Bool"))
ts.Accumulate(schema.SpawnInt("Int"))
ts.Accumulate(schema.SpawnFloat("Float"))
ts.Accumulate(schema.SpawnString("String"))
ts.Accumulate(schema.SpawnBytes("Bytes"))
engine.Init(t, ts)
var tests = []struct {
name string
kind ipld.Kind
value interface{}
}{
{"Bool", ipld.Kind_Bool, true},
{"Int", ipld.Kind_Int, int64(23)},
{"Float", ipld.Kind_Float, 12.25},
{"String", ipld.Kind_String, "foo"},
{"Bytes", ipld.Kind_Bytes, []byte("bar")},
}
// We test each of the five scalar prototypes in subtests.
for _, testProto := range tests {
np := engine.PrototypeByName(testProto.name)
// For each prototype, we try assigning all scalar values.
for _, testAssign := range tests {
// We try both AssignKind and AssignNode.
for _, useAssignNode := range []bool{false, true} {
testName := fmt.Sprintf("%s-Assign%s", testProto.name, testAssign.name)
if useAssignNode {
testName = fmt.Sprintf("%s-AssignNode-%s", testProto.name, testAssign.name)
}
t.Run(testName, func(t *testing.T) {
nb := np.NewBuilder()
// Assigning the right value for the kind should succeed.
var err error
if useAssignNode {
np2 := engine.PrototypeByName(testAssign.name)
nb2 := np2.NewBuilder()
qt.Assert(t, assignValue(nb2, testAssign.value), qt.IsNil)
n2 := nb2.Build()
err = nb.AssignNode(n2)
} else {
err = assignValue(nb, testAssign.value)
}
if testAssign.kind == testProto.kind {
qt.Assert(t, err, qt.IsNil)
} else {
qt.Assert(t, err, qt.Not(qt.IsNil))
// Assign something anyway, just so we can Build later.
err := assignValue(nb, testProto.value)
qt.Assert(t, err, qt.IsNil)
}
n := nb.Build()
// For both the regular node and its repr version,
// getting the right value for the kind should work.
for _, n := range []ipld.Node{
n,
n.(schema.TypedNode).Representation(),
} {
var gotValue interface{}
err = nil
switch testAssign.kind {
case ipld.Kind_Bool:
gotValue, err = n.AsBool()
case ipld.Kind_Int:
gotValue, err = n.AsInt()
case ipld.Kind_Float:
gotValue, err = n.AsFloat()
case ipld.Kind_String:
gotValue, err = n.AsString()
case ipld.Kind_Bytes:
gotValue, err = n.AsBytes()
default:
t.Fatal(testAssign.kind)
}
if testAssign.kind == testProto.kind {
qt.Assert(t, err, qt.IsNil)
qt.Assert(t, gotValue, qt.DeepEquals, testAssign.value)
} else {
qt.Assert(t, err, qt.Not(qt.IsNil))
}
}
})
}
}
}
}
package tests
import (
"testing"
. "github.com/warpfork/go-wish"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
func SchemaTestString(t *testing.T, engine Engine) {
ts := schema.TypeSystem{}
ts.Init()
ts.Accumulate(schema.SpawnString("String"))
engine.Init(t, ts)
np := engine.PrototypeByName("String")
t.Run("create string", func(t *testing.T) {
nb := np.NewBuilder()
Wish(t, nb.AssignString("woiu"), ShouldEqual, nil)
n := nb.Build().(schema.TypedNode)
t.Run("read string", func(t *testing.T) {
Wish(t, n.Kind(), ShouldEqual, ipld.Kind_String)
})
t.Run("read representation", func(t *testing.T) {
nr := n.Representation()
Wish(t, nr.Kind(), ShouldEqual, ipld.Kind_String)
Wish(t, str(nr), ShouldEqual, "woiu")
})
})
t.Run("create null is rejected", func(t *testing.T) {
nb := np.NewBuilder()
Wish(t, nb.AssignNull(), ShouldBeSameTypeAs, ipld.ErrWrongKind{})
})
}
......@@ -6,9 +6,9 @@ import (
"github.com/ipld/go-ipld-prime/node/tests"
)
func TestString(t *testing.T) {
func TestScalars(t *testing.T) {
t.Parallel()
engine := &genAndCompileEngine{prefix: "string"}
tests.SchemaTestString(t, engine)
engine := &genAndCompileEngine{prefix: "scalars"}
tests.SchemaTestScalars(t, engine)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment