mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-20 06:39:56 +00:00
Since the release of v1.20, we have warned that it be required that the import path for every generated package be specified. This CL simplifies the mechanism for specifying such information to just the "M" command-line flags and the "go_package" options in the .proto source files, where the former takes precedence over the latter. Changes made: * Remove "import_prefix" and "import_path" flags. * Make the Go import path and package name derivation logic simpler where both "M" flags and "go_package" options are parsed the same way by calling the common splitImportPathAndPackageName function, and where "M" flags take precedence over "go_package" options. The exception to this occurs when neither "M" nor "go_package" specify an explicit Go package name, where the derivation may be non-intuitive. See the "NOTE" comment for the rationale for this behavior, which actually matches what was already the case. * Remove the pathTypeLegacy output mode and make pathTypeImport the default. The pathTypeLegacy mode becomes even more non-sensible now that we require that the import path always be provided. * Remove the baseName function. After deleting unsupported functionality, this seems to only be used by GeneratedFile.QualifiedGoIdent. However, that method does not need to create stable package names since they are only used locally within the generated file. They only need to guarantee the property of validity and uniqueness. * Remove the warn function since there are no more warnings. Change-Id: Ic95fb3cde5ffcb71bbcc829fcff34369758cebef Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/301953 Trust: Joe Tsai <joetsai@digital-static.net> Reviewed-by: Damien Neil <dneil@google.com>
339 lines
9.4 KiB
Go
339 lines
9.4 KiB
Go
// 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.
|
|
|
|
package protogen
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
|
|
"google.golang.org/protobuf/types/descriptorpb"
|
|
"google.golang.org/protobuf/types/pluginpb"
|
|
)
|
|
|
|
func TestPluginParameters(t *testing.T) {
|
|
var flags flag.FlagSet
|
|
value := flags.Int("integer", 0, "")
|
|
const params = "integer=2"
|
|
_, err := Options{
|
|
ParamFunc: flags.Set,
|
|
}.New(&pluginpb.CodeGeneratorRequest{
|
|
Parameter: proto.String(params),
|
|
})
|
|
if err != nil {
|
|
t.Errorf("New(generator parameters %q): %v", params, err)
|
|
}
|
|
if *value != 2 {
|
|
t.Errorf("New(generator parameters %q): integer=%v, want 2", params, *value)
|
|
}
|
|
}
|
|
|
|
func TestPluginParameterErrors(t *testing.T) {
|
|
for _, parameter := range []string{
|
|
"unknown=1",
|
|
"boolean=error",
|
|
} {
|
|
var flags flag.FlagSet
|
|
flags.Bool("boolean", false, "")
|
|
_, err := Options{
|
|
ParamFunc: flags.Set,
|
|
}.New(&pluginpb.CodeGeneratorRequest{
|
|
Parameter: proto.String(parameter),
|
|
})
|
|
if err == nil {
|
|
t.Errorf("New(generator parameters %q): want error, got nil", parameter)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNoGoPackage(t *testing.T) {
|
|
_, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
|
|
ProtoFile: []*descriptorpb.FileDescriptorProto{
|
|
{
|
|
Name: proto.String("testdata/go_package/no_go_package.proto"),
|
|
Syntax: proto.String(protoreflect.Proto3.String()),
|
|
Package: proto.String("goproto.testdata"),
|
|
},
|
|
},
|
|
})
|
|
if err == nil {
|
|
t.Fatalf("missing go_package option: New(req) = nil, want error")
|
|
}
|
|
}
|
|
|
|
func TestInvalidImportPath(t *testing.T) {
|
|
_, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
|
|
ProtoFile: []*descriptorpb.FileDescriptorProto{
|
|
{
|
|
Name: proto.String("testdata/go_package/no_go_package.proto"),
|
|
Syntax: proto.String(protoreflect.Proto3.String()),
|
|
Package: proto.String("goproto.testdata"),
|
|
Options: &descriptorpb.FileOptions{
|
|
GoPackage: proto.String("foo"),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
if err == nil {
|
|
t.Fatalf("missing go_package option: New(req) = nil, want error")
|
|
}
|
|
}
|
|
|
|
func TestPackageNamesAndPaths(t *testing.T) {
|
|
const (
|
|
filename = "dir/filename.proto"
|
|
protoPackageName = "proto.package"
|
|
)
|
|
for _, test := range []struct {
|
|
desc string
|
|
parameter string
|
|
goPackageOption string
|
|
generate bool
|
|
wantPackageName GoPackageName
|
|
wantImportPath GoImportPath
|
|
wantFilename string
|
|
}{
|
|
{
|
|
desc: "go_package option sets import path",
|
|
goPackageOption: "golang.org/x/foo",
|
|
generate: true,
|
|
wantPackageName: "foo",
|
|
wantImportPath: "golang.org/x/foo",
|
|
wantFilename: "golang.org/x/foo/filename",
|
|
},
|
|
{
|
|
desc: "go_package option sets import path and package",
|
|
goPackageOption: "golang.org/x/foo;bar",
|
|
generate: true,
|
|
wantPackageName: "bar",
|
|
wantImportPath: "golang.org/x/foo",
|
|
wantFilename: "golang.org/x/foo/filename",
|
|
},
|
|
{
|
|
desc: "command line sets import path for a file",
|
|
parameter: "Mdir/filename.proto=golang.org/x/bar",
|
|
goPackageOption: "golang.org/x/foo",
|
|
generate: true,
|
|
wantPackageName: "foo",
|
|
wantImportPath: "golang.org/x/bar",
|
|
wantFilename: "golang.org/x/bar/filename",
|
|
},
|
|
{
|
|
desc: "command line sets import path for a file with package name specified",
|
|
parameter: "Mdir/filename.proto=golang.org/x/bar;bar",
|
|
goPackageOption: "golang.org/x/foo",
|
|
generate: true,
|
|
wantPackageName: "bar",
|
|
wantImportPath: "golang.org/x/bar",
|
|
wantFilename: "golang.org/x/bar/filename",
|
|
},
|
|
{
|
|
desc: "module option set",
|
|
parameter: "module=golang.org/x",
|
|
goPackageOption: "golang.org/x/foo",
|
|
generate: false,
|
|
wantPackageName: "foo",
|
|
wantImportPath: "golang.org/x/foo",
|
|
wantFilename: "foo/filename",
|
|
},
|
|
{
|
|
desc: "paths=import uses import path from command line",
|
|
parameter: "paths=import,Mdir/filename.proto=golang.org/x/bar",
|
|
goPackageOption: "golang.org/x/foo",
|
|
generate: true,
|
|
wantPackageName: "foo",
|
|
wantImportPath: "golang.org/x/bar",
|
|
wantFilename: "golang.org/x/bar/filename",
|
|
},
|
|
{
|
|
desc: "module option implies paths=import",
|
|
parameter: "module=golang.org/x,Mdir/filename.proto=golang.org/x/foo",
|
|
generate: false,
|
|
wantPackageName: "foo",
|
|
wantImportPath: "golang.org/x/foo",
|
|
wantFilename: "foo/filename",
|
|
},
|
|
} {
|
|
context := fmt.Sprintf(`
|
|
TEST: %v
|
|
--go_out=%v:.
|
|
file %q: generate=%v
|
|
option go_package = %q;
|
|
|
|
`,
|
|
test.desc, test.parameter, filename, test.generate, test.goPackageOption)
|
|
|
|
req := &pluginpb.CodeGeneratorRequest{
|
|
Parameter: proto.String(test.parameter),
|
|
ProtoFile: []*descriptorpb.FileDescriptorProto{
|
|
{
|
|
Name: proto.String(filename),
|
|
Package: proto.String(protoPackageName),
|
|
Options: &descriptorpb.FileOptions{
|
|
GoPackage: proto.String(test.goPackageOption),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if test.generate {
|
|
req.FileToGenerate = []string{filename}
|
|
}
|
|
gen, err := Options{}.New(req)
|
|
if err != nil {
|
|
t.Errorf("%vNew(req) = %v", context, err)
|
|
continue
|
|
}
|
|
gotFile, ok := gen.FilesByPath[filename]
|
|
if !ok {
|
|
t.Errorf("%v%v: missing file info", context, filename)
|
|
continue
|
|
}
|
|
if got, want := gotFile.GoPackageName, test.wantPackageName; got != want {
|
|
t.Errorf("%vGoPackageName=%v, want %v", context, got, want)
|
|
}
|
|
if got, want := gotFile.GoImportPath, test.wantImportPath; got != want {
|
|
t.Errorf("%vGoImportPath=%v, want %v", context, got, want)
|
|
}
|
|
gen.NewGeneratedFile(gotFile.GeneratedFilenamePrefix, "")
|
|
resp := gen.Response()
|
|
if got, want := resp.File[0].GetName(), test.wantFilename; got != want {
|
|
t.Errorf("%vgenerated filename=%v, want %v", context, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPackageNameInference(t *testing.T) {
|
|
gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
|
|
Parameter: proto.String("Mdir/file1.proto=path/to/file1"),
|
|
ProtoFile: []*descriptorpb.FileDescriptorProto{
|
|
{
|
|
Name: proto.String("dir/file1.proto"),
|
|
Package: proto.String("proto.package"),
|
|
},
|
|
{
|
|
Name: proto.String("dir/file2.proto"),
|
|
Package: proto.String("proto.package"),
|
|
Options: &descriptorpb.FileOptions{
|
|
GoPackage: proto.String("path/to/file2"),
|
|
},
|
|
},
|
|
},
|
|
FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("New(req) = %v", err)
|
|
}
|
|
if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
|
|
t.Errorf("missing file info for dir/file1.proto")
|
|
} else if f1.GoPackageName != "file1" {
|
|
t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
|
|
}
|
|
}
|
|
|
|
func TestInconsistentPackageNames(t *testing.T) {
|
|
_, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
|
|
ProtoFile: []*descriptorpb.FileDescriptorProto{
|
|
{
|
|
Name: proto.String("dir/file1.proto"),
|
|
Package: proto.String("proto.package"),
|
|
Options: &descriptorpb.FileOptions{
|
|
GoPackage: proto.String("golang.org/x/foo"),
|
|
},
|
|
},
|
|
{
|
|
Name: proto.String("dir/file2.proto"),
|
|
Package: proto.String("proto.package"),
|
|
Options: &descriptorpb.FileOptions{
|
|
GoPackage: proto.String("golang.org/x/foo;bar"),
|
|
},
|
|
},
|
|
},
|
|
FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
|
|
})
|
|
if err == nil {
|
|
t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
|
|
}
|
|
}
|
|
|
|
func TestImports(t *testing.T) {
|
|
gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
|
|
g.P("package foo")
|
|
g.P()
|
|
for _, importPath := range []GoImportPath{
|
|
"golang.org/x/foo",
|
|
// Multiple references to the same package.
|
|
"golang.org/x/bar",
|
|
"golang.org/x/bar",
|
|
// Reference to a different package with the same basename.
|
|
"golang.org/y/bar",
|
|
"golang.org/x/baz",
|
|
// Reference to a package conflicting with a predeclared identifier.
|
|
"golang.org/z/string",
|
|
} {
|
|
g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
|
|
}
|
|
want := `package foo
|
|
|
|
import (
|
|
bar "golang.org/x/bar"
|
|
baz "golang.org/x/baz"
|
|
bar1 "golang.org/y/bar"
|
|
string1 "golang.org/z/string"
|
|
)
|
|
|
|
var _ = X // "golang.org/x/foo"
|
|
var _ = bar.X // "golang.org/x/bar"
|
|
var _ = bar.X // "golang.org/x/bar"
|
|
var _ = bar1.X // "golang.org/y/bar"
|
|
var _ = baz.X // "golang.org/x/baz"
|
|
var _ = string1.X // "golang.org/z/string"
|
|
`
|
|
got, err := g.Content()
|
|
if err != nil {
|
|
t.Fatalf("g.Content() = %v", err)
|
|
}
|
|
if diff := cmp.Diff(string(want), string(got)); diff != "" {
|
|
t.Fatalf("content mismatch (-want +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
func TestImportRewrites(t *testing.T) {
|
|
gen, err := Options{
|
|
ImportRewriteFunc: func(i GoImportPath) GoImportPath {
|
|
return "prefix/" + i
|
|
},
|
|
}.New(&pluginpb.CodeGeneratorRequest{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
|
|
g.P("package foo")
|
|
g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
|
|
want := `package foo
|
|
|
|
import (
|
|
bar "prefix/golang.org/x/bar"
|
|
)
|
|
|
|
var _ = bar.X
|
|
`
|
|
got, err := g.Content()
|
|
if err != nil {
|
|
t.Fatalf("g.Content() = %v", err)
|
|
}
|
|
if diff := cmp.Diff(string(want), string(got)); diff != "" {
|
|
t.Fatalf("content mismatch (-want +got):\n%s", diff)
|
|
}
|
|
}
|