blob: 6f08b2e3de7be1e86b409d74223151487d863277 [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, "")
24 opts := &Options{
25 ParamFunc: flags.Set,
26 }
27 const params = "integer=2"
28 _, err := New(&pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -070029 Parameter: proto.String(params),
Damien Neil3cf6e622018-09-11 13:53:14 -070030 }, opts)
31 if err != nil {
32 t.Errorf("New(generator parameters %q): %v", params, err)
33 }
34 if *value != 2 {
35 t.Errorf("New(generator parameters %q): integer=%v, want 2", params, *value)
36 }
37}
38
39func TestPluginParameterErrors(t *testing.T) {
40 for _, parameter := range []string{
41 "unknown=1",
42 "boolean=error",
43 } {
44 var flags flag.FlagSet
45 flags.Bool("boolean", false, "")
46 opts := &Options{
47 ParamFunc: flags.Set,
48 }
49 _, err := New(&pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -070050 Parameter: proto.String(parameter),
Damien Neil3cf6e622018-09-11 13:53:14 -070051 }, opts)
52 if err == nil {
53 t.Errorf("New(generator parameters %q): want error, got nil", parameter)
54 }
55 }
56}
57
Joe Tsai94a85102019-03-12 21:28:30 -070058func TestNoGoPackage(t *testing.T) {
59 gen, err := New(&pluginpb.CodeGeneratorRequest{
60 ProtoFile: []*descriptorpb.FileDescriptorProto{
61 {
Damien Neila8a2cea2019-07-10 16:17:16 -070062 Name: proto.String("testdata/go_package/no_go_package.proto"),
63 Syntax: proto.String(protoreflect.Proto3.String()),
64 Package: proto.String("goproto.testdata"),
Joe Tsai94a85102019-03-12 21:28:30 -070065 },
66 {
Damien Neila8a2cea2019-07-10 16:17:16 -070067 Name: proto.String("testdata/go_package/no_go_package_import.proto"),
68 Syntax: proto.String(protoreflect.Proto3.String()),
69 Package: proto.String("goproto.testdata"),
Joe Tsai25fc6fb2020-02-10 14:04:25 -080070 Dependency: []string{"testdata/go_package/no_go_package.proto"},
Joe Tsai94a85102019-03-12 21:28:30 -070071 },
72 },
73 }, nil)
Damien Neil220c2022018-08-15 11:24:18 -070074 if err != nil {
75 t.Fatal(err)
76 }
Joe Tsai94a85102019-03-12 21:28:30 -070077
78 for i, f := range gen.Files {
79 if got, want := string(f.GoPackageName), "goproto_testdata"; got != want {
80 t.Errorf("gen.Files[%d].GoPackageName = %v, want %v", i, got, want)
Damien Neil220c2022018-08-15 11:24:18 -070081 }
Joe Tsai94a85102019-03-12 21:28:30 -070082 if got, want := string(f.GoImportPath), "testdata/go_package"; got != want {
83 t.Errorf("gen.Files[%d].GoImportPath = %v, want %v", i, got, want)
Damien Neil220c2022018-08-15 11:24:18 -070084 }
85 }
86}
87
Damien Neil082ce922018-09-06 10:23:53 -070088func TestPackageNamesAndPaths(t *testing.T) {
89 const (
90 filename = "dir/filename.proto"
91 protoPackageName = "proto.package"
92 )
93 for _, test := range []struct {
94 desc string
95 parameter string
96 goPackageOption string
97 generate bool
98 wantPackageName GoPackageName
99 wantImportPath GoImportPath
100 wantFilenamePrefix string
101 }{
102 {
103 desc: "no parameters, no go_package option",
104 generate: true,
105 wantPackageName: "proto_package",
106 wantImportPath: "dir",
107 wantFilenamePrefix: "dir/filename",
108 },
109 {
110 desc: "go_package option sets import path",
111 goPackageOption: "golang.org/x/foo",
112 generate: true,
113 wantPackageName: "foo",
114 wantImportPath: "golang.org/x/foo",
115 wantFilenamePrefix: "golang.org/x/foo/filename",
116 },
117 {
118 desc: "go_package option sets import path and package",
119 goPackageOption: "golang.org/x/foo;bar",
120 generate: true,
121 wantPackageName: "bar",
122 wantImportPath: "golang.org/x/foo",
123 wantFilenamePrefix: "golang.org/x/foo/filename",
124 },
125 {
126 desc: "go_package option sets package",
127 goPackageOption: "foo",
128 generate: true,
129 wantPackageName: "foo",
130 wantImportPath: "dir",
131 wantFilenamePrefix: "dir/filename",
132 },
133 {
134 desc: "command line sets import path for a file",
135 parameter: "Mdir/filename.proto=golang.org/x/bar",
136 goPackageOption: "golang.org/x/foo",
137 generate: true,
138 wantPackageName: "foo",
139 wantImportPath: "golang.org/x/bar",
140 wantFilenamePrefix: "golang.org/x/foo/filename",
141 },
142 {
143 desc: "import_path parameter sets import path of generated files",
144 parameter: "import_path=golang.org/x/bar",
145 goPackageOption: "golang.org/x/foo",
146 generate: true,
147 wantPackageName: "foo",
148 wantImportPath: "golang.org/x/bar",
149 wantFilenamePrefix: "golang.org/x/foo/filename",
150 },
151 {
152 desc: "import_path parameter does not set import path of dependencies",
153 parameter: "import_path=golang.org/x/bar",
154 goPackageOption: "golang.org/x/foo",
155 generate: false,
156 wantPackageName: "foo",
157 wantImportPath: "golang.org/x/foo",
158 wantFilenamePrefix: "golang.org/x/foo/filename",
159 },
160 } {
161 context := fmt.Sprintf(`
162TEST: %v
163 --go_out=%v:.
164 file %q: generate=%v
165 option go_package = %q;
166
167 `,
168 test.desc, test.parameter, filename, test.generate, test.goPackageOption)
169
170 req := &pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -0700171 Parameter: proto.String(test.parameter),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800172 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700173 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700174 Name: proto.String(filename),
175 Package: proto.String(protoPackageName),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800176 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700177 GoPackage: proto.String(test.goPackageOption),
Damien Neil082ce922018-09-06 10:23:53 -0700178 },
179 },
180 },
181 }
182 if test.generate {
183 req.FileToGenerate = []string{filename}
184 }
Damien Neil3cf6e622018-09-11 13:53:14 -0700185 gen, err := New(req, nil)
Damien Neil082ce922018-09-06 10:23:53 -0700186 if err != nil {
187 t.Errorf("%vNew(req) = %v", context, err)
188 continue
189 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700190 gotFile, ok := gen.FilesByPath[filename]
Damien Neil082ce922018-09-06 10:23:53 -0700191 if !ok {
192 t.Errorf("%v%v: missing file info", context, filename)
193 continue
194 }
195 if got, want := gotFile.GoPackageName, test.wantPackageName; got != want {
196 t.Errorf("%vGoPackageName=%v, want %v", context, got, want)
197 }
198 if got, want := gotFile.GoImportPath, test.wantImportPath; got != want {
199 t.Errorf("%vGoImportPath=%v, want %v", context, got, want)
200 }
201 if got, want := gotFile.GeneratedFilenamePrefix, test.wantFilenamePrefix; got != want {
202 t.Errorf("%vGeneratedFilenamePrefix=%v, want %v", context, got, want)
203 }
204 }
205}
206
207func TestPackageNameInference(t *testing.T) {
208 gen, err := New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800209 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700210 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700211 Name: proto.String("dir/file1.proto"),
212 Package: proto.String("proto.package"),
Damien Neil082ce922018-09-06 10:23:53 -0700213 },
214 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700215 Name: proto.String("dir/file2.proto"),
216 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800217 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700218 GoPackage: proto.String("foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700219 },
220 },
221 },
222 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Damien Neil3cf6e622018-09-11 13:53:14 -0700223 }, nil)
Damien Neil082ce922018-09-06 10:23:53 -0700224 if err != nil {
225 t.Fatalf("New(req) = %v", err)
226 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700227 if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
Damien Neil082ce922018-09-06 10:23:53 -0700228 t.Errorf("missing file info for dir/file1.proto")
229 } else if f1.GoPackageName != "foo" {
230 t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
231 }
232}
233
234func TestInconsistentPackageNames(t *testing.T) {
235 _, err := New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800236 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700237 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700238 Name: proto.String("dir/file1.proto"),
239 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800240 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700241 GoPackage: proto.String("golang.org/x/foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700242 },
243 },
244 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700245 Name: proto.String("dir/file2.proto"),
246 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800247 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700248 GoPackage: proto.String("golang.org/x/foo;bar"),
Damien Neil082ce922018-09-06 10:23:53 -0700249 },
250 },
251 },
252 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Damien Neil3cf6e622018-09-11 13:53:14 -0700253 }, nil)
Damien Neil082ce922018-09-06 10:23:53 -0700254 if err == nil {
255 t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
256 }
257}
258
Damien Neild9016772018-08-23 14:39:30 -0700259func TestImports(t *testing.T) {
Damien Neil3cf6e622018-09-11 13:53:14 -0700260 gen, err := New(&pluginpb.CodeGeneratorRequest{}, nil)
Damien Neild9016772018-08-23 14:39:30 -0700261 if err != nil {
262 t.Fatal(err)
263 }
264 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
265 g.P("package foo")
266 g.P()
267 for _, importPath := range []GoImportPath{
268 "golang.org/x/foo",
269 // Multiple references to the same package.
270 "golang.org/x/bar",
271 "golang.org/x/bar",
272 // Reference to a different package with the same basename.
273 "golang.org/y/bar",
274 "golang.org/x/baz",
Damien Neil87214662018-10-05 11:23:35 -0700275 // Reference to a package conflicting with a predeclared identifier.
276 "golang.org/z/string",
Damien Neild9016772018-08-23 14:39:30 -0700277 } {
278 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
279 }
280 want := `package foo
281
282import (
283 bar "golang.org/x/bar"
Damien Neild9016772018-08-23 14:39:30 -0700284 baz "golang.org/x/baz"
Damien Neil1ec33152018-09-13 13:12:36 -0700285 bar1 "golang.org/y/bar"
Damien Neil87214662018-10-05 11:23:35 -0700286 string1 "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700287)
288
Damien Neil87214662018-10-05 11:23:35 -0700289var _ = X // "golang.org/x/foo"
290var _ = bar.X // "golang.org/x/bar"
291var _ = bar.X // "golang.org/x/bar"
292var _ = bar1.X // "golang.org/y/bar"
293var _ = baz.X // "golang.org/x/baz"
294var _ = string1.X // "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700295`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800296 got, err := g.Content()
Damien Neild9016772018-08-23 14:39:30 -0700297 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800298 t.Fatalf("g.Content() = %v", err)
Damien Neild9016772018-08-23 14:39:30 -0700299 }
Joe Tsai94a85102019-03-12 21:28:30 -0700300 if diff := cmp.Diff(string(want), string(got)); diff != "" {
301 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neild9016772018-08-23 14:39:30 -0700302 }
303}
304
Damien Neil1fa8ab02018-09-27 15:51:05 -0700305func TestImportRewrites(t *testing.T) {
306 gen, err := New(&pluginpb.CodeGeneratorRequest{}, &Options{
307 ImportRewriteFunc: func(i GoImportPath) GoImportPath {
308 return "prefix/" + i
309 },
310 })
311 if err != nil {
312 t.Fatal(err)
313 }
314 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
315 g.P("package foo")
316 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
317 want := `package foo
318
Joe Tsai94a85102019-03-12 21:28:30 -0700319import (
320 bar "prefix/golang.org/x/bar"
321)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700322
323var _ = bar.X
324`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800325 got, err := g.Content()
Damien Neil1fa8ab02018-09-27 15:51:05 -0700326 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800327 t.Fatalf("g.Content() = %v", err)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700328 }
Joe Tsai94a85102019-03-12 21:28:30 -0700329 if diff := cmp.Diff(string(want), string(got)); diff != "" {
330 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neil220c2022018-08-15 11:24:18 -0700331 }
332}