package descriptor import ( "testing" "github.com/golang/protobuf/proto" descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" plugin "github.com/golang/protobuf/protoc-gen-go/plugin" ) func loadFile(t *testing.T, reg *Registry, src string) *descriptor.FileDescriptorProto { var file descriptor.FileDescriptorProto if err := proto.UnmarshalText(src, &file); err != nil { t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", src, err) } reg.loadFile(&file) return &file } func load(t *testing.T, reg *Registry, src string) error { var req plugin.CodeGeneratorRequest if err := proto.UnmarshalText(src, &req); err != nil { t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", src, err) } return reg.Load(&req) } func TestLoadFile(t *testing.T) { reg := NewRegistry() fd := loadFile(t, reg, ` name: 'example.proto' package: 'example' message_type < name: 'ExampleMessage' field < name: 'str' label: LABEL_OPTIONAL type: TYPE_STRING number: 1 > > `) file := reg.files["example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: ".", Name: "example"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } msg, err := reg.LookupMsg("", ".example.ExampleMessage") if err != nil { t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".example.ExampleMessage", err) return } if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".example.ExampleMessage", got, want) } if got, want := msg.File, file; got != want { t.Errorf("msg.File = %v; want %v", got, want) } if got := msg.Outers; got != nil { t.Errorf("msg.Outers = %v; want %v", got, nil) } if got, want := len(msg.Fields), 1; got != want { t.Errorf("len(msg.Fields) = %d; want %d", got, want) } else if got, want := msg.Fields[0].FieldDescriptorProto, fd.MessageType[0].Field[0]; got != want { t.Errorf("msg.Fields[0].FieldDescriptorProto = %v; want %v", got, want) } else if got, want := msg.Fields[0].Message, msg; got != want { t.Errorf("msg.Fields[0].Message = %v; want %v", got, want) } if got, want := len(file.Messages), 1; got != want { t.Errorf("file.Meeesages = %#v; want %#v", file.Messages, []*Message{msg}) } if got, want := file.Messages[0], msg; got != want { t.Errorf("file.Meeesages[0] = %v; want %v", got, want) } } func TestLoadFileNestedPackage(t *testing.T) { reg := NewRegistry() loadFile(t, reg, ` name: 'example.proto' package: 'example.nested.nested2' `) file := reg.files["example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: ".", Name: "example_nested_nested2"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadFileWithDir(t *testing.T) { reg := NewRegistry() loadFile(t, reg, ` name: 'path/to/example.proto' package: 'example' `) file := reg.files["path/to/example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: "path/to", Name: "example"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadFileWithoutPackage(t *testing.T) { reg := NewRegistry() loadFile(t, reg, ` name: 'path/to/example_file.proto' `) file := reg.files["path/to/example_file.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: "path/to", Name: "example_file"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadFileWithMapping(t *testing.T) { reg := NewRegistry() reg.AddPkgMap("path/to/example.proto", "example.com/proj/example/proto") loadFile(t, reg, ` name: 'path/to/example.proto' package: 'example' `) file := reg.files["path/to/example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: "example.com/proj/example/proto", Name: "example"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadFileWithPackageNameCollision(t *testing.T) { reg := NewRegistry() loadFile(t, reg, ` name: 'path/to/another.proto' package: 'example' `) loadFile(t, reg, ` name: 'path/to/example.proto' package: 'example' `) if err := reg.ReserveGoPackageAlias("ioutil", "io/ioutil"); err != nil { t.Fatalf("reg.ReserveGoPackageAlias(%q) failed with %v; want success", "ioutil", err) } loadFile(t, reg, ` name: 'path/to/ioutil.proto' package: 'ioutil' `) file := reg.files["path/to/another.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/another.proto") return } wantPkg := GoPackage{Path: "path/to", Name: "example"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } file = reg.files["path/to/example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/example.proto") return } wantPkg = GoPackage{Path: "path/to", Name: "example", Alias: ""} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } file = reg.files["path/to/ioutil.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/ioutil.proto") return } wantPkg = GoPackage{Path: "path/to", Name: "ioutil", Alias: "ioutil_0"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadFileWithIdenticalGoPkg(t *testing.T) { reg := NewRegistry() reg.AddPkgMap("path/to/another.proto", "example.com/example") reg.AddPkgMap("path/to/example.proto", "example.com/example") loadFile(t, reg, ` name: 'path/to/another.proto' package: 'example' `) loadFile(t, reg, ` name: 'path/to/example.proto' package: 'example' `) file := reg.files["path/to/example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: "example.com/example", Name: "example"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } file = reg.files["path/to/another.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg = GoPackage{Path: "example.com/example", Name: "example"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadFileWithPrefix(t *testing.T) { reg := NewRegistry() reg.SetPrefix("third_party") loadFile(t, reg, ` name: 'path/to/example.proto' package: 'example' `) file := reg.files["path/to/example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: "third_party/path/to", Name: "example"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLookupMsgWithoutPackage(t *testing.T) { reg := NewRegistry() fd := loadFile(t, reg, ` name: 'example.proto' message_type < name: 'ExampleMessage' field < name: 'str' label: LABEL_OPTIONAL type: TYPE_STRING number: 1 > > `) msg, err := reg.LookupMsg("", ".ExampleMessage") if err != nil { t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".ExampleMessage", err) return } if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".ExampleMessage", got, want) } } func TestLookupMsgWithNestedPackage(t *testing.T) { reg := NewRegistry() fd := loadFile(t, reg, ` name: 'example.proto' package: 'nested.nested2.mypackage' message_type < name: 'ExampleMessage' field < name: 'str' label: LABEL_OPTIONAL type: TYPE_STRING number: 1 > > `) for _, name := range []string{ "nested.nested2.mypackage.ExampleMessage", "nested2.mypackage.ExampleMessage", "mypackage.ExampleMessage", "ExampleMessage", } { msg, err := reg.LookupMsg("nested.nested2.mypackage", name) if err != nil { t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", ".nested.nested2.mypackage", name, err) return } if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", ".nested.nested2.mypackage", name, got, want) } } for _, loc := range []string{ ".nested.nested2.mypackage", "nested.nested2.mypackage", ".nested.nested2", "nested.nested2", ".nested", "nested", ".", "", "somewhere.else", } { name := "nested.nested2.mypackage.ExampleMessage" msg, err := reg.LookupMsg(loc, name) if err != nil { t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err) return } if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want) } } for _, loc := range []string{ ".nested.nested2.mypackage", "nested.nested2.mypackage", ".nested.nested2", "nested.nested2", ".nested", "nested", } { name := "nested2.mypackage.ExampleMessage" msg, err := reg.LookupMsg(loc, name) if err != nil { t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err) return } if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want) } } } func TestLoadWithInconsistentTargetPackage(t *testing.T) { for _, spec := range []struct { req string consistent bool }{ // root package, no explicit go package { req: ` file_to_generate: 'a.proto' file_to_generate: 'b.proto' proto_file < name: 'a.proto' message_type < name: 'A' > service < name: "AService" method < name: "Meth" input_type: "A" output_type: "A" options < [google.api.http] < post: "/v1/a" body: "*" > > > > > proto_file < name: 'b.proto' message_type < name: 'B' > service < name: "BService" method < name: "Meth" input_type: "B" output_type: "B" options < [google.api.http] < post: "/v1/b" body: "*" > > > > > `, consistent: false, }, // named package, no explicit go package { req: ` file_to_generate: 'a.proto' file_to_generate: 'b.proto' proto_file < name: 'a.proto' package: 'example.foo' message_type < name: 'A' > service < name: "AService" method < name: "Meth" input_type: "A" output_type: "A" options < [google.api.http] < post: "/v1/a" body: "*" > > > > > proto_file < name: 'b.proto' package: 'example.foo' message_type < name: 'B' > service < name: "BService" method < name: "Meth" input_type: "B" output_type: "B" options < [google.api.http] < post: "/v1/b" body: "*" > > > > > `, consistent: true, }, // root package, explicit go package { req: ` file_to_generate: 'a.proto' file_to_generate: 'b.proto' proto_file < name: 'a.proto' options < go_package: 'foo' > message_type < name: 'A' > service < name: "AService" method < name: "Meth" input_type: "A" output_type: "A" options < [google.api.http] < post: "/v1/a" body: "*" > > > > > proto_file < name: 'b.proto' options < go_package: 'foo' > message_type < name: 'B' > service < name: "BService" method < name: "Meth" input_type: "B" output_type: "B" options < [google.api.http] < post: "/v1/b" body: "*" > > > > > `, consistent: true, }, // named package, explicit go package { req: ` file_to_generate: 'a.proto' file_to_generate: 'b.proto' proto_file < name: 'a.proto' package: 'example.foo' options < go_package: 'foo' > message_type < name: 'A' > service < name: "AService" method < name: "Meth" input_type: "A" output_type: "A" options < [google.api.http] < post: "/v1/a" body: "*" > > > > > proto_file < name: 'b.proto' package: 'example.foo' options < go_package: 'foo' > message_type < name: 'B' > service < name: "BService" method < name: "Meth" input_type: "B" output_type: "B" options < [google.api.http] < post: "/v1/b" body: "*" > > > > > `, consistent: true, }, } { reg := NewRegistry() err := load(t, reg, spec.req) if got, want := err == nil, spec.consistent; got != want { if want { t.Errorf("reg.Load(%s) failed with %v; want success", spec.req, err) continue } t.Errorf("reg.Load(%s) succeeded; want an package inconsistency error", spec.req) } } } func TestLoadOverridedPackageName(t *testing.T) { reg := NewRegistry() loadFile(t, reg, ` name: 'example.proto' package: 'example' options < go_package: 'example.com/xyz;pb' > `) file := reg.files["example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: "example.com/xyz", Name: "pb"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadSetInputPath(t *testing.T) { reg := NewRegistry() reg.SetImportPath("foo/examplepb") loadFile(t, reg, ` name: 'example.proto' package: 'example' `) file := reg.files["example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: ".", Name: "examplepb"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestLoadGoPackageInputPath(t *testing.T) { reg := NewRegistry() reg.SetImportPath("examplepb") loadFile(t, reg, ` name: 'example.proto' package: 'example' options < go_package: 'example.com/xyz;pb' > `) file := reg.files["example.proto"] if file == nil { t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") return } wantPkg := GoPackage{Path: "example.com/xyz", Name: "pb"} if got, want := file.GoPkg, wantPkg; got != want { t.Errorf("file.GoPkg = %#v; want %#v", got, want) } } func TestUnboundExternalHTTPRules(t *testing.T) { reg := NewRegistry() methodName := ".example.ExampleService.Echo" reg.AddExternalHTTPRule(methodName, nil) assertStringSlice(t, "unbound external HTTP rules", reg.UnboundExternalHTTPRules(), []string{methodName}) loadFile(t, reg, ` name: "path/to/example.proto", package: "example" message_type < name: "StringMessage" field < name: "string" number: 1 label: LABEL_OPTIONAL type: TYPE_STRING > > service < name: "ExampleService" method < name: "Echo" input_type: "StringMessage" output_type: "StringMessage" > > `) assertStringSlice(t, "unbound external HTTP rules", reg.UnboundExternalHTTPRules(), []string{}) } func assertStringSlice(t *testing.T, message string, got, want []string) { if len(got) != len(want) { t.Errorf("%s = %#v len(%d); want %#v len(%d)", message, got, len(got), want, len(want)) } for i := range want { if got[i] != want[i] { t.Errorf("%s[%d] = %#v; want %#v", message, i, got[i], want[i]) } } }