blob: 1dde86b3de263c770f46786c1292ae1ee36c6118 [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 compile_test
import (
"reflect"
"testing"
"v.io/v23/i18n"
"v.io/v23/vdl"
"v.io/x/ref/lib/vdl/build"
"v.io/x/ref/lib/vdl/compile"
"v.io/x/ref/lib/vdl/internal/vdltestutil"
"v.io/x/ref/lib/vdl/parse"
)
func TestError(t *testing.T) {
for _, test := range errorTests {
testError(t, test)
}
}
func testError(t *testing.T, test errorTest) {
env := compile.NewEnv(-1)
for _, epkg := range test.Pkgs {
// Compile the package with a single file, and adding the "package foo"
// prefix to the source data automatically.
files := map[string]string{
epkg.Name + ".vdl": "package " + epkg.Name + "\n" + epkg.Data,
}
buildPkg := vdltestutil.FakeBuildPackage(epkg.Name, epkg.Name, files)
pkg := build.BuildPackage(buildPkg, env)
vdltestutil.ExpectResult(t, env.Errors, test.Name, epkg.ErrRE)
if pkg == nil || epkg.ErrRE != "" {
continue
}
matchErrorRes(t, test.Name, epkg, pkg.Files[0].ErrorDefs)
}
}
func matchErrorRes(t *testing.T, tname string, epkg errorPkg, edefs []*compile.ErrorDef) {
// Look for an ErrorDef called "Res" to compare our expected results.
for _, edef := range edefs {
if edef.ID == epkg.Name+".Res" {
got, want := cleanErrorDef(*edef), cleanErrorDef(epkg.Want)
if !reflect.DeepEqual(got, want) {
t.Errorf("%s got %+v, want %+v", tname, got, want)
}
return
}
}
t.Errorf("%s couldn't find Res in package %s", tname, epkg.Name)
}
// cleanErrorDef resets fields that we don't care about testing.
func cleanErrorDef(ed compile.ErrorDef) compile.ErrorDef {
ed.NamePos = compile.NamePos{}
ed.Exported = false
ed.ID = ""
for _, param := range ed.Params {
param.Pos = parse.Pos{}
}
ed.File = nil
return ed
}
type errorPkg struct {
Name string
Data string
Want compile.ErrorDef
ErrRE string
}
type ep []errorPkg
type errorTest struct {
Name string
Pkgs ep
}
const (
en i18n.LangID = "en"
zh = "zh"
)
func arg(name string, t *vdl.Type) *compile.Field {
arg := new(compile.Field)
arg.Name = name
arg.Type = t
return arg
}
const pre = "{1:}{2:} "
var errorTests = []errorTest{
{"NoParams1", ep{{"a", `error Res() {"en":"msg1"}`,
compile.ErrorDef{
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"NoParams2", ep{{"a", `error Res() {"en":"msg1","zh":"msg2"}`,
compile.ErrorDef{
Formats: []compile.LangFmt{{en, pre + "msg1"}, {zh, pre + "msg2"}},
English: pre + "msg1",
},
"",
}}},
{"NoParamsNoRetry", ep{{"a", `error Res() {NoRetry,"en":"msg1"}`,
compile.ErrorDef{
RetryCode: vdl.WireRetryCodeNoRetry,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"NoParamsRetryConnection", ep{{"a", `error Res() {RetryConnection,"en":"msg1"}`,
compile.ErrorDef{
RetryCode: vdl.WireRetryCodeRetryConnection,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"NoParamsRetryRefetch", ep{{"a", `error Res() {RetryRefetch,"en":"msg1"}`,
compile.ErrorDef{
RetryCode: vdl.WireRetryCodeRetryRefetch,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"NoParamsRetryBackoff", ep{{"a", `error Res() {RetryBackoff,"en":"msg1"}`,
compile.ErrorDef{
RetryCode: vdl.WireRetryCodeRetryBackoff,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"NoParamsMulti", ep{{"a", `error Res() {RetryRefetch,"en":"msg1","zh":"msg2"}`,
compile.ErrorDef{
RetryCode: vdl.WireRetryCodeRetryRefetch,
Formats: []compile.LangFmt{{en, pre + "msg1"}, {zh, pre + "msg2"}},
English: pre + "msg1",
},
"",
}}},
{"WithParams1", ep{{"a", `error Res(x string, y int32) {"en":"msg1"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"WithParams2", ep{{"a", `error Res(x string, y int32) {"en":"msg1","zh":"msg2"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
Formats: []compile.LangFmt{{en, pre + "msg1"}, {zh, pre + "msg2"}},
English: pre + "msg1",
},
"",
}}},
{"WithParamsNoRetry", ep{{"a", `error Res(x string, y int32) {NoRetry,"en":"msg1"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
RetryCode: vdl.WireRetryCodeNoRetry,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"WithParamsRetryConnection", ep{{"a", `error Res(x string, y int32) {RetryConnection,"en":"msg1"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
RetryCode: vdl.WireRetryCodeRetryConnection,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"WithParamsRetryRefetch", ep{{"a", `error Res(x string, y int32) {RetryRefetch,"en":"msg1"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
RetryCode: vdl.WireRetryCodeRetryRefetch,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"WithParamsRetryBackoff", ep{{"a", `error Res(x string, y int32) {RetryBackoff,"en":"msg1"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
RetryCode: vdl.WireRetryCodeRetryBackoff,
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
}}},
{"WithParamsMulti", ep{{"a", `error Res(x string, y int32) {RetryRefetch,"en":"msg1","zh":"msg2"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
RetryCode: vdl.WireRetryCodeRetryRefetch,
Formats: []compile.LangFmt{{en, pre + "msg1"}, {zh, pre + "msg2"}},
English: pre + "msg1",
},
"",
}}},
{"WithParamsFormat", ep{{"a", `error Res(x string, y int32) {"en":"en {x} {y}","zh":"zh {y} {x}"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.StringType), arg("y", vdl.Int32Type)},
Formats: []compile.LangFmt{{en, pre + "en {3} {4}"}, {zh, pre + "zh {4} {3}"}},
English: pre + "en {3} {4}",
},
"",
}}},
{"WithSamePackageParam", ep{{"a", `error Res(x Bool) {"en":"en {x}"};type Bool bool`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.NamedType("a.Bool", vdl.BoolType))},
Formats: []compile.LangFmt{{en, pre + "en {3}"}},
English: pre + "en {3}",
},
"",
}}},
// Test multi-package errors.
{"MultiPkgSameErrorName", ep{
{
"a", `error Res() {"en":"msg1"}`,
compile.ErrorDef{
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
},
{
"b", `error Res() {"en":"msg2"}`,
compile.ErrorDef{
Formats: []compile.LangFmt{{en, pre + "msg2"}},
English: pre + "msg2",
},
"",
},
}},
{"MultiPkgTypeDep", ep{
{
"a", `error Res() {"en":"msg1"};type Bool bool`,
compile.ErrorDef{
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
},
{
"b", `import "a";error Res(x a.Bool) {"en":"en {x}"}`,
compile.ErrorDef{
Params: []*compile.Field{arg("x", vdl.NamedType("a.Bool", vdl.BoolType))},
Formats: []compile.LangFmt{{en, pre + "en {3}"}},
English: pre + "en {3}",
},
"",
},
}},
{"RedefinitionOfImportName", ep{
{
"a", `error Res() {"en":"msg1"}`,
compile.ErrorDef{
Formats: []compile.LangFmt{{en, pre + "msg1"}},
English: pre + "msg1",
},
"",
},
{
"b", `import "a";error a() {"en":"en {}"}`, compile.ErrorDef{},
"error a name conflict",
},
}},
// Test errors.
{"NoParamsNoLangFmt1", ep{{"a", `error Res()`, compile.ErrorDef{}, englishFormat}}},
{"NoParamsNoLangFmt2", ep{{"a", `error Res() {}`, compile.ErrorDef{}, englishFormat}}},
{"NoParamsNoLangFmt3", ep{{"a", `error Res() {NoRetry}`, compile.ErrorDef{}, englishFormat}}},
{"WithParamsNoLangFmt1", ep{{"a", `error Res(x string, y int32)`, compile.ErrorDef{}, englishFormat}}},
{"WithParamsNoLangFmt2", ep{{"a", `error Res(x string, y int32) {}`, compile.ErrorDef{}, englishFormat}}},
{"WithParamsNoLangFmt3", ep{{"a", `error Res(x string, y int32) {NoRetry}`, compile.ErrorDef{}, englishFormat}}},
{"MissingParamName1", ep{{"a", `error Res(bool) {"en":"msg1"}`, compile.ErrorDef{}, "parameters must be named"}}},
{"MissingParamName2", ep{{"a", `error Res(bool, int32) {"en":"msg1"}`, compile.ErrorDef{}, "parameters must be named"}}},
{"UnknownType", ep{{"a", `error Res(x foo) {"en":"msg1"}`, compile.ErrorDef{}, "type foo undefined"}}},
{"NoTransitiveExportArg", ep{{"a", `type foo bool; error Res(x foo) {"en":"msg1"}`, compile.ErrorDef{}, "transitively exported"}}},
{"InvalidParam", ep{{"a", `error Res(_x foo) {"en":"msg1"}`, compile.ErrorDef{}, "param _x invalid"}}},
{"DupParam", ep{{"a", `error Res(x bool, x int32) {"en":"msg1"}`, compile.ErrorDef{}, "param x duplicate name"}}},
{"UnknownAction", ep{{"a", `error Res() {Foo,"en":"msg1"}`, compile.ErrorDef{}, "unknown action"}}},
{"EmptyLanguage", ep{{"a", `error Res() {"":"msg"}`, compile.ErrorDef{}, "empty language"}}},
{"DupLanguage", ep{{"a", `error Res() {"en":"msg1","en":"msg2"}`, compile.ErrorDef{}, "duplicate language en"}}},
{"UnknownParam", ep{{"a", `error Res() {"en":"{foo}"}`, compile.ErrorDef{}, `unknown param "foo"`}}},
{"DupError", ep{{"a", `error Res() {"en":"msg1"};error Res() {"en":"msg1"}`, compile.ErrorDef{}, "error Res name conflict"}}},
}
const englishFormat = "must define at least one English format"