2018-08-16 00:07:18 +00:00
|
|
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2018-08-27 05:48:17 +00:00
|
|
|
package protoregistry_test
|
2018-08-16 00:07:18 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
|
|
|
2018-09-22 00:44:00 +00:00
|
|
|
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
|
|
|
|
preg "github.com/golang/protobuf/v2/reflect/protoregistry"
|
|
|
|
ptype "github.com/golang/protobuf/v2/reflect/prototype"
|
2018-08-16 00:07:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestFiles(t *testing.T) {
|
|
|
|
type (
|
|
|
|
file struct {
|
|
|
|
Path string
|
|
|
|
Pkg pref.FullName
|
|
|
|
}
|
|
|
|
testFile struct {
|
|
|
|
inFile *ptype.File
|
|
|
|
wantErr string
|
|
|
|
}
|
|
|
|
testFindDesc struct {
|
|
|
|
inName pref.FullName
|
|
|
|
wantOk bool
|
|
|
|
}
|
|
|
|
testRangePkg struct {
|
|
|
|
inPkg pref.FullName
|
|
|
|
wantFiles []file
|
|
|
|
}
|
|
|
|
testRangePath struct {
|
|
|
|
inPath string
|
|
|
|
wantFiles []file
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
files []testFile
|
|
|
|
findDescs []testFindDesc
|
|
|
|
rangePkgs []testRangePkg
|
|
|
|
rangePaths []testRangePath
|
|
|
|
}{{
|
|
|
|
// Test that overlapping packages and files are permitted.
|
|
|
|
files: []testFile{
|
|
|
|
{inFile: &ptype.File{Syntax: pref.Proto2, Package: "foo.bar"}},
|
|
|
|
{inFile: &ptype.File{Syntax: pref.Proto2, Path: "foo/bar/test.proto", Package: "my.test"}},
|
|
|
|
{inFile: &ptype.File{Syntax: pref.Proto2, Path: "foo/bar/test.proto", Package: "foo.bar.baz"}},
|
|
|
|
{inFile: &ptype.File{Syntax: pref.Proto2, Package: "my.test.package"}},
|
|
|
|
{inFile: &ptype.File{Syntax: pref.Proto2, Package: "foo.bar"}},
|
|
|
|
{inFile: &ptype.File{Syntax: pref.Proto2, Path: "foo/bar/baz/../test.proto", Package: "my.test"}},
|
|
|
|
},
|
|
|
|
|
|
|
|
rangePkgs: []testRangePkg{{
|
|
|
|
inPkg: "nothing",
|
|
|
|
}, {
|
|
|
|
inPkg: "",
|
|
|
|
wantFiles: []file{
|
|
|
|
{"", "foo.bar"},
|
|
|
|
{"", "foo.bar"},
|
|
|
|
{"foo/bar/test.proto", "foo.bar.baz"},
|
|
|
|
{"foo/bar/test.proto", "my.test"},
|
|
|
|
{"", "my.test.package"},
|
|
|
|
{"foo/bar/baz/../test.proto", "my.test"},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
inPkg: ".",
|
|
|
|
}, {
|
|
|
|
inPkg: "foo",
|
|
|
|
wantFiles: []file{
|
|
|
|
{"", "foo.bar"},
|
|
|
|
{"", "foo.bar"},
|
|
|
|
{"foo/bar/test.proto", "foo.bar.baz"},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
inPkg: "foo.",
|
|
|
|
}, {
|
|
|
|
inPkg: "foo..",
|
|
|
|
}, {
|
|
|
|
inPkg: "foo.bar.baz",
|
|
|
|
wantFiles: []file{
|
|
|
|
{"foo/bar/test.proto", "foo.bar.baz"},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
inPkg: "fo",
|
|
|
|
}},
|
|
|
|
|
|
|
|
rangePaths: []testRangePath{{
|
|
|
|
inPath: "nothing",
|
|
|
|
}, {
|
|
|
|
inPath: "",
|
|
|
|
wantFiles: []file{
|
|
|
|
{"", "foo.bar"},
|
|
|
|
{"", "foo.bar"},
|
|
|
|
{"", "my.test.package"},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
inPath: "foo/bar/test.proto",
|
|
|
|
wantFiles: []file{
|
|
|
|
{"foo/bar/test.proto", "foo.bar.baz"},
|
|
|
|
{"foo/bar/test.proto", "my.test"},
|
|
|
|
},
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
// Test when new enum conflicts with existing package.
|
|
|
|
files: []testFile{{
|
|
|
|
inFile: &ptype.File{Syntax: pref.Proto2, Path: "test1a.proto", Package: "foo.bar.baz"},
|
|
|
|
}, {
|
|
|
|
inFile: &ptype.File{Syntax: pref.Proto2, Path: "test1b.proto", Enums: []ptype.Enum{{Name: "foo"}}},
|
|
|
|
wantErr: `file "test1b.proto" has a name conflict over foo`,
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
// Test when new package conflicts with existing enum.
|
|
|
|
files: []testFile{{
|
|
|
|
inFile: &ptype.File{Syntax: pref.Proto2, Path: "test2a.proto", Enums: []ptype.Enum{{Name: "foo"}}},
|
|
|
|
}, {
|
|
|
|
inFile: &ptype.File{Syntax: pref.Proto2, Path: "test2b.proto", Package: "foo.bar.baz"},
|
|
|
|
wantErr: `file "test2b.proto" has a name conflict over foo`,
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
// Test when new enum conflicts with existing enum in same package.
|
|
|
|
files: []testFile{{
|
|
|
|
inFile: &ptype.File{Syntax: pref.Proto2, Path: "test3a.proto", Package: "foo", Enums: []ptype.Enum{{Name: "BAR"}}},
|
|
|
|
}, {
|
|
|
|
inFile: &ptype.File{Syntax: pref.Proto2, Path: "test3b.proto", Package: "foo", Enums: []ptype.Enum{{Name: "BAR"}}},
|
|
|
|
wantErr: `file "test3b.proto" has a name conflict over foo.BAR`,
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
files: []testFile{{
|
|
|
|
inFile: &ptype.File{
|
|
|
|
Syntax: pref.Proto2,
|
|
|
|
Package: "fizz.buzz",
|
|
|
|
Messages: []ptype.Message{{
|
|
|
|
Name: "Message",
|
|
|
|
Fields: []ptype.Field{{
|
|
|
|
Name: "Field",
|
|
|
|
Number: 1,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.StringKind,
|
|
|
|
OneofName: "Oneof",
|
|
|
|
}},
|
|
|
|
Oneofs: []ptype.Oneof{{Name: "Oneof"}},
|
|
|
|
ExtensionRanges: [][2]pref.FieldNumber{{1000, 2000}},
|
|
|
|
}},
|
|
|
|
Enums: []ptype.Enum{{
|
|
|
|
Name: "Enum",
|
|
|
|
Values: []ptype.EnumValue{{Name: "EnumValue", Number: 0}},
|
|
|
|
}},
|
|
|
|
Extensions: []ptype.Extension{{
|
|
|
|
Name: "Extension",
|
|
|
|
Number: 1000,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.StringKind,
|
|
|
|
ExtendedType: ptype.PlaceholderMessage("fizz.buzz.Message"),
|
|
|
|
}},
|
|
|
|
Services: []ptype.Service{{
|
|
|
|
Name: "Service",
|
|
|
|
Methods: []ptype.Method{{
|
|
|
|
Name: "Method",
|
|
|
|
InputType: ptype.PlaceholderMessage("fizz.buzz.Message"),
|
|
|
|
OutputType: ptype.PlaceholderMessage("fizz.buzz.Message"),
|
|
|
|
IsStreamingClient: true,
|
|
|
|
IsStreamingServer: true,
|
|
|
|
}},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
inFile: &ptype.File{
|
|
|
|
Syntax: pref.Proto2,
|
|
|
|
Package: "fizz.buzz.gazz",
|
|
|
|
Enums: []ptype.Enum{{
|
|
|
|
Name: "Enum",
|
|
|
|
Values: []ptype.EnumValue{{Name: "EnumValue", Number: 0}},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
// Conflict over a single declaration.
|
|
|
|
inFile: &ptype.File{
|
|
|
|
Syntax: pref.Proto2,
|
|
|
|
Package: "fizz.buzz",
|
|
|
|
Enums: []ptype.Enum{{
|
|
|
|
Name: "Enum1",
|
|
|
|
Values: []ptype.EnumValue{{Name: "EnumValue1", Number: 0}},
|
|
|
|
}, {
|
|
|
|
Name: "Enum2",
|
|
|
|
Values: []ptype.EnumValue{{Name: "EnumValue2", Number: 0}},
|
|
|
|
}, {
|
|
|
|
Name: "Enum3",
|
|
|
|
Values: []ptype.EnumValue{{Name: "Enum", Number: 0}}, // conflict
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
wantErr: "name conflict over fizz.buzz.Enum",
|
|
|
|
}, {
|
|
|
|
// Previously failed registration should not pollute the namespace.
|
|
|
|
inFile: &ptype.File{
|
|
|
|
Syntax: pref.Proto2,
|
|
|
|
Package: "fizz.buzz",
|
|
|
|
Enums: []ptype.Enum{{
|
|
|
|
Name: "Enum1",
|
|
|
|
Values: []ptype.EnumValue{{Name: "EnumValue1", Number: 0}},
|
|
|
|
}, {
|
|
|
|
Name: "Enum2",
|
|
|
|
Values: []ptype.EnumValue{{Name: "EnumValue2", Number: 0}},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
// Make sure we can register without package name.
|
|
|
|
inFile: &ptype.File{
|
|
|
|
Syntax: pref.Proto2,
|
|
|
|
Messages: []ptype.Message{{
|
|
|
|
Name: "Message",
|
|
|
|
Messages: []ptype.Message{{
|
|
|
|
Name: "Message",
|
|
|
|
Messages: []ptype.Message{{
|
|
|
|
Name: "Message",
|
|
|
|
}},
|
|
|
|
}},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}},
|
|
|
|
|
|
|
|
findDescs: []testFindDesc{
|
|
|
|
{"", false},
|
|
|
|
{"Enum", false},
|
|
|
|
{"Message", true},
|
|
|
|
{"Message.", false},
|
|
|
|
{"Message.Message", true},
|
|
|
|
{"Message.Message.Message", true},
|
|
|
|
{"Message.Message.Message.Message", false},
|
|
|
|
{"fizz.buzz", false},
|
|
|
|
{"fizz.buzz.Enum", true},
|
|
|
|
{"fizz.buzz.Enum1", true},
|
|
|
|
{"fizz.buzz.Enum1.EnumValue", false},
|
|
|
|
{"fizz.buzz.EnumValue", true},
|
|
|
|
{"fizz.buzz.Message", true},
|
|
|
|
{"fizz.buzz.Message.Field", true},
|
|
|
|
{"fizz.buzz.Message.Oneof", true},
|
|
|
|
{"fizz.buzz.Extension", true},
|
|
|
|
{"fizz.buzz.Service", true},
|
|
|
|
{"fizz.buzz.Service.Method", true},
|
|
|
|
{"fizz.buzz.Method", false},
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
|
|
|
|
sortFiles := cmpopts.SortSlices(func(x, y file) bool {
|
|
|
|
return x.Path < y.Path || (x.Path == y.Path && x.Pkg < y.Pkg)
|
|
|
|
})
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run("", func(t *testing.T) {
|
2018-08-27 05:48:17 +00:00
|
|
|
var files preg.Files
|
2018-08-16 00:07:18 +00:00
|
|
|
for i, tc := range tt.files {
|
|
|
|
fd, err := ptype.NewFile(tc.inFile)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("file %d, prototype.NewFile() error: %v", i, err)
|
|
|
|
}
|
|
|
|
gotErr := files.Register(fd)
|
|
|
|
if (gotErr == nil && tc.wantErr != "") || !strings.Contains(fmt.Sprint(gotErr), tc.wantErr) {
|
|
|
|
t.Errorf("file %d, Register() = %v, want %v", i, gotErr, tc.wantErr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tt.findDescs {
|
|
|
|
got, err := files.FindDescriptorByName(tc.inName)
|
|
|
|
if (got == nil) == (err == nil) {
|
|
|
|
if tc.wantOk {
|
|
|
|
t.Errorf("FindDescriptorByName(%v) = (%v, %v), want (non-nil, nil)", tc.inName, got, err)
|
|
|
|
} else {
|
|
|
|
t.Errorf("FindDescriptorByName(%v) = (%v, %v), want (nil, NotFound)", tc.inName, got, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gotName := pref.FullName("<nil>")
|
|
|
|
if got != nil {
|
|
|
|
gotName = got.FullName()
|
|
|
|
}
|
|
|
|
wantName := pref.FullName("<nil>")
|
|
|
|
if tc.wantOk {
|
|
|
|
wantName = tc.inName
|
|
|
|
}
|
|
|
|
if gotName != wantName {
|
|
|
|
t.Errorf("FindDescriptorByName(%v) = %v, want %v", tc.inName, gotName, wantName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tt.rangePkgs {
|
|
|
|
var gotFiles []file
|
|
|
|
files.RangeFilesByPackage(tc.inPkg, func(fd pref.FileDescriptor) bool {
|
|
|
|
gotFiles = append(gotFiles, file{fd.Path(), fd.Package()})
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if diff := cmp.Diff(tc.wantFiles, gotFiles, sortFiles); diff != "" {
|
|
|
|
t.Errorf("RangeFilesByPackage(%v) mismatch (-want +got):\n%v", tc.inPkg, diff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tt.rangePaths {
|
|
|
|
var gotFiles []file
|
|
|
|
files.RangeFilesByPath(tc.inPath, func(fd pref.FileDescriptor) bool {
|
|
|
|
gotFiles = append(gotFiles, file{fd.Path(), fd.Package()})
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if diff := cmp.Diff(tc.wantFiles, gotFiles, sortFiles); diff != "" {
|
|
|
|
t.Errorf("RangeFilesByPath(%v) mismatch (-want +got):\n%v", tc.inPath, diff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|