blob: 84579caab78783f91d56190a729c1c5cde674b05 [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.
// The following enables go generate to generate the doc.go file.
//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
package main
import (
"fmt"
"io"
"os"
"strings"
"v.io/v23/vdl"
"v.io/v23/vdl/vdltest"
"v.io/x/lib/cmdline"
"v.io/x/lib/textutil"
"v.io/x/ref/lib/vdl/build"
"v.io/x/ref/lib/vdl/codegen/vdlgen"
"v.io/x/ref/lib/vdl/compile"
)
var cmdGen = &cmdline.Command{
Runner: cmdline.RunnerFunc(runGen),
Name: "vdltestgen",
Short: "generates test data for the vdltest package",
Long: `
Command vdltestgen generates types and values for the vdltest package. The
following files are generated:
vtype_gen.vdl - Named "V" types, regular VDL types.
ventry_pass_gen.vdl - Entries that pass conversion from source to target.
ventry_fail_gen.vdl - Entries that fail conversion from source to target.
xtype_gen.vdl - Named "X" types, no VDL{IsZero,Read,Write} methods.
xentry_pass_gen.vdl - Entries that pass conversion from source to target.
xentry_fail_gen.vdl - Entries that fail conversion from source to target.
Do not run this tool manually. Instead invoke it via:
$ jiri run go generate v.io/v23/vdl/vdltest
`,
}
func main() {
cmdline.Main(cmdGen)
}
const (
vdltestPkgName = "v.io/v23/vdl/vdltest"
typeGenFileName = "type_gen.vdl"
typeManualFileName = "type_manual.vdl"
passGenFileName = "entry_pass_gen.vdl"
failGenFileName = "entry_fail_gen.vdl"
)
func runGen(_ *cmdline.Env, _ []string) error {
// Build the vdltest package, to pick up manually-generated types and entries.
vdltestPkg, err := buildVDLTestPackage()
if err != nil {
return err
}
// Generate "v" types and entries.
vGenTypes := genAndWriteTypes("v")
vManTypes := collectManualTypes("v", vdltestPkg)
vPass, vFail := genEntries(vGenTypes, vGenTypes)
pass, fail := genEntries(vManTypes, append(vManTypes, vGenTypes...))
writeEntries("v", append(vPass, pass...), append(vFail, fail...))
// Generate "x" types and entries, skipping entries already covered by "v".
xGenTypes := genAndWriteTypes("x")
xManTypes := collectManualTypes("x", vdltestPkg)
xTargetTypes := subtractTypes(xGenTypes, vGenTypes)
xPass, xFail := genEntries(xTargetTypes, xGenTypes)
pass, fail = genEntries(xManTypes, append(xManTypes, xGenTypes...))
writeEntries("x", append(xPass, pass...), append(xFail, fail...))
return nil
}
func buildVDLTestPackage() (*compile.Package, error) {
env := compile.NewEnv(-1)
pkgs := build.TransitivePackages([]string{vdltestPkgName}, build.UnknownPathIsError, build.Opts{}, env.Errors)
if !env.Errors.IsEmpty() {
return nil, env.Errors.ToError()
}
for _, pkg := range pkgs {
build.BuildPackage(pkg, env)
}
return env.ResolvePackage(vdltestPkgName), env.Errors.ToError()
}
// genAndWriteTypes generates types and writes the type file.
func genAndWriteTypes(prefix string) []*vdl.Type {
const maxTypeDepth = 3
gen := vdltest.NewTypeGenerator()
gen.RandSeed(1)
gen.NamePrefix = strings.ToUpper(prefix)
genTypes := gen.Gen(maxTypeDepth)
writeTypeFile(prefix+typeGenFileName, genTypes)
return genTypes
}
// collectManualTypes collects manually-defined types from pkg.
func collectManualTypes(prefix string, pkg *compile.Package) []*vdl.Type {
var manTypes []*vdl.Type
for _, file := range pkg.Files {
if file.BaseName != prefix+typeManualFileName {
continue
}
for _, def := range file.TypeDefs {
manTypes = append(manTypes, def.Type)
}
}
return manTypes
}
// subtractTypes returns all types that don't appear in sub.
func subtractTypes(types, sub []*vdl.Type) []*vdl.Type {
subMap := make(map[*vdl.Type]bool)
for _, tt := range sub {
subMap[tt] = true
}
var result []*vdl.Type
for _, tt := range types {
if !subMap[tt] {
result = append(result, tt)
}
}
return result
}
// genEntries generates entries for all target types, using source types as the
// candidates to mimic conversion values.
func genEntries(target, source []*vdl.Type) (pass, fail []vdltest.EntryValue) {
gen := vdltest.NewEntryGenerator(source)
gen.RandSeed(1)
pass = gen.GenAllPass(target)
gen.RandSeed(1)
fail = gen.GenAllFail(target)
return
}
func writeEntries(prefix string, pass, fail []vdltest.EntryValue) {
writeEntryFile(prefix+passGenFileName, prefix+"AllPass", pass)
writeEntryFile(prefix+failGenFileName, prefix+"AllFail", fail)
}
// This tool is only used to generate test cases for the vdltest package, so the
// strategy is to panic on any error, to make the code simpler.
func panicOnError(err error) {
if err != nil {
panic(err)
}
}
func createFile(name string) (*os.File, func()) {
file, err := os.Create(name)
panicOnError(err)
return file, func() { panicOnError(file.Close()) }
}
func writef(w io.Writer, format string, args ...interface{}) {
_, err := fmt.Fprintf(w, format, args...)
panicOnError(err)
}
func writeHeader(w io.Writer) {
writef(w, `// 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.
// This file was auto-generated by v.io/v23/vdl/vdltest/internal/vdltestgen
// Run the following to re-generate:
// $ jiri run go generate v.io/v23/vdl/vdltest
package vdltest
`)
}
func writeTypeFile(fileName string, types []*vdl.Type) {
fmt.Printf("Writing %[1]s:\t%[2]d types\n", fileName, len(types))
file, cleanup := createFile(fileName)
defer cleanup()
writeHeader(file)
comment := textutil.PrefixLineWriter(file, "// ")
panicOnError(vdltest.PrintTypeStats(comment, types...))
writef(comment, "\nOnly named types appear below, by definition.\n")
panicOnError(comment.Flush())
writef(file, "\ntype (\n")
for _, tt := range types {
if tt.Name() != "" {
base := vdlgen.BaseType(tt, vdltestPkgName, nil)
base = strings.Replace(base, "\n", "\n\t", -1)
writef(file, "\t%[1]s %[2]s\n", tt.Name(), base)
}
}
writef(file, ")\n")
}
func writeEntryFile(fileName, constName string, entries []vdltest.EntryValue) {
fmt.Printf("Writing %[1]s:\t%[2]d entries\n", fileName, len(entries))
file, cleanup := createFile(fileName)
defer cleanup()
writeHeader(file)
comment := textutil.PrefixLineWriter(file, "// ")
panicOnError(vdltest.PrintEntryStats(comment, entries...))
panicOnError(comment.Flush())
writef(file, "\nconst %[1]s = []vdlEntry{\n", constName)
for _, e := range entries {
if e.IsCanonical() {
writef(file, "\t// Canonical\n")
}
target := vdlgen.TypedConst(e.Target, vdltestPkgName, nil)
source := vdlgen.TypedConst(e.Source, vdltestPkgName, nil)
if len(target)*2+len(source)*2 < 100 {
// Write a pretty one-liner, if it's short enough.
writef(file, "\t{ %[1]v, %#[2]q, %#[3]q, %[3]s, %#[4]q, %[4]s },\n", e.IsCanonical(), e.Label, target, source)
} else {
writef(file, "\t{ %[1]v, %#[2]q,\n\t %#[3]q,\n\t %[3]s,\n\t %#[4]q,\n\t %[4]s,\n\t},\n", e.IsCanonical(), e.Label, target, source)
}
}
writef(file, "}\n")
}