blob: 419305f5aab33720802153bee5d9e106279f313b [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 Tsaiab0ca4f2020-02-27 14:47:29 -080057 gen, 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 },
64 {
Damien Neila8a2cea2019-07-10 16:17:16 -070065 Name: proto.String("testdata/go_package/no_go_package_import.proto"),
66 Syntax: proto.String(protoreflect.Proto3.String()),
67 Package: proto.String("goproto.testdata"),
Joe Tsai25fc6fb2020-02-10 14:04:25 -080068 Dependency: []string{"testdata/go_package/no_go_package.proto"},
Joe Tsai94a85102019-03-12 21:28:30 -070069 },
70 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -080071 })
Damien Neil220c2022018-08-15 11:24:18 -070072 if err != nil {
73 t.Fatal(err)
74 }
Joe Tsai94a85102019-03-12 21:28:30 -070075
76 for i, f := range gen.Files {
77 if got, want := string(f.GoPackageName), "goproto_testdata"; got != want {
78 t.Errorf("gen.Files[%d].GoPackageName = %v, want %v", i, got, want)
Damien Neil220c2022018-08-15 11:24:18 -070079 }
Joe Tsai94a85102019-03-12 21:28:30 -070080 if got, want := string(f.GoImportPath), "testdata/go_package"; got != want {
81 t.Errorf("gen.Files[%d].GoImportPath = %v, want %v", i, got, want)
Damien Neil220c2022018-08-15 11:24:18 -070082 }
83 }
84}
85
Damien Neil082ce922018-09-06 10:23:53 -070086func TestPackageNamesAndPaths(t *testing.T) {
87 const (
88 filename = "dir/filename.proto"
89 protoPackageName = "proto.package"
90 )
91 for _, test := range []struct {
92 desc string
93 parameter string
94 goPackageOption string
95 generate bool
96 wantPackageName GoPackageName
97 wantImportPath GoImportPath
98 wantFilenamePrefix string
99 }{
100 {
101 desc: "no parameters, no go_package option",
102 generate: true,
103 wantPackageName: "proto_package",
104 wantImportPath: "dir",
105 wantFilenamePrefix: "dir/filename",
106 },
107 {
108 desc: "go_package option sets import path",
109 goPackageOption: "golang.org/x/foo",
110 generate: true,
111 wantPackageName: "foo",
112 wantImportPath: "golang.org/x/foo",
113 wantFilenamePrefix: "golang.org/x/foo/filename",
114 },
115 {
116 desc: "go_package option sets import path and package",
117 goPackageOption: "golang.org/x/foo;bar",
118 generate: true,
119 wantPackageName: "bar",
120 wantImportPath: "golang.org/x/foo",
121 wantFilenamePrefix: "golang.org/x/foo/filename",
122 },
123 {
124 desc: "go_package option sets package",
125 goPackageOption: "foo",
126 generate: true,
127 wantPackageName: "foo",
128 wantImportPath: "dir",
129 wantFilenamePrefix: "dir/filename",
130 },
131 {
132 desc: "command line sets import path for a file",
133 parameter: "Mdir/filename.proto=golang.org/x/bar",
134 goPackageOption: "golang.org/x/foo",
135 generate: true,
136 wantPackageName: "foo",
137 wantImportPath: "golang.org/x/bar",
138 wantFilenamePrefix: "golang.org/x/foo/filename",
139 },
140 {
141 desc: "import_path parameter sets import path of generated files",
142 parameter: "import_path=golang.org/x/bar",
143 goPackageOption: "golang.org/x/foo",
144 generate: true,
145 wantPackageName: "foo",
146 wantImportPath: "golang.org/x/bar",
147 wantFilenamePrefix: "golang.org/x/foo/filename",
148 },
149 {
150 desc: "import_path parameter does not set import path of dependencies",
151 parameter: "import_path=golang.org/x/bar",
152 goPackageOption: "golang.org/x/foo",
153 generate: false,
154 wantPackageName: "foo",
155 wantImportPath: "golang.org/x/foo",
156 wantFilenamePrefix: "golang.org/x/foo/filename",
157 },
158 } {
159 context := fmt.Sprintf(`
160TEST: %v
161 --go_out=%v:.
162 file %q: generate=%v
163 option go_package = %q;
164
165 `,
166 test.desc, test.parameter, filename, test.generate, test.goPackageOption)
167
168 req := &pluginpb.CodeGeneratorRequest{
Damien Neila8a2cea2019-07-10 16:17:16 -0700169 Parameter: proto.String(test.parameter),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800170 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700171 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700172 Name: proto.String(filename),
173 Package: proto.String(protoPackageName),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800174 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700175 GoPackage: proto.String(test.goPackageOption),
Damien Neil082ce922018-09-06 10:23:53 -0700176 },
177 },
178 },
179 }
180 if test.generate {
181 req.FileToGenerate = []string{filename}
182 }
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800183 gen, err := Options{}.New(req)
Damien Neil082ce922018-09-06 10:23:53 -0700184 if err != nil {
185 t.Errorf("%vNew(req) = %v", context, err)
186 continue
187 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700188 gotFile, ok := gen.FilesByPath[filename]
Damien Neil082ce922018-09-06 10:23:53 -0700189 if !ok {
190 t.Errorf("%v%v: missing file info", context, filename)
191 continue
192 }
193 if got, want := gotFile.GoPackageName, test.wantPackageName; got != want {
194 t.Errorf("%vGoPackageName=%v, want %v", context, got, want)
195 }
196 if got, want := gotFile.GoImportPath, test.wantImportPath; got != want {
197 t.Errorf("%vGoImportPath=%v, want %v", context, got, want)
198 }
199 if got, want := gotFile.GeneratedFilenamePrefix, test.wantFilenamePrefix; got != want {
200 t.Errorf("%vGeneratedFilenamePrefix=%v, want %v", context, got, want)
201 }
202 }
203}
204
205func TestPackageNameInference(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800206 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800207 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700208 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700209 Name: proto.String("dir/file1.proto"),
210 Package: proto.String("proto.package"),
Damien Neil082ce922018-09-06 10:23:53 -0700211 },
212 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700213 Name: proto.String("dir/file2.proto"),
214 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800215 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700216 GoPackage: proto.String("foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700217 },
218 },
219 },
220 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800221 })
Damien Neil082ce922018-09-06 10:23:53 -0700222 if err != nil {
223 t.Fatalf("New(req) = %v", err)
224 }
Joe Tsai2cec4842019-08-20 20:14:19 -0700225 if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
Damien Neil082ce922018-09-06 10:23:53 -0700226 t.Errorf("missing file info for dir/file1.proto")
227 } else if f1.GoPackageName != "foo" {
228 t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
229 }
230}
231
232func TestInconsistentPackageNames(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800233 _, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
Joe Tsaie1f8d502018-11-26 18:55:29 -0800234 ProtoFile: []*descriptorpb.FileDescriptorProto{
Damien Neil082ce922018-09-06 10:23:53 -0700235 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700236 Name: proto.String("dir/file1.proto"),
237 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800238 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700239 GoPackage: proto.String("golang.org/x/foo"),
Damien Neil082ce922018-09-06 10:23:53 -0700240 },
241 },
242 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700243 Name: proto.String("dir/file2.proto"),
244 Package: proto.String("proto.package"),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800245 Options: &descriptorpb.FileOptions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700246 GoPackage: proto.String("golang.org/x/foo;bar"),
Damien Neil082ce922018-09-06 10:23:53 -0700247 },
248 },
249 },
250 FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800251 })
Damien Neil082ce922018-09-06 10:23:53 -0700252 if err == nil {
253 t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
254 }
255}
256
Damien Neild9016772018-08-23 14:39:30 -0700257func TestImports(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800258 gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{})
Damien Neild9016772018-08-23 14:39:30 -0700259 if err != nil {
260 t.Fatal(err)
261 }
262 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
263 g.P("package foo")
264 g.P()
265 for _, importPath := range []GoImportPath{
266 "golang.org/x/foo",
267 // Multiple references to the same package.
268 "golang.org/x/bar",
269 "golang.org/x/bar",
270 // Reference to a different package with the same basename.
271 "golang.org/y/bar",
272 "golang.org/x/baz",
Damien Neil87214662018-10-05 11:23:35 -0700273 // Reference to a package conflicting with a predeclared identifier.
274 "golang.org/z/string",
Damien Neild9016772018-08-23 14:39:30 -0700275 } {
276 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
277 }
278 want := `package foo
279
280import (
281 bar "golang.org/x/bar"
Damien Neild9016772018-08-23 14:39:30 -0700282 baz "golang.org/x/baz"
Damien Neil1ec33152018-09-13 13:12:36 -0700283 bar1 "golang.org/y/bar"
Damien Neil87214662018-10-05 11:23:35 -0700284 string1 "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700285)
286
Damien Neil87214662018-10-05 11:23:35 -0700287var _ = X // "golang.org/x/foo"
288var _ = bar.X // "golang.org/x/bar"
289var _ = bar.X // "golang.org/x/bar"
290var _ = bar1.X // "golang.org/y/bar"
291var _ = baz.X // "golang.org/x/baz"
292var _ = string1.X // "golang.org/z/string"
Damien Neild9016772018-08-23 14:39:30 -0700293`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800294 got, err := g.Content()
Damien Neild9016772018-08-23 14:39:30 -0700295 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800296 t.Fatalf("g.Content() = %v", err)
Damien Neild9016772018-08-23 14:39:30 -0700297 }
Joe Tsai94a85102019-03-12 21:28:30 -0700298 if diff := cmp.Diff(string(want), string(got)); diff != "" {
299 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neild9016772018-08-23 14:39:30 -0700300 }
301}
302
Damien Neil1fa8ab02018-09-27 15:51:05 -0700303func TestImportRewrites(t *testing.T) {
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800304 gen, err := Options{
Damien Neil1fa8ab02018-09-27 15:51:05 -0700305 ImportRewriteFunc: func(i GoImportPath) GoImportPath {
306 return "prefix/" + i
307 },
Joe Tsaiab0ca4f2020-02-27 14:47:29 -0800308 }.New(&pluginpb.CodeGeneratorRequest{})
Damien Neil1fa8ab02018-09-27 15:51:05 -0700309 if err != nil {
310 t.Fatal(err)
311 }
312 g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
313 g.P("package foo")
314 g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
315 want := `package foo
316
Joe Tsai94a85102019-03-12 21:28:30 -0700317import (
318 bar "prefix/golang.org/x/bar"
319)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700320
321var _ = bar.X
322`
Damien Neil7bf3ce22018-12-21 15:54:06 -0800323 got, err := g.Content()
Damien Neil1fa8ab02018-09-27 15:51:05 -0700324 if err != nil {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800325 t.Fatalf("g.Content() = %v", err)
Damien Neil1fa8ab02018-09-27 15:51:05 -0700326 }
Joe Tsai94a85102019-03-12 21:28:30 -0700327 if diff := cmp.Diff(string(want), string(got)); diff != "" {
328 t.Fatalf("content mismatch (-want +got):\n%s", diff)
Damien Neil220c2022018-08-15 11:24:18 -0700329 }
330}