Add extra entries to vdltest.AllPass and vdltest.AllFail
The extra entries are the top-level union interface, the
top-level error interface, and a variety of extra pointer tests.
Change-Id: I3dc003a042d1a16ae393e8bd3bdc36e18cb3ac1f
diff --git a/vdl/.api b/vdl/.api
index d96c09f..56704dd 100644
--- a/vdl/.api
+++ b/vdl/.api
@@ -72,6 +72,7 @@
pkg vdl, func ValueOf(interface{}) *Value
pkg vdl, func ValueTarget(*Value) (Target, error)
pkg vdl, func WireRetryCodeFromString(string) (WireRetryCode, error)
+pkg vdl, func WrapInUnionInterface(reflect.Value) reflect.Value
pkg vdl, func Write(Encoder, interface{}) error
pkg vdl, func WriteReflect(Encoder, reflect.Value) error
pkg vdl, func ZeroValue(*Type) *Value
diff --git a/vdl/reflect_reader.go b/vdl/reflect_reader.go
index b20277d..a1afa65 100644
--- a/vdl/reflect_reader.go
+++ b/vdl/reflect_reader.go
@@ -197,10 +197,7 @@
}
// Lookup the reflect type based on the decoder type, and create a new value
// to decode into.
- //
- // TODO(toddw): Replace typeToReflectFixed with TypeToReflect, after we've
- // fixed it to treat the error type correctly.
- rtDecode := typeToReflectFixed(dec.Type())
+ rtDecode := TypeToReflect(dec.Type())
if rtDecode == nil {
return fmt.Errorf("vdl: %v not registered, call vdl.Register, or use vdl.Value or vom.RawBytes instead", dec.Type())
}
diff --git a/vdl/register.go b/vdl/register.go
index f722f7b..587c0d7 100644
--- a/vdl/register.go
+++ b/vdl/register.go
@@ -120,6 +120,24 @@
return ri
}
+// WrapInUnionInterface returns a value of the union interface type that holds
+// the union value rv. Returns an invalid reflect.Value if rv isn't a union
+// value. Returns rv unchanged if its type is already the interface type.
+func WrapInUnionInterface(rv reflect.Value) reflect.Value {
+ ri, _, err := deriveReflectInfo(rv.Type())
+ switch {
+ case err != nil || len(ri.UnionFields) == 0:
+ return reflect.Value{} // rv isn't a union
+ case ri.Type == rv.Type():
+ return rv // rv's type is already the interface type
+ }
+ // Here ri.Type is the union interface type, while rv is a concrete struct
+ // type. Wrap rv in a value of the interface type.
+ rvIface := reflect.New(ri.Type).Elem()
+ rvIface.Set(rv)
+ return rvIface
+}
+
// reflectInfo holds the reflection information for a type. All fields are
// populated via reflection over the Type.
//
@@ -377,9 +395,6 @@
// named types in our registry, and build the unnamed types that we can via the
// Go reflect package. Returns nil for types that can't be manufactured.
func TypeToReflect(t *Type) reflect.Type {
- // TODO(toddw): This is broken, since it doesn't handle registered native
- // error types correctly. But it's hard to fix with the old convert.go logic,
- // so we'll wait until we get rid of that code completely.
if t.Name() != "" {
// Named types cannot be manufactured via Go reflect, so we lookup in our
// registry instead.
@@ -431,60 +446,6 @@
}
}
-// TODO(toddw): Replace TypeToReflect with typeToReflectFixed after the old
-// conversion logic is removed.
-func typeToReflectFixed(t *Type) reflect.Type {
- if t.Name() != "" {
- // Named types cannot be manufactured via Go reflect, so we lookup in our
- // registry instead.
- if ri := reflectInfoFromName(t.Name()); ri != nil {
- if ni := nativeInfoFromWire(ri.Type); ni != nil {
- return ni.NativeType
- }
- return ri.Type
- }
- return nil
- }
- // We can make some unnamed types via Go reflect. Return nil otherwise.
- switch t.Kind() {
- case Any, Enum, Union:
- // We can't make unnamed versions of any of these types.
- return nil
- case Optional:
- if elem := typeToReflectFixed(t.Elem()); elem != nil {
- return reflect.PtrTo(elem)
- }
- return nil
- case Array:
- if elem := typeToReflectFixed(t.Elem()); elem != nil {
- return reflect.ArrayOf(t.Len(), elem)
- }
- return nil
- case List:
- if elem := typeToReflectFixed(t.Elem()); elem != nil {
- return reflect.SliceOf(elem)
- }
- return nil
- case Set:
- if key := typeToReflectFixed(t.Key()); key != nil {
- return reflect.MapOf(key, rtUnnamedEmptyStruct)
- }
- return nil
- case Map:
- if key, elem := typeToReflectFixed(t.Key()), typeToReflectFixed(t.Elem()); key != nil && elem != nil {
- return reflect.MapOf(key, elem)
- }
- return nil
- case Struct:
- if t.NumField() == 0 {
- return rtUnnamedEmptyStruct
- }
- return nil
- default:
- return rtFromKind[t.Kind()]
- }
-}
-
var rtFromKind = [...]reflect.Type{
Bool: rtBool,
Byte: rtByte,
diff --git a/vdl/vdltest/all.go b/vdl/vdltest/all.go
index f1ee8e8..e4a152b 100644
--- a/vdl/vdltest/all.go
+++ b/vdl/vdltest/all.go
@@ -9,15 +9,24 @@
"flag"
"reflect"
"strings"
+
+ "v.io/v23/vdl"
)
// The following causes data files to be generated when "go generate" is run.
//go:generate ./gen.sh
-var flagContains string
+var flagVDLTest string
func init() {
- flag.StringVar(&flagContains, "vdltest", "", "Filter vdltest.All to only return entries that contain the given substring.")
+ flag.StringVar(&flagVDLTest, "vdltest", "", `Filter vdltest.All to only return entries that contain the given substring. If the value starts with "!", only returns entries that don't contain the given substring.`)
+}
+
+func filter() (bool, string) {
+ if strings.HasPrefix(flagVDLTest, "!") {
+ return false, strings.TrimPrefix(flagVDLTest, "!")
+ }
+ return true, flagVDLTest
}
// The following vars are defined in generated files:
@@ -30,7 +39,7 @@
func AllPass() []Entry {
var result []Entry
for _, e := range fromVDLEntries(vAllPass, xAllPass) {
- if strings.Contains(e.Name(), flagContains) {
+ if want, substr := filter(); strings.Contains(e.Name(), substr) == want {
result = append(result, e)
}
}
@@ -61,7 +70,7 @@
func AllFail() []Entry {
var result []Entry
for _, e := range fromVDLEntries(vAllFail, xAllFail) {
- if strings.Contains(e.Name(), flagContains) {
+ if want, substr := filter(); strings.Contains(e.Name(), substr) == want {
result = append(result, e)
}
}
@@ -84,15 +93,8 @@
var result []Entry
for _, entries := range groups {
for _, e := range entries {
- entry := fromVDLEntry(e)
- result = append(result, entry)
- // TODO(toddw): add additional entries and pointer tests.
- /*
- switch ttTarget := vdl.TypeOf(e.Target); {
- case ttTarget.Kind() == vdl.Union:
- case ttTarget == vdl.ErrorType:
- }
- */
+ result = append(result, fromVDLEntry(e))
+ result = append(result, genExtraEntries(e)...)
}
}
return result
@@ -103,8 +105,92 @@
IsCanonical: e.IsCanonical,
Label: e.Label,
TargetLabel: e.TargetLabel,
- Target: reflect.ValueOf(e.Target),
+ Target: rvSafeValueOf(e.Target),
SourceLabel: e.SourceLabel,
- Source: reflect.ValueOf(e.Source),
+ Source: rvSafeValueOf(e.Source),
}
}
+
+func rvSafeValueOf(v interface{}) reflect.Value {
+ // reflect.ValueOf(nil) returns an invalid reflect.Value, but we want a valid
+ // reflect.Value representing nil.
+ if v == nil {
+ return reflect.ValueOf(&v).Elem()
+ }
+ return reflect.ValueOf(v)
+}
+
+// genExtraEntries generates extra entries based on e. None of these are
+// canonical; they all deal with quirks in the Go representation of VDL values.
+func genExtraEntries(e vdlEntry) []Entry {
+ var extra []Entry
+ switch ttTarget := vdl.TypeOf(e.Target); {
+ case ttTarget.Kind() == vdl.Union:
+ // Add entry for top-level union interface.
+ entry := fromVDLEntry(e)
+ entry.IsCanonical = false
+ entry.Label += " [union interface]"
+ entry.Target = vdl.WrapInUnionInterface(entry.Target)
+ entry.TargetLabel += " [interface]"
+ if vdl.TypeOf(e.Source).Kind() == vdl.Union {
+ entry.Source = vdl.WrapInUnionInterface(entry.Source)
+ entry.SourceLabel += " [interface]"
+ }
+ extra = append(extra, entry)
+ case ttTarget == vdl.ErrorType:
+ // Add entry for top-level error interface.
+ entry := fromVDLEntry(e)
+ entry.IsCanonical = false
+ entry.Label += " [error interface]"
+ entry.Target = rvWrapInErrorInterface(entry.Target)
+ entry.TargetLabel += " [interface]"
+ if vdl.TypeOf(e.Source) == vdl.ErrorType {
+ entry.Source = rvWrapInErrorInterface(entry.Source)
+ entry.SourceLabel += " [interface]"
+ }
+ extra = append(extra, entry)
+ }
+ // Add entries with up to 2 additional pointers on the target and source.
+ const maxPtrs = 2
+ for t := 0; t < maxPtrs; t++ {
+ for s := 0; s < maxPtrs; s++ {
+ if t == 0 && s == 0 {
+ continue // skip the 0,0 case, which is already represented by e
+ }
+ entry := fromVDLEntry(e)
+ entry.IsCanonical = false
+ entry.Label += " [pointers]"
+ entry.Target, entry.TargetLabel = rvAddPointers(t, entry.Target, entry.TargetLabel)
+ entry.Source, entry.SourceLabel = rvAddPointers(s, entry.Source, entry.SourceLabel)
+ extra = append(extra, entry)
+ }
+ }
+ return extra
+}
+
+var rtError = reflect.TypeOf((*error)(nil)).Elem()
+
+// rvWrapInErrorInterface returns a reflect.Value whose type is the Go error
+// interface, which is set to rv.
+//
+// REQUIRES: rv must implement the Go error interface
+func rvWrapInErrorInterface(rv reflect.Value) reflect.Value {
+ rvError := reflect.New(rtError).Elem()
+ if rv.Kind() != reflect.Ptr || !rv.IsNil() {
+ // Only set the value for non-nil pointers. If rv is (*verror.E)(nil), we
+ // want to end up with Go error(nil).
+ rvError.Set(rv)
+ }
+ return rvError
+}
+
+// rvAddPointers adds num pointers to rv.
+func rvAddPointers(num int, rv reflect.Value, label string) (reflect.Value, string) {
+ for i := 0; i < num; i++ {
+ rvPtr := reflect.New(rv.Type())
+ rvPtr.Elem().Set(rv)
+ rv = rvPtr
+ label = "*" + label
+ }
+ return rv, label
+}
diff --git a/vdl/vdltest/internal/vdltestgen/doc.go b/vdl/vdltest/internal/vdltestgen/doc.go
index cdad5f0..01b4db4 100644
--- a/vdl/vdltest/internal/vdltestgen/doc.go
+++ b/vdl/vdltest/internal/vdltestgen/doc.go
@@ -48,5 +48,7 @@
Dump timing information to stderr before exiting the program.
-vdltest=
Filter vdltest.All to only return entries that contain the given substring.
+ If the value starts with "!", only returns entries that don't contain the
+ given substring.
*/
package main
diff --git a/vom/vomtest/data.go b/vom/vomtest/data.go
index 9602a1d..35963f2 100644
--- a/vom/vomtest/data.go
+++ b/vom/vomtest/data.go
@@ -14,17 +14,24 @@
// The following causes data files to be generated when "go generate" is run.
//go:generate ./gen.sh
-var flagContains string
+var flagVOMTest string
func init() {
- flag.StringVar(&flagContains, "vomtest", "", "Filter vomtest.Data to only return entries that contain the given substring.")
+ flag.StringVar(&flagVOMTest, "vomtest", "", `Filter vomtest.Data to only return entries that contain the given substring. If the value starts with "!", only returns entries that don't contain the given substring.`)
+}
+
+func filter() (bool, string) {
+ if strings.HasPrefix(flagVOMTest, "!") {
+ return false, strings.TrimPrefix(flagVOMTest, "!")
+ }
+ return true, flagVOMTest
}
// Data returns all vom test cases.
func Data() []TestCase {
var result []TestCase
for _, t := range data81 {
- if strings.Contains(t.Name, flagContains) {
+ if want, substr := filter(); strings.Contains(t.Name, substr) == want {
result = append(result, t)
}
}
diff --git a/vom/vomtest/internal/vomtestgen/doc.go b/vom/vomtest/internal/vomtestgen/doc.go
index a7cfcf2..e07b085 100644
--- a/vom/vomtest/internal/vomtestgen/doc.go
+++ b/vom/vomtest/internal/vomtestgen/doc.go
@@ -32,5 +32,7 @@
Dump timing information to stderr before exiting the program.
-vdltest=
Filter vdltest.All to only return entries that contain the given substring.
+ If the value starts with "!", only returns entries that don't contain the
+ given substring.
*/
package main