blob: 0967516e53f43b246bf3158e89c57d2e47951a99 [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 {
Joe Tsai6ad8e632020-03-18 00:59:09 -0700145 desc: "command line sets import path for a file with package name specified",
146 parameter: "Mdir/filename.proto=golang.org/x/bar;bar",
147 goPackageOption: "golang.org/x/foo",
148 generate: true,
149 wantPackageName: "bar",
150 wantImportPath: "golang.org/x/bar",
151 wantFilenamePrefix: "golang.org/x/foo/filename",
152 },
153 {
Damien Neil082ce922018-09-06 10:23:53 -0700154 desc: "import_path parameter sets import path of generated files",
155 parameter: "import_path=golang.org/x/bar",
156 goPackageOption: "golang.org/x/foo",
157 generate: true,
158 wantPackageName: "foo",
159 wantImportPath: "golang.org/x/bar",
160 wantFilenamePrefix: "golang.org/x/foo/filename",
161 },
162 {
163 desc: "import_path parameter does not set import path of dependencies",
164 parameter: "import_path=golang.org/x/bar",
165 goPackageOption: "golang.org/x/foo",
166 generate: false,
167 wantPackageName: "foo",
168 wantImportPath: "golang.org/x/foo",
169 wantFilenamePrefix: "golang.org/x/foo/filename",
170 },
Damien Neilaadba562020-02-15 14:28:51 -0800171 {
172 desc: "paths=import uses import path from command line",
173 parameter: "paths=import,Mdir/filename.proto=golang.org/x/bar",
174 goPackageOption: "golang.org/x/foo",
175 generate: true,
176 wantPackageName: "foo",
177 wantImportPath: "golang.org/x/bar",
178 wantFilenamePrefix: "golang.org/x/bar/filename",
179 },
Damien Neil082ce922018-09-06 10:23:53 -0700180 } {
181 context := fmt.Sprintf(`
182TEST: %v
183 --go_out=%v:.
184 file %q: generate=%v
185 option go_package = %q;
186
187 `,
188 test.desc, test.parameter, filename, test.generate, test.goPackageOption)
189
190 req := &pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -0700191 Parameter: proto.String(test.parameter),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800192 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700193 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700194 Name: proto.String(filename),
195 Package: proto.String(protoPackageName),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800196 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700197 GoPackage: proto.String(test.goPackageOption),
Damien Neil082ce922018-09-06 10:23:53 -0700198 },
199 },
200 },
201 }
202 if test.generate {
203 req.FileToGenerate = []string{filename}
204 }
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800205 gen, err := Options{}.New(req)
Damien Neil082ce922018-09-06 10:23:53 -0700206 if err != nil {
207 t.Errorf("%vNew(req) = %v", context, err)
208 continue
209 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700210 gotFile, ok := gen.FilesByPath[filename]
Damien Neil082ce922018-09-06 10:23:53 -0700211 if !ok {
212 t.Errorf("%v%v: missing file info", context, filename)
213 continue
214 }
215 if got, want := gotFile.GoPackageName, test.wantPackageName; got != want {
216 t.Errorf("%vGoPackageName=%v, want %v", context, got, want)
217 }
218 if got, want := gotFile.GoImportPath, test.wantImportPath; got != want {
219 t.Errorf("%vGoImportPath=%v, want %v", context, got, want)
220 }
221 if got, want := gotFile.GeneratedFilenamePrefix, test.wantFilenamePrefix; got != want {
222 t.Errorf("%vGeneratedFilenamePrefix=%v, want %v", context, got, want)
223 }
224 }
225}
226
227func TestPackageNameInference(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800228 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800229 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700230 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700231 Name: proto.String("dir/file1.proto"),
232 Package: proto.String("proto.package"),
Damien Neil082ce922018-09-06 10:23:53 -0700233 },
234 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700235 Name: proto.String("dir/file2.proto"),
236 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800237 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700238 GoPackage: proto.String("foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700239 },
240 },
241 },
242 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800243 })
Damien Neil082ce922018-09-06 10:23:53 -0700244 if err != nil {
245 t.Fatalf("New(req) = %v", err)
246 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700247 if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
Damien Neil082ce922018-09-06 10:23:53 -0700248 t.Errorf("missing file info for dir/file1.proto")
249 } else if f1.GoPackageName != "foo" {
250 t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
251 }
252}
253
254func TestInconsistentPackageNames(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800255 _, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800256 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700257 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700258 Name: proto.String("dir/file1.proto"),
259 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800260 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700261 GoPackage: proto.String("golang.org/x/foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700262 },
263 },
264 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700265 Name: proto.String("dir/file2.proto"),
266 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800267 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700268 GoPackage: proto.String("golang.org/x/foo;bar"),
Damien Neil082ce922018-09-06 10:23:53 -0700269 },
270 },
271 },
272 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800273 })
Damien Neil082ce922018-09-06 10:23:53 -0700274 if err == nil {
275 t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
276 }
277}
278
Damien Neild9016772018-08-23 14:39:30 -0700279func TestImports(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800280 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{})
Damien Neild9016772018-08-23 14:39:30 -0700281 if err != nil {
282 t.Fatal(err)
283 }
284 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
285 g.P("package foo")
286 g.P()
287 for _, importPath := range []GoImportPath{
288 "golang.org/x/foo",
289 // Multiple references to the same package.
290 "golang.org/x/bar",
291 "golang.org/x/bar",
292 // Reference to a different package with the same basename.
293 "golang.org/y/bar",
294 "golang.org/x/baz",
Damien Neil87214662018-10-05 11:23:35 -0700295 // Reference to a package conflicting with a predeclared identifier.
296 "golang.org/z/string",
Damien Neild9016772018-08-23 14:39:30 -0700297 } {
298 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
299 }
300 want := `package foo
301
302import (
303 bar "golang.org/x/bar"
Damien Neild9016772018-08-23 14:39:30 -0700304 baz "golang.org/x/baz"
Damien Neil1ec33152018-09-13 13:12:36 -0700305 bar1 "golang.org/y/bar"
Damien Neil87214662018-10-05 11:23:35 -0700306 string1 "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700307)
308
Damien Neil87214662018-10-05 11:23:35 -0700309var _ = X // "golang.org/x/foo"
310var _ = bar.X // "golang.org/x/bar"
311var _ = bar.X // "golang.org/x/bar"
312var _ = bar1.X // "golang.org/y/bar"
313var _ = baz.X // "golang.org/x/baz"
314var _ = string1.X // "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700315`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800316 got, err := g.Content()
Damien Neild9016772018-08-23 14:39:30 -0700317 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800318 t.Fatalf("g.Content() = %v", err)
Damien Neild9016772018-08-23 14:39:30 -0700319 }
Joe Tsai94a85102019-03-12 21:28:30 -0700320 if diff := cmp.Diff(string(want), string(got)); diff != "" {
321 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neild9016772018-08-23 14:39:30 -0700322 }
323}
324
Damien Neil1fa8ab02018-09-27 15:51:05 -0700325func TestImportRewrites(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800326 gen, err := Options{
Damien Neil1fa8ab02018-09-27 15:51:05 -0700327 ImportRewriteFunc: func(i GoImportPath) GoImportPath {
328 return "prefix/" + i
329 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800330 }.New(&pluginpb.CodeGeneratorRequest{})
Damien Neil1fa8ab02018-09-27 15:51:05 -0700331 if err != nil {
332 t.Fatal(err)
333 }
334 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
335 g.P("package foo")
336 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
337 want := `package foo
338
Joe Tsai94a85102019-03-12 21:28:30 -0700339import (
340 bar "prefix/golang.org/x/bar"
341)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700342
343var _ = bar.X
344`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800345 got, err := g.Content()
Damien Neil1fa8ab02018-09-27 15:51:05 -0700346 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800347 t.Fatalf("g.Content() = %v", err)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700348 }
Joe Tsai94a85102019-03-12 21:28:30 -0700349 if diff := cmp.Diff(string(want), string(got)); diff != "" {
350 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neil220c2022018-08-15 11:24:18 -0700351 }
352}