blob: 935ca491db9c8c18f56fb8d2f4dba28b6eab4905 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vdl
import (
"reflect"
"sync"
"testing"
)
var NUnionWant = []*reflectInfo{{
Type: reflect.TypeOf((*NUnionABC)(nil)).Elem(),
Name: "v.io/v23/vdl.NUnionABC",
UnionFields: []reflectField{
{"A", reflect.TypeOf(false), reflect.TypeOf(NUnionABCA{})},
{"B", reflect.TypeOf(string("")), reflect.TypeOf(NUnionABCB{})},
{"C", reflect.TypeOf(NStructInt64{}), reflect.TypeOf(NUnionABCC{})},
},
}}
var reflectInfoTests = []struct {
rt reflect.Type
ri []*reflectInfo
}{
{reflect.TypeOf(int64(0)), []*reflectInfo{{Type: reflect.TypeOf(int64(0))}}},
{reflect.TypeOf(string("")), []*reflectInfo{{Type: reflect.TypeOf(string(""))}}},
{reflect.TypeOf([]byte{}), []*reflectInfo{{Type: reflect.TypeOf([]byte{})}}},
{
reflect.TypeOf(NEnumA),
[]*reflectInfo{{
Type: reflect.TypeOf(NEnumA),
Name: "v.io/v23/vdl.NEnum",
EnumLabels: []string{"A", "B", "C", "ABC"},
}},
},
{reflect.TypeOf((*NUnionABC)(nil)).Elem(), NUnionWant},
{reflect.TypeOf(NUnionABCA{}), NUnionWant},
{reflect.TypeOf(NUnionABCB{}), NUnionWant},
{reflect.TypeOf(NUnionABCC{}), NUnionWant},
{
reflect.TypeOf(NRecurseSelf{}),
[]*reflectInfo{{
Type: reflect.TypeOf(NRecurseSelf{}),
Name: "v.io/v23/vdl.NRecurseSelf",
}},
},
{
reflect.TypeOf(NRecurseA{}),
[]*reflectInfo{
{
Type: reflect.TypeOf(NRecurseA{}),
Name: "v.io/v23/vdl.NRecurseA",
},
{
Type: reflect.TypeOf(NRecurseB{}),
Name: "v.io/v23/vdl.NRecurseB",
},
},
},
{
reflect.TypeOf(NRecurseB{}),
[]*reflectInfo{
{
Type: reflect.TypeOf(NRecurseB{}),
Name: "v.io/v23/vdl.NRecurseB",
},
{
Type: reflect.TypeOf(NRecurseA{}),
Name: "v.io/v23/vdl.NRecurseA",
},
},
},
}
// Test deriveReflectInfo success.
func TestDeriveReflectInfo(t *testing.T) {
for _, test := range reflectInfoTests {
ri, _, err := deriveReflectInfo(test.rt)
if ri == nil || err != nil {
t.Errorf("%s deriveReflectInfo failed: (%v, %v)", test.rt, ri, err)
continue
}
if got, want := ri, test.ri[0]; !reflect.DeepEqual(got, want) {
t.Errorf("%s got %v, want %v", test.rt, got, want)
}
}
}
// Test Register called by multiple goroutines concurrently on the same types,
// to expose locking issues in the registry.
func TestRegister(t *testing.T) {
var done sync.WaitGroup
for i := 0; i < 3; i++ {
done.Add(1)
go func() {
testRegister(t)
done.Done()
}()
}
done.Wait()
}
func testRegister(t *testing.T) {
for _, test := range reflectInfoTests {
Register(reflect.New(test.rt).Interface())
for _, testri := range test.ri {
if testri.Name != "" {
ri := reflectInfoFromName(testri.Name)
if got, want := ri, testri; !reflect.DeepEqual(got, want) {
t.Errorf("%s reflectInfoFromName got %v, want %v", test.rt, got, want)
}
}
}
}
}
type (
nBadDescribe1 struct{}
nBadDescribe2 struct{}
nBadDescribe3 struct{}
nBadEnumNoLabels int
nBadEnumString1 int
nBadEnumString2 int
nBadEnumString3 int
nBadEnumSet1 int
nBadEnumSet2 int
nBadEnumSet3 int
nBadEnumSet4 int
nBadUnionNoFields struct{}
nBadUnionUnexp struct{}
nBadUnionField1 struct{}
nBadUnionField2 struct{}
nBadUnionField3 struct{}
nBadUnionName1 struct{ Value bool }
nBadUnionName2 struct{ Value bool }
nBadUnionIndex1 struct{ Value bool }
nBadUnionIndex2 struct{ Value bool }
)
// No description
func (nBadDescribe1) __VDLReflect() { panic("X") }
// In-arg isn't a struct
func (nBadDescribe2) __VDLReflect(int) { panic("X") }
// Can't have out-arg
func (nBadDescribe3) __VDLReflect(struct{}) error { panic("X") }
// No enum labels
func (nBadEnumNoLabels) __VDLReflect(struct{ Enum struct{} }) { panic("X") }
// No String method
func (nBadEnumString1) __VDLReflect(struct{ Enum struct{ A string } }) { panic("X") }
// String method isn't String() string
func (nBadEnumString2) __VDLReflect(struct{ Enum struct{ A string } }) { panic("X") }
func (nBadEnumString2) String() { panic("X") }
// String method isn't String() string
func (nBadEnumString3) __VDLReflect(struct{ Enum struct{ A string } }) { panic("X") }
func (nBadEnumString3) String() bool { panic("X") }
// No Set method
func (nBadEnumSet1) __VDLReflect(struct{ Enum struct{ A string } }) { panic("X") }
func (nBadEnumSet1) String() string { panic("X") }
// Set method isn't Set(string) error
func (nBadEnumSet2) __VDLReflect(struct{ Enum struct{ A string } }) { panic("X") }
func (nBadEnumSet2) String() string { panic("X") }
func (nBadEnumSet2) Set() { panic("X") }
// Set method isn't Set(string) error
func (nBadEnumSet3) __VDLReflect(struct{ Enum struct{ A string } }) { panic("X") }
func (nBadEnumSet3) String() string { panic("X") }
func (nBadEnumSet3) Set(bool) error { panic("X") }
// Set method receiver isn't a pointer
func (nBadEnumSet4) __VDLReflect(struct{ Enum struct{ A string } }) { panic("X") }
func (nBadEnumSet4) String() string { panic("X") }
func (nBadEnumSet4) Set(string) error { panic("X") }
// No union fields
func (nBadUnionNoFields) __VDLReflect(struct {
Type NUnionABC
Union struct{}
}) {
panic("X")
}
// Field name isn't exported
func (nBadUnionUnexp) __VDLReflect(struct {
Type NUnionABC
Union struct{ a NUnionABCA }
}) {
panic("X")
}
// Field type isn't struct
func (nBadUnionField1) __VDLReflect(struct {
Type NUnionABC
Union struct{ A bool }
}) {
panic("X")
}
// Field type has no field
func (nBadUnionField2) __VDLReflect(struct {
Type NUnionABC
Union struct{ A struct{} }
}) {
panic("X")
}
// Field type name isn't "Value"
func (nBadUnionField3) __VDLReflect(struct {
Type NUnionABC
Union struct{ A struct{ value bool } }
}) {
panic("X")
}
// Name method isn't Name() string
func (nBadUnionName1) Name() { panic("X") }
func (nBadUnionName1) __VDLReflect(struct {
Type NUnionABC
Union struct{ A nBadUnionName1 }
}) {
panic("X")
}
// Name method isn't Name() string
func (nBadUnionName2) Name() bool { panic("X") }
func (nBadUnionName2) __VDLReflect(struct {
Type NUnionABC
Union struct{ A nBadUnionName2 }
}) {
panic("X")
}
// Index method isn't Index() int
func (nBadUnionIndex1) Name() string { panic("X") }
func (nBadUnionIndex1) Index() { panic("X") }
func (nBadUnionIndex1) __VDLReflect(struct {
Type NUnionABC
Union struct{ A nBadUnionIndex1 }
}) {
panic("X")
}
// Index method isn't Index() int
func (nBadUnionIndex2) Name() string { panic("X") }
func (nBadUnionIndex2) Index() bool { panic("X") }
func (nBadUnionIndex2) __VDLReflect(struct {
Type NUnionABC
Union struct{ A nBadUnionIndex2 }
}) {
panic("X")
}
// rtErrorTest describes a test case with rt as input, and errstr as output.
type rtErrorTest struct {
rt reflect.Type
errstr string
}
const (
badDescribe = `invalid __VDLReflect (want __VDLReflect(struct{...}))`
badEnumString = `must have method String() string`
badEnumSet = `must have pointer method Set(string) error`
badUnionField = `bad concrete field type`
badUnionName = `must have method Name() string`
badUnionIndex = `must have method Index() int`
)
var reflectInfoErrorTests = []rtErrorTest{
{reflect.TypeOf(nBadDescribe1{}), badDescribe},
{reflect.TypeOf(nBadDescribe2{}), badDescribe},
{reflect.TypeOf(nBadDescribe3{}), badDescribe},
{reflect.TypeOf(nBadEnumNoLabels(0)), `no labels`},
{reflect.TypeOf(nBadEnumString1(0)), badEnumString},
{reflect.TypeOf(nBadEnumString2(0)), badEnumString},
{reflect.TypeOf(nBadEnumString3(0)), badEnumString},
{reflect.TypeOf(nBadEnumSet1(0)), badEnumSet},
{reflect.TypeOf(nBadEnumSet2(0)), badEnumSet},
{reflect.TypeOf(nBadEnumSet3(0)), badEnumSet},
{reflect.TypeOf(nBadEnumSet4(0)), badEnumSet},
{reflect.TypeOf(nBadUnionNoFields{}), `no fields`},
{reflect.TypeOf(nBadUnionUnexp{}), `must be exported`},
{reflect.TypeOf(nBadUnionField1{}), badUnionField},
{reflect.TypeOf(nBadUnionField2{}), badUnionField},
{reflect.TypeOf(nBadUnionField3{}), badUnionField},
{reflect.TypeOf(nBadUnionName1{}), badUnionName},
{reflect.TypeOf(nBadUnionName2{}), badUnionName},
{reflect.TypeOf(nBadUnionIndex1{}), badUnionIndex},
{reflect.TypeOf(nBadUnionIndex2{}), badUnionIndex},
}
// Test deriveReflectInfo errors.
func TestDeriveReflectInfoError(t *testing.T) {
for _, test := range reflectInfoErrorTests {
got, _, err := deriveReflectInfo(test.rt)
ExpectErr(t, err, test.errstr, "deriveReflectInfo(%v)", test.rt)
if got != nil {
t.Errorf("deriveReflectInfo(%v) got %v, want nil", test.rt, got)
}
}
}
type (
NameConflictType interface {
Index() int
Interface() interface{}
Name() string
__VDLReflect(__NameConflictReflect)
}
__NameConflictReflect struct {
Name string `vdl:"v.io/v23/vdl.OtherNameConflictType"`
Type NameConflictType
Union struct{ A NameConflictUnionField }
}
NameConflictUnionField struct{ Value int64 }
OtherNameConflictType struct{}
)
func (x NameConflictUnionField) Index() int { return 0 }
func (x NameConflictUnionField) Interface() interface{} { return x.Value }
func (x NameConflictUnionField) Name() string { return "A" }
func (x NameConflictUnionField) __VDLReflect(__NameConflictReflect) {}
func TestReflectNameConflicts(t *testing.T) {
Register(NameConflictUnionField{})
ExpectPanic(t, func() {
Register(OtherNameConflictType{})
}, "duplicate name", "")
}