blob: 4f5ceeda78f11ccfb9d7202ebfb558bc6366d6a6 [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 Neil3cf6e622018-09-11 13:53:14 -070021func TestPluginParameters(t *testing.T) {
22 var flags flag.FlagSet
23 value := flags.Int("integer", 0, "")
Damien Neil3cf6e622018-09-11 13:53:14 -070024 const params = "integer=2"
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080025 _, err := Options{
26 ParamFunc: flags.Set,
27 }.New(&pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -070028 Parameter: proto.String(params),
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080029 })
Damien Neil3cf6e622018-09-11 13:53:14 -070030 if err != nil {
31 t.Errorf("New(generator parameters %q): %v", params, err)
32 }
33 if *value != 2 {
34 t.Errorf("New(generator parameters %q): integer=%v, want 2", params, *value)
35 }
36}
37
38func TestPluginParameterErrors(t *testing.T) {
39 for _, parameter := range []string{
40 "unknown=1",
41 "boolean=error",
42 } {
43 var flags flag.FlagSet
44 flags.Bool("boolean", false, "")
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080045 _, err := Options{
Damien Neil3cf6e622018-09-11 13:53:14 -070046 ParamFunc: flags.Set,
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080047 }.New(&pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -070048 Parameter: proto.String(parameter),
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080049 })
Damien Neil3cf6e622018-09-11 13:53:14 -070050 if err == nil {
51 t.Errorf("New(generator parameters %q): want error, got nil", parameter)
52 }
53 }
54}
55
Joe Tsai94a85102019-03-12 21:28:30 -070056func TestNoGoPackage(t *testing.T) {
Joe Tsai8b366e82021-03-16 03:20:52 -070057 _, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsai94a85102019-03-12 21:28:30 -070058 ProtoFile: []*descriptorpb.FileDescriptorProto{
59 {
Damien Neila8a2cea2019-07-10 16:17:16 -070060 Name: proto.String("testdata/go_package/no_go_package.proto"),
61 Syntax: proto.String(protoreflect.Proto3.String()),
62 Package: proto.String("goproto.testdata"),
Joe Tsai94a85102019-03-12 21:28:30 -070063 },
Joe Tsai8b366e82021-03-16 03:20:52 -070064 },
65 })
66 if err == nil {
67 t.Fatalf("missing go_package option: New(req) = nil, want error")
68 }
69}
70
71func TestInvalidImportPath(t *testing.T) {
72 _, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
73 ProtoFile: []*descriptorpb.FileDescriptorProto{
Joe Tsai94a85102019-03-12 21:28:30 -070074 {
Joe Tsai8b366e82021-03-16 03:20:52 -070075 Name: proto.String("testdata/go_package/no_go_package.proto"),
76 Syntax: proto.String(protoreflect.Proto3.String()),
77 Package: proto.String("goproto.testdata"),
78 Options: &descriptorpb.FileOptions{
79 GoPackage: proto.String("foo"),
80 },
Joe Tsai94a85102019-03-12 21:28:30 -070081 },
82 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080083 })
Joe Tsai8b366e82021-03-16 03:20:52 -070084 if err == nil {
85 t.Fatalf("missing go_package option: New(req) = nil, want error")
Damien Neil220c2022018-08-15 11:24:18 -070086 }
87}
88
Damien Neil082ce922018-09-06 10:23:53 -070089func TestPackageNamesAndPaths(t *testing.T) {
90 const (
91 filename = "dir/filename.proto"
92 protoPackageName = "proto.package"
93 )
94 for _, test := range []struct {
Damien Neilffbc5fd2020-02-12 23:38:30 -080095 desc string
96 parameter string
97 goPackageOption string
98 generate bool
99 wantPackageName GoPackageName
100 wantImportPath GoImportPath
101 wantFilename string
Damien Neil082ce922018-09-06 10:23:53 -0700102 }{
103 {
Damien Neilffbc5fd2020-02-12 23:38:30 -0800104 desc: "go_package option sets import path",
105 goPackageOption: "golang.org/x/foo",
106 generate: true,
107 wantPackageName: "foo",
108 wantImportPath: "golang.org/x/foo",
109 wantFilename: "golang.org/x/foo/filename",
Damien Neil082ce922018-09-06 10:23:53 -0700110 },
111 {
Joe Tsai4c193d12021-05-04 11:03:42 -0700112 desc: "go_package option sets import path without slashes",
113 goPackageOption: "golang.org;foo",
114 generate: true,
115 wantPackageName: "foo",
116 wantImportPath: "golang.org",
117 wantFilename: "golang.org/filename",
118 },
119 {
Damien Neilffbc5fd2020-02-12 23:38:30 -0800120 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 wantFilename: "golang.org/x/foo/filename",
Damien Neil082ce922018-09-06 10:23:53 -0700126 },
127 {
Damien Neilffbc5fd2020-02-12 23:38:30 -0800128 desc: "command line sets import path for a file",
129 parameter: "Mdir/filename.proto=golang.org/x/bar",
130 goPackageOption: "golang.org/x/foo",
131 generate: true,
132 wantPackageName: "foo",
133 wantImportPath: "golang.org/x/bar",
Joe Tsai8b366e82021-03-16 03:20:52 -0700134 wantFilename: "golang.org/x/bar/filename",
Damien Neil082ce922018-09-06 10:23:53 -0700135 },
136 {
Damien Neilffbc5fd2020-02-12 23:38:30 -0800137 desc: "command line sets import path for a file with package name specified",
138 parameter: "Mdir/filename.proto=golang.org/x/bar;bar",
139 goPackageOption: "golang.org/x/foo",
140 generate: true,
141 wantPackageName: "bar",
142 wantImportPath: "golang.org/x/bar",
Joe Tsai8b366e82021-03-16 03:20:52 -0700143 wantFilename: "golang.org/x/bar/filename",
Damien Neil082ce922018-09-06 10:23:53 -0700144 },
Damien Neilaadba562020-02-15 14:28:51 -0800145 {
Damien Neilffbc5fd2020-02-12 23:38:30 -0800146 desc: "module option set",
147 parameter: "module=golang.org/x",
148 goPackageOption: "golang.org/x/foo",
149 generate: false,
150 wantPackageName: "foo",
151 wantImportPath: "golang.org/x/foo",
152 wantFilename: "foo/filename",
153 },
154 {
155 desc: "paths=import uses import path from command line",
156 parameter: "paths=import,Mdir/filename.proto=golang.org/x/bar",
157 goPackageOption: "golang.org/x/foo",
158 generate: true,
159 wantPackageName: "foo",
160 wantImportPath: "golang.org/x/bar",
161 wantFilename: "golang.org/x/bar/filename",
162 },
163 {
164 desc: "module option implies paths=import",
165 parameter: "module=golang.org/x,Mdir/filename.proto=golang.org/x/foo",
166 generate: false,
Joe Tsai8b366e82021-03-16 03:20:52 -0700167 wantPackageName: "foo",
Damien Neilffbc5fd2020-02-12 23:38:30 -0800168 wantImportPath: "golang.org/x/foo",
169 wantFilename: "foo/filename",
Damien Neilaadba562020-02-15 14:28:51 -0800170 },
Damien Neil082ce922018-09-06 10:23:53 -0700171 } {
172 context := fmt.Sprintf(`
173TEST: %v
174 --go_out=%v:.
175 file %q: generate=%v
176 option go_package = %q;
177
178 `,
179 test.desc, test.parameter, filename, test.generate, test.goPackageOption)
180
181 req := &pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -0700182 Parameter: proto.String(test.parameter),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800183 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700184 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700185 Name: proto.String(filename),
186 Package: proto.String(protoPackageName),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800187 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700188 GoPackage: proto.String(test.goPackageOption),
Damien Neil082ce922018-09-06 10:23:53 -0700189 },
190 },
191 },
192 }
193 if test.generate {
194 req.FileToGenerate = []string{filename}
195 }
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800196 gen, err := Options{}.New(req)
Damien Neil082ce922018-09-06 10:23:53 -0700197 if err != nil {
198 t.Errorf("%vNew(req) = %v", context, err)
199 continue
200 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700201 gotFile, ok := gen.FilesByPath[filename]
Damien Neil082ce922018-09-06 10:23:53 -0700202 if !ok {
203 t.Errorf("%v%v: missing file info", context, filename)
204 continue
205 }
206 if got, want := gotFile.GoPackageName, test.wantPackageName; got != want {
207 t.Errorf("%vGoPackageName=%v, want %v", context, got, want)
208 }
209 if got, want := gotFile.GoImportPath, test.wantImportPath; got != want {
210 t.Errorf("%vGoImportPath=%v, want %v", context, got, want)
211 }
Damien Neilffbc5fd2020-02-12 23:38:30 -0800212 gen.NewGeneratedFile(gotFile.GeneratedFilenamePrefix, "")
213 resp := gen.Response()
214 if got, want := resp.File[0].GetName(), test.wantFilename; got != want {
215 t.Errorf("%vgenerated filename=%v, want %v", context, got, want)
Damien Neil082ce922018-09-06 10:23:53 -0700216 }
217 }
218}
219
220func TestPackageNameInference(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800221 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsai8b366e82021-03-16 03:20:52 -0700222 Parameter: proto.String("Mdir/file1.proto=path/to/file1"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800223 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700224 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700225 Name: proto.String("dir/file1.proto"),
226 Package: proto.String("proto.package"),
Damien Neil082ce922018-09-06 10:23:53 -0700227 },
228 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700229 Name: proto.String("dir/file2.proto"),
230 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800231 Options: &descriptorpb.FileOptions{
Joe Tsai8b366e82021-03-16 03:20:52 -0700232 GoPackage: proto.String("path/to/file2"),
Damien Neil082ce922018-09-06 10:23:53 -0700233 },
234 },
235 },
236 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800237 })
Damien Neil082ce922018-09-06 10:23:53 -0700238 if err != nil {
239 t.Fatalf("New(req) = %v", err)
240 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700241 if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
Damien Neil082ce922018-09-06 10:23:53 -0700242 t.Errorf("missing file info for dir/file1.proto")
Joe Tsai8b366e82021-03-16 03:20:52 -0700243 } else if f1.GoPackageName != "file1" {
Damien Neil082ce922018-09-06 10:23:53 -0700244 t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
245 }
246}
247
248func TestInconsistentPackageNames(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800249 _, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800250 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700251 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700252 Name: proto.String("dir/file1.proto"),
253 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800254 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700255 GoPackage: proto.String("golang.org/x/foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700256 },
257 },
258 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700259 Name: proto.String("dir/file2.proto"),
260 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800261 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700262 GoPackage: proto.String("golang.org/x/foo;bar"),
Damien Neil082ce922018-09-06 10:23:53 -0700263 },
264 },
265 },
266 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800267 })
Damien Neil082ce922018-09-06 10:23:53 -0700268 if err == nil {
269 t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
270 }
271}
272
Damien Neild9016772018-08-23 14:39:30 -0700273func TestImports(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800274 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{})
Damien Neild9016772018-08-23 14:39:30 -0700275 if err != nil {
276 t.Fatal(err)
277 }
278 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
279 g.P("package foo")
280 g.P()
281 for _, importPath := range []GoImportPath{
282 "golang.org/x/foo",
283 // Multiple references to the same package.
284 "golang.org/x/bar",
285 "golang.org/x/bar",
286 // Reference to a different package with the same basename.
287 "golang.org/y/bar",
288 "golang.org/x/baz",
Damien Neil87214662018-10-05 11:23:35 -0700289 // Reference to a package conflicting with a predeclared identifier.
290 "golang.org/z/string",
Damien Neild9016772018-08-23 14:39:30 -0700291 } {
292 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
293 }
294 want := `package foo
295
296import (
297 bar "golang.org/x/bar"
Damien Neild9016772018-08-23 14:39:30 -0700298 baz "golang.org/x/baz"
Damien Neil1ec33152018-09-13 13:12:36 -0700299 bar1 "golang.org/y/bar"
Damien Neil87214662018-10-05 11:23:35 -0700300 string1 "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700301)
302
Damien Neil87214662018-10-05 11:23:35 -0700303var _ = X // "golang.org/x/foo"
304var _ = bar.X // "golang.org/x/bar"
305var _ = bar.X // "golang.org/x/bar"
306var _ = bar1.X // "golang.org/y/bar"
307var _ = baz.X // "golang.org/x/baz"
308var _ = string1.X // "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700309`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800310 got, err := g.Content()
Damien Neild9016772018-08-23 14:39:30 -0700311 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800312 t.Fatalf("g.Content() = %v", err)
Damien Neild9016772018-08-23 14:39:30 -0700313 }
Joe Tsai94a85102019-03-12 21:28:30 -0700314 if diff := cmp.Diff(string(want), string(got)); diff != "" {
315 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neild9016772018-08-23 14:39:30 -0700316 }
317}
318
Damien Neil1fa8ab02018-09-27 15:51:05 -0700319func TestImportRewrites(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800320 gen, err := Options{
Damien Neil1fa8ab02018-09-27 15:51:05 -0700321 ImportRewriteFunc: func(i GoImportPath) GoImportPath {
322 return "prefix/" + i
323 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800324 }.New(&pluginpb.CodeGeneratorRequest{})
Damien Neil1fa8ab02018-09-27 15:51:05 -0700325 if err != nil {
326 t.Fatal(err)
327 }
328 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
329 g.P("package foo")
330 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
331 want := `package foo
332
Joe Tsai94a85102019-03-12 21:28:30 -0700333import (
334 bar "prefix/golang.org/x/bar"
335)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700336
337var _ = bar.X
338`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800339 got, err := g.Content()
Damien Neil1fa8ab02018-09-27 15:51:05 -0700340 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800341 t.Fatalf("g.Content() = %v", err)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700342 }
Joe Tsai94a85102019-03-12 21:28:30 -0700343 if diff := cmp.Diff(string(want), string(got)); diff != "" {
344 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neil220c2022018-08-15 11:24:18 -0700345 }
346}