blob: e851ddfdedb6ca32c321bdd89a76f65f18743f1a [file] [log] [blame]
// Copyright 2016 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 vdltest provides a variety of VDL types and values for testing.
package vdltest
import (
"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 flagVDLTest string
func init() {
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:
// var vAllPass, vAllFail, xAllPass, xAllFail []vdlEntry
// AllPass returns all entries where the source value, when converted to the
// type of the target value, results in exactly the target value.
//
// The -vdltest flag may be used to filter the returned entries.
func AllPass() []Entry {
var result []Entry
for _, e := range fromVDLEntries(vAllPass, xAllPass) {
if want, substr := filter(); strings.Contains(e.Name(), substr) == want {
result = append(result, e)
}
}
return result
}
// AllPassFunc returns the entries in AllPass where fn(e) returns true for each
// returned entry.
func AllPassFunc(fn func(e Entry) bool) []Entry {
var result []Entry
for _, e := range AllPass() {
if fn(e) {
result = append(result, e)
}
}
return result
}
// AllFail returns all entries where the source value, when converted to the
// type of the target value, results in a conversion error.
//
// E.g. the types of the source and target may be incompatible; trying to
// convert a source bool to a target struct returns an error. Or the values may
// be inconvertible; trying to convert a source int32(-1) to a target uint32
// returns an error.
//
// The -vdltest flag may be used to filter the returned entries.
func AllFail() []Entry {
var result []Entry
for _, e := range fromVDLEntries(vAllFail, xAllFail) {
if want, substr := filter(); strings.Contains(e.Name(), substr) == want {
result = append(result, e)
}
}
return result
}
// AllFailFunc returns the entries in AllFail where fn(e) returns true for each
// returned entry.
func AllFailFunc(fn func(e Entry) bool) []Entry {
var result []Entry
for _, e := range AllFail() {
if fn(e) {
result = append(result, e)
}
}
return result
}
func fromVDLEntries(groups ...[]vdlEntry) []Entry {
var result []Entry
for _, entries := range groups {
for _, e := range entries {
result = append(result, fromVDLEntry(e))
result = append(result, genExtraEntries(e)...)
}
}
return result
}
func fromVDLEntry(e vdlEntry) Entry {
return Entry{
IsCanonical: e.IsCanonical,
Label: e.Label,
TargetLabel: e.TargetLabel,
Target: rvSafeValueOf(e.Target),
SourceLabel: e.SourceLabel,
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]"
if target := vdl.WrapInUnionInterface(entry.Target); target.IsValid() {
entry.Target = target
} else {
// If the target is a native type, with a union wire type of union, it
// won't have a union interface. There's no extra entry to generate.
break
}
entry.TargetLabel += " [interface]"
if vdl.TypeOf(e.Source).Kind() == vdl.Union {
if source := vdl.WrapInUnionInterface(entry.Source); source.IsValid() {
entry.Source = 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
}