blob: 1f7b75cec6a270493624c2a586947c564d45056b [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 },
Damien Neilaadba562020-02-15 14:28:51 -0800162 {
163 desc: "paths=import uses import path from command line",
164 parameter: "paths=import,Mdir/filename.proto=golang.org/x/bar",
165 goPackageOption: "golang.org/x/foo",
166 generate: true,
167 wantPackageName: "foo",
168 wantImportPath: "golang.org/x/bar",
169 wantFilenamePrefix: "golang.org/x/bar/filename",
170 },
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 }
212 if got, want := gotFile.GeneratedFilenamePrefix, test.wantFilenamePrefix; got != want {
213 t.Errorf("%vGeneratedFilenamePrefix=%v, want %v", context, got, want)
214 }
215 }
216}
217
218func TestPackageNameInference(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800219 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800220 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700221 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700222 Name: proto.String("dir/file1.proto"),
223 Package: proto.String("proto.package"),
Damien Neil082ce922018-09-06 10:23:53 -0700224 },
225 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700226 Name: proto.String("dir/file2.proto"),
227 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800228 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700229 GoPackage: proto.String("foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700230 },
231 },
232 },
233 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800234 })
Damien Neil082ce922018-09-06 10:23:53 -0700235 if err != nil {
236 t.Fatalf("New(req) = %v", err)
237 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700238 if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
Damien Neil082ce922018-09-06 10:23:53 -0700239 t.Errorf("missing file info for dir/file1.proto")
240 } else if f1.GoPackageName != "foo" {
241 t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
242 }
243}
244
245func TestInconsistentPackageNames(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800246 _, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800247 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700248 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700249 Name: proto.String("dir/file1.proto"),
250 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800251 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700252 GoPackage: proto.String("golang.org/x/foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700253 },
254 },
255 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700256 Name: proto.String("dir/file2.proto"),
257 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800258 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700259 GoPackage: proto.String("golang.org/x/foo;bar"),
Damien Neil082ce922018-09-06 10:23:53 -0700260 },
261 },
262 },
263 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800264 })
Damien Neil082ce922018-09-06 10:23:53 -0700265 if err == nil {
266 t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
267 }
268}
269
Damien Neild9016772018-08-23 14:39:30 -0700270func TestImports(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800271 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{})
Damien Neild9016772018-08-23 14:39:30 -0700272 if err != nil {
273 t.Fatal(err)
274 }
275 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
276 g.P("package foo")
277 g.P()
278 for _, importPath := range []GoImportPath{
279 "golang.org/x/foo",
280 // Multiple references to the same package.
281 "golang.org/x/bar",
282 "golang.org/x/bar",
283 // Reference to a different package with the same basename.
284 "golang.org/y/bar",
285 "golang.org/x/baz",
Damien Neil87214662018-10-05 11:23:35 -0700286 // Reference to a package conflicting with a predeclared identifier.
287 "golang.org/z/string",
Damien Neild9016772018-08-23 14:39:30 -0700288 } {
289 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
290 }
291 want := `package foo
292
293import (
294 bar "golang.org/x/bar"
Damien Neild9016772018-08-23 14:39:30 -0700295 baz "golang.org/x/baz"
Damien Neil1ec33152018-09-13 13:12:36 -0700296 bar1 "golang.org/y/bar"
Damien Neil87214662018-10-05 11:23:35 -0700297 string1 "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700298)
299
Damien Neil87214662018-10-05 11:23:35 -0700300var _ = X // "golang.org/x/foo"
301var _ = bar.X // "golang.org/x/bar"
302var _ = bar.X // "golang.org/x/bar"
303var _ = bar1.X // "golang.org/y/bar"
304var _ = baz.X // "golang.org/x/baz"
305var _ = string1.X // "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700306`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800307 got, err := g.Content()
Damien Neild9016772018-08-23 14:39:30 -0700308 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800309 t.Fatalf("g.Content() = %v", err)
Damien Neild9016772018-08-23 14:39:30 -0700310 }
Joe Tsai94a85102019-03-12 21:28:30 -0700311 if diff := cmp.Diff(string(want), string(got)); diff != "" {
312 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neild9016772018-08-23 14:39:30 -0700313 }
314}
315
Damien Neil1fa8ab02018-09-27 15:51:05 -0700316func TestImportRewrites(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800317 gen, err := Options{
Damien Neil1fa8ab02018-09-27 15:51:05 -0700318 ImportRewriteFunc: func(i GoImportPath) GoImportPath {
319 return "prefix/" + i
320 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800321 }.New(&pluginpb.CodeGeneratorRequest{})
Damien Neil1fa8ab02018-09-27 15:51:05 -0700322 if err != nil {
323 t.Fatal(err)
324 }
325 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
326 g.P("package foo")
327 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
328 want := `package foo
329
Joe Tsai94a85102019-03-12 21:28:30 -0700330import (
331 bar "prefix/golang.org/x/bar"
332)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700333
334var _ = bar.X
335`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800336 got, err := g.Content()
Damien Neil1fa8ab02018-09-27 15:51:05 -0700337 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800338 t.Fatalf("g.Content() = %v", err)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700339 }
Joe Tsai94a85102019-03-12 21:28:30 -0700340 if diff := cmp.Diff(string(want), string(got)); diff != "" {
341 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neil220c2022018-08-15 11:24:18 -0700342 }
343}