blob: 5cc3d66252817c547e333ad4e7fe0a39268c0f7a [file] [log] [blame]
Damien Neil220c2022018-08-15 11:24:18 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package protogen
6
7import (
Damien Neil3cf6e622018-09-11 13:53:14 -07008 "flag"
Damien Neil082ce922018-09-06 10:23:53 -07009 "fmt"
Damien Neil220c2022018-08-15 11:24:18 -070010 "testing"
11
Joe Tsai94a85102019-03-12 21:28:30 -070012 "github.com/google/go-cmp/cmp"
Joe Tsaie0daf312020-02-25 12:51:10 -080013
Damien Neila8a2cea2019-07-10 16:17:16 -070014 "google.golang.org/protobuf/proto"
Damien Neile89e6242019-05-13 23:55:40 -070015 "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsaie1f8d502018-11-26 18:55:29 -080016
Joe Tsaia95b29f2019-05-16 12:47:20 -070017 "google.golang.org/protobuf/types/descriptorpb"
18 "google.golang.org/protobuf/types/pluginpb"
Damien Neil220c2022018-08-15 11:24:18 -070019)
20
Damien Neile358d432020-03-06 13:58:41 -080021func init() {
22 warnings = false // avoid spam in tests
23}
24
Damien Neil3cf6e622018-09-11 13:53:14 -070025func TestPluginParameters(t *testing.T) {
26 var flags flag.FlagSet
27 value := flags.Int("integer", 0, "")
Damien Neil3cf6e622018-09-11 13:53:14 -070028 const params = "integer=2"
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080029 _, err := Options{
30 ParamFunc: flags.Set,
31 }.New(&pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -070032 Parameter: proto.String(params),
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080033 })
Damien Neil3cf6e622018-09-11 13:53:14 -070034 if err != nil {
35 t.Errorf("New(generator parameters %q): %v", params, err)
36 }
37 if *value != 2 {
38 t.Errorf("New(generator parameters %q): integer=%v, want 2", params, *value)
39 }
40}
41
42func TestPluginParameterErrors(t *testing.T) {
43 for _, parameter := range []string{
44 "unknown=1",
45 "boolean=error",
46 } {
47 var flags flag.FlagSet
48 flags.Bool("boolean", false, "")
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080049 _, err := Options{
Damien Neil3cf6e622018-09-11 13:53:14 -070050 ParamFunc: flags.Set,
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080051 }.New(&pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -070052 Parameter: proto.String(parameter),
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080053 })
Damien Neil3cf6e622018-09-11 13:53:14 -070054 if err == nil {
55 t.Errorf("New(generator parameters %q): want error, got nil", parameter)
56 }
57 }
58}
59
Joe Tsai94a85102019-03-12 21:28:30 -070060func TestNoGoPackage(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080061 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsai94a85102019-03-12 21:28:30 -070062 ProtoFile: []*descriptorpb.FileDescriptorProto{
63 {
Damien Neila8a2cea2019-07-10 16:17:16 -070064 Name: proto.String("testdata/go_package/no_go_package.proto"),
65 Syntax: proto.String(protoreflect.Proto3.String()),
66 Package: proto.String("goproto.testdata"),
Joe Tsai94a85102019-03-12 21:28:30 -070067 },
68 {
Damien Neila8a2cea2019-07-10 16:17:16 -070069 Name: proto.String("testdata/go_package/no_go_package_import.proto"),
70 Syntax: proto.String(protoreflect.Proto3.String()),
71 Package: proto.String("goproto.testdata"),
Joe Tsai25fc6fb2020-02-10 14:04:25 -080072 Dependency: []string{"testdata/go_package/no_go_package.proto"},
Joe Tsai94a85102019-03-12 21:28:30 -070073 },
74 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080075 })
Damien Neil220c2022018-08-15 11:24:18 -070076 if err != nil {
77 t.Fatal(err)
78 }
Joe Tsai94a85102019-03-12 21:28:30 -070079
80 for i, f := range gen.Files {
81 if got, want := string(f.GoPackageName), "goproto_testdata"; got != want {
82 t.Errorf("gen.Files[%d].GoPackageName = %v, want %v", i, got, want)
Damien Neil220c2022018-08-15 11:24:18 -070083 }
Joe Tsai94a85102019-03-12 21:28:30 -070084 if got, want := string(f.GoImportPath), "testdata/go_package"; got != want {
85 t.Errorf("gen.Files[%d].GoImportPath = %v, want %v", i, got, want)
Damien Neil220c2022018-08-15 11:24:18 -070086 }
87 }
88}
89
Damien Neil082ce922018-09-06 10:23:53 -070090func TestPackageNamesAndPaths(t *testing.T) {
91 const (
92 filename = "dir/filename.proto"
93 protoPackageName = "proto.package"
94 )
95 for _, test := range []struct {
96 desc string
97 parameter string
98 goPackageOption string
99 generate bool
100 wantPackageName GoPackageName
101 wantImportPath GoImportPath
102 wantFilenamePrefix string
103 }{
104 {
105 desc: "no parameters, no go_package option",
106 generate: true,
107 wantPackageName: "proto_package",
108 wantImportPath: "dir",
109 wantFilenamePrefix: "dir/filename",
110 },
111 {
112 desc: "go_package option sets import path",
113 goPackageOption: "golang.org/x/foo",
114 generate: true,
115 wantPackageName: "foo",
116 wantImportPath: "golang.org/x/foo",
117 wantFilenamePrefix: "golang.org/x/foo/filename",
118 },
119 {
120 desc: "go_package option sets import path and package",
121 goPackageOption: "golang.org/x/foo;bar",
122 generate: true,
123 wantPackageName: "bar",
124 wantImportPath: "golang.org/x/foo",
125 wantFilenamePrefix: "golang.org/x/foo/filename",
126 },
127 {
128 desc: "go_package option sets package",
129 goPackageOption: "foo",
130 generate: true,
131 wantPackageName: "foo",
132 wantImportPath: "dir",
133 wantFilenamePrefix: "dir/filename",
134 },
135 {
136 desc: "command line sets import path for a file",
137 parameter: "Mdir/filename.proto=golang.org/x/bar",
138 goPackageOption: "golang.org/x/foo",
139 generate: true,
140 wantPackageName: "foo",
141 wantImportPath: "golang.org/x/bar",
142 wantFilenamePrefix: "golang.org/x/foo/filename",
143 },
144 {
145 desc: "import_path parameter sets import path of generated files",
146 parameter: "import_path=golang.org/x/bar",
147 goPackageOption: "golang.org/x/foo",
148 generate: true,
149 wantPackageName: "foo",
150 wantImportPath: "golang.org/x/bar",
151 wantFilenamePrefix: "golang.org/x/foo/filename",
152 },
153 {
154 desc: "import_path parameter does not set import path of dependencies",
155 parameter: "import_path=golang.org/x/bar",
156 goPackageOption: "golang.org/x/foo",
157 generate: false,
158 wantPackageName: "foo",
159 wantImportPath: "golang.org/x/foo",
160 wantFilenamePrefix: "golang.org/x/foo/filename",
161 },
162 } {
163 context := fmt.Sprintf(`
164TEST: %v
165 --go_out=%v:.
166 file %q: generate=%v
167 option go_package = %q;
168
169 `,
170 test.desc, test.parameter, filename, test.generate, test.goPackageOption)
171
172 req := &pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -0700173 Parameter: proto.String(test.parameter),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800174 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700175 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700176 Name: proto.String(filename),
177 Package: proto.String(protoPackageName),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800178 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700179 GoPackage: proto.String(test.goPackageOption),
Damien Neil082ce922018-09-06 10:23:53 -0700180 },
181 },
182 },
183 }
184 if test.generate {
185 req.FileToGenerate = []string{filename}
186 }
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800187 gen, err := Options{}.New(req)
Damien Neil082ce922018-09-06 10:23:53 -0700188 if err != nil {
189 t.Errorf("%vNew(req) = %v", context, err)
190 continue
191 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700192 gotFile, ok := gen.FilesByPath[filename]
Damien Neil082ce922018-09-06 10:23:53 -0700193 if !ok {
194 t.Errorf("%v%v: missing file info", context, filename)
195 continue
196 }
197 if got, want := gotFile.GoPackageName, test.wantPackageName; got != want {
198 t.Errorf("%vGoPackageName=%v, want %v", context, got, want)
199 }
200 if got, want := gotFile.GoImportPath, test.wantImportPath; got != want {
201 t.Errorf("%vGoImportPath=%v, want %v", context, got, want)
202 }
203 if got, want := gotFile.GeneratedFilenamePrefix, test.wantFilenamePrefix; got != want {
204 t.Errorf("%vGeneratedFilenamePrefix=%v, want %v", context, got, want)
205 }
206 }
207}
208
209func TestPackageNameInference(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800210 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800211 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700212 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700213 Name: proto.String("dir/file1.proto"),
214 Package: proto.String("proto.package"),
Damien Neil082ce922018-09-06 10:23:53 -0700215 },
216 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700217 Name: proto.String("dir/file2.proto"),
218 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800219 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700220 GoPackage: proto.String("foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700221 },
222 },
223 },
224 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800225 })
Damien Neil082ce922018-09-06 10:23:53 -0700226 if err != nil {
227 t.Fatalf("New(req) = %v", err)
228 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700229 if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
Damien Neil082ce922018-09-06 10:23:53 -0700230 t.Errorf("missing file info for dir/file1.proto")
231 } else if f1.GoPackageName != "foo" {
232 t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
233 }
234}
235
236func TestInconsistentPackageNames(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800237 _, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800238 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700239 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700240 Name: proto.String("dir/file1.proto"),
241 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800242 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700243 GoPackage: proto.String("golang.org/x/foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700244 },
245 },
246 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700247 Name: proto.String("dir/file2.proto"),
248 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800249 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700250 GoPackage: proto.String("golang.org/x/foo;bar"),
Damien Neil082ce922018-09-06 10:23:53 -0700251 },
252 },
253 },
254 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800255 })
Damien Neil082ce922018-09-06 10:23:53 -0700256 if err == nil {
257 t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
258 }
259}
260
Damien Neild9016772018-08-23 14:39:30 -0700261func TestImports(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800262 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{})
Damien Neild9016772018-08-23 14:39:30 -0700263 if err != nil {
264 t.Fatal(err)
265 }
266 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
267 g.P("package foo")
268 g.P()
269 for _, importPath := range []GoImportPath{
270 "golang.org/x/foo",
271 // Multiple references to the same package.
272 "golang.org/x/bar",
273 "golang.org/x/bar",
274 // Reference to a different package with the same basename.
275 "golang.org/y/bar",
276 "golang.org/x/baz",
Damien Neil87214662018-10-05 11:23:35 -0700277 // Reference to a package conflicting with a predeclared identifier.
278 "golang.org/z/string",
Damien Neild9016772018-08-23 14:39:30 -0700279 } {
280 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
281 }
282 want := `package foo
283
284import (
285 bar "golang.org/x/bar"
Damien Neild9016772018-08-23 14:39:30 -0700286 baz "golang.org/x/baz"
Damien Neil1ec33152018-09-13 13:12:36 -0700287 bar1 "golang.org/y/bar"
Damien Neil87214662018-10-05 11:23:35 -0700288 string1 "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700289)
290
Damien Neil87214662018-10-05 11:23:35 -0700291var _ = X // "golang.org/x/foo"
292var _ = bar.X // "golang.org/x/bar"
293var _ = bar.X // "golang.org/x/bar"
294var _ = bar1.X // "golang.org/y/bar"
295var _ = baz.X // "golang.org/x/baz"
296var _ = string1.X // "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700297`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800298 got, err := g.Content()
Damien Neild9016772018-08-23 14:39:30 -0700299 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800300 t.Fatalf("g.Content() = %v", err)
Damien Neild9016772018-08-23 14:39:30 -0700301 }
Joe Tsai94a85102019-03-12 21:28:30 -0700302 if diff := cmp.Diff(string(want), string(got)); diff != "" {
303 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neild9016772018-08-23 14:39:30 -0700304 }
305}
306
Damien Neil1fa8ab02018-09-27 15:51:05 -0700307func TestImportRewrites(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800308 gen, err := Options{
Damien Neil1fa8ab02018-09-27 15:51:05 -0700309 ImportRewriteFunc: func(i GoImportPath) GoImportPath {
310 return "prefix/" + i
311 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800312 }.New(&pluginpb.CodeGeneratorRequest{})
Damien Neil1fa8ab02018-09-27 15:51:05 -0700313 if err != nil {
314 t.Fatal(err)
315 }
316 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
317 g.P("package foo")
318 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
319 want := `package foo
320
Joe Tsai94a85102019-03-12 21:28:30 -0700321import (
322 bar "prefix/golang.org/x/bar"
323)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700324
325var _ = bar.X
326`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800327 got, err := g.Content()
Damien Neil1fa8ab02018-09-27 15:51:05 -0700328 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800329 t.Fatalf("g.Content() = %v", err)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700330 }
Joe Tsai94a85102019-03-12 21:28:30 -0700331 if diff := cmp.Diff(string(want), string(got)); diff != "" {
332 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neil220c2022018-08-15 11:24:18 -0700333 }
334}