protogen_test.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // 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. package protogen
  5. import (
  6. "flag"
  7. "fmt"
  8. "testing"
  9. "github.com/google/go-cmp/cmp"
  10. "google.golang.org/protobuf/proto"
  11. "google.golang.org/protobuf/reflect/protoreflect"
  12. "google.golang.org/protobuf/types/descriptorpb"
  13. "google.golang.org/protobuf/types/pluginpb"
  14. )
  15. func TestPluginParameters(t *testing.T) {
  16. var flags flag.FlagSet
  17. value := flags.Int("integer", 0, "")
  18. opts := &Options{
  19. ParamFunc: flags.Set,
  20. }
  21. const params = "integer=2"
  22. _, err := New(&pluginpb.CodeGeneratorRequest{
  23. Parameter: proto.String(params),
  24. }, opts)
  25. if err != nil {
  26. t.Errorf("New(generator parameters %q): %v", params, err)
  27. }
  28. if *value != 2 {
  29. t.Errorf("New(generator parameters %q): integer=%v, want 2", params, *value)
  30. }
  31. }
  32. func TestPluginParameterErrors(t *testing.T) {
  33. for _, parameter := range []string{
  34. "unknown=1",
  35. "boolean=error",
  36. } {
  37. var flags flag.FlagSet
  38. flags.Bool("boolean", false, "")
  39. opts := &Options{
  40. ParamFunc: flags.Set,
  41. }
  42. _, err := New(&pluginpb.CodeGeneratorRequest{
  43. Parameter: proto.String(parameter),
  44. }, opts)
  45. if err == nil {
  46. t.Errorf("New(generator parameters %q): want error, got nil", parameter)
  47. }
  48. }
  49. }
  50. func TestNoGoPackage(t *testing.T) {
  51. gen, err := New(&pluginpb.CodeGeneratorRequest{
  52. ProtoFile: []*descriptorpb.FileDescriptorProto{
  53. {
  54. Name: proto.String("testdata/go_package/no_go_package.proto"),
  55. Syntax: proto.String(protoreflect.Proto3.String()),
  56. Package: proto.String("goproto.testdata"),
  57. },
  58. {
  59. Name: proto.String("testdata/go_package/no_go_package_import.proto"),
  60. Syntax: proto.String(protoreflect.Proto3.String()),
  61. Package: proto.String("goproto.testdata"),
  62. Dependency: []string{"go_package/no_go_package.proto"},
  63. },
  64. },
  65. }, nil)
  66. if err != nil {
  67. t.Fatal(err)
  68. }
  69. for i, f := range gen.Files {
  70. if got, want := string(f.GoPackageName), "goproto_testdata"; got != want {
  71. t.Errorf("gen.Files[%d].GoPackageName = %v, want %v", i, got, want)
  72. }
  73. if got, want := string(f.GoImportPath), "testdata/go_package"; got != want {
  74. t.Errorf("gen.Files[%d].GoImportPath = %v, want %v", i, got, want)
  75. }
  76. }
  77. }
  78. func TestPackageNamesAndPaths(t *testing.T) {
  79. const (
  80. filename = "dir/filename.proto"
  81. protoPackageName = "proto.package"
  82. )
  83. for _, test := range []struct {
  84. desc string
  85. parameter string
  86. goPackageOption string
  87. generate bool
  88. wantPackageName GoPackageName
  89. wantImportPath GoImportPath
  90. wantFilenamePrefix string
  91. }{
  92. {
  93. desc: "no parameters, no go_package option",
  94. generate: true,
  95. wantPackageName: "proto_package",
  96. wantImportPath: "dir",
  97. wantFilenamePrefix: "dir/filename",
  98. },
  99. {
  100. desc: "go_package option sets import path",
  101. goPackageOption: "golang.org/x/foo",
  102. generate: true,
  103. wantPackageName: "foo",
  104. wantImportPath: "golang.org/x/foo",
  105. wantFilenamePrefix: "golang.org/x/foo/filename",
  106. },
  107. {
  108. desc: "go_package option sets import path and package",
  109. goPackageOption: "golang.org/x/foo;bar",
  110. generate: true,
  111. wantPackageName: "bar",
  112. wantImportPath: "golang.org/x/foo",
  113. wantFilenamePrefix: "golang.org/x/foo/filename",
  114. },
  115. {
  116. desc: "go_package option sets package",
  117. goPackageOption: "foo",
  118. generate: true,
  119. wantPackageName: "foo",
  120. wantImportPath: "dir",
  121. wantFilenamePrefix: "dir/filename",
  122. },
  123. {
  124. desc: "command line sets import path for a file",
  125. parameter: "Mdir/filename.proto=golang.org/x/bar",
  126. goPackageOption: "golang.org/x/foo",
  127. generate: true,
  128. wantPackageName: "foo",
  129. wantImportPath: "golang.org/x/bar",
  130. wantFilenamePrefix: "golang.org/x/foo/filename",
  131. },
  132. {
  133. desc: "import_path parameter sets import path of generated files",
  134. parameter: "import_path=golang.org/x/bar",
  135. goPackageOption: "golang.org/x/foo",
  136. generate: true,
  137. wantPackageName: "foo",
  138. wantImportPath: "golang.org/x/bar",
  139. wantFilenamePrefix: "golang.org/x/foo/filename",
  140. },
  141. {
  142. desc: "import_path parameter does not set import path of dependencies",
  143. parameter: "import_path=golang.org/x/bar",
  144. goPackageOption: "golang.org/x/foo",
  145. generate: false,
  146. wantPackageName: "foo",
  147. wantImportPath: "golang.org/x/foo",
  148. wantFilenamePrefix: "golang.org/x/foo/filename",
  149. },
  150. } {
  151. context := fmt.Sprintf(`
  152. TEST: %v
  153. --go_out=%v:.
  154. file %q: generate=%v
  155. option go_package = %q;
  156. `,
  157. test.desc, test.parameter, filename, test.generate, test.goPackageOption)
  158. req := &pluginpb.CodeGeneratorRequest{
  159. Parameter: proto.String(test.parameter),
  160. ProtoFile: []*descriptorpb.FileDescriptorProto{
  161. {
  162. Name: proto.String(filename),
  163. Package: proto.String(protoPackageName),
  164. Options: &descriptorpb.FileOptions{
  165. GoPackage: proto.String(test.goPackageOption),
  166. },
  167. },
  168. },
  169. }
  170. if test.generate {
  171. req.FileToGenerate = []string{filename}
  172. }
  173. gen, err := New(req, nil)
  174. if err != nil {
  175. t.Errorf("%vNew(req) = %v", context, err)
  176. continue
  177. }
  178. gotFile, ok := gen.FilesByPath[filename]
  179. if !ok {
  180. t.Errorf("%v%v: missing file info", context, filename)
  181. continue
  182. }
  183. if got, want := gotFile.GoPackageName, test.wantPackageName; got != want {
  184. t.Errorf("%vGoPackageName=%v, want %v", context, got, want)
  185. }
  186. if got, want := gotFile.GoImportPath, test.wantImportPath; got != want {
  187. t.Errorf("%vGoImportPath=%v, want %v", context, got, want)
  188. }
  189. if got, want := gotFile.GeneratedFilenamePrefix, test.wantFilenamePrefix; got != want {
  190. t.Errorf("%vGeneratedFilenamePrefix=%v, want %v", context, got, want)
  191. }
  192. }
  193. }
  194. func TestPackageNameInference(t *testing.T) {
  195. gen, err := New(&pluginpb.CodeGeneratorRequest{
  196. ProtoFile: []*descriptorpb.FileDescriptorProto{
  197. {
  198. Name: proto.String("dir/file1.proto"),
  199. Package: proto.String("proto.package"),
  200. },
  201. {
  202. Name: proto.String("dir/file2.proto"),
  203. Package: proto.String("proto.package"),
  204. Options: &descriptorpb.FileOptions{
  205. GoPackage: proto.String("foo"),
  206. },
  207. },
  208. },
  209. FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
  210. }, nil)
  211. if err != nil {
  212. t.Fatalf("New(req) = %v", err)
  213. }
  214. if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
  215. t.Errorf("missing file info for dir/file1.proto")
  216. } else if f1.GoPackageName != "foo" {
  217. t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
  218. }
  219. }
  220. func TestInconsistentPackageNames(t *testing.T) {
  221. _, err := New(&pluginpb.CodeGeneratorRequest{
  222. ProtoFile: []*descriptorpb.FileDescriptorProto{
  223. {
  224. Name: proto.String("dir/file1.proto"),
  225. Package: proto.String("proto.package"),
  226. Options: &descriptorpb.FileOptions{
  227. GoPackage: proto.String("golang.org/x/foo"),
  228. },
  229. },
  230. {
  231. Name: proto.String("dir/file2.proto"),
  232. Package: proto.String("proto.package"),
  233. Options: &descriptorpb.FileOptions{
  234. GoPackage: proto.String("golang.org/x/foo;bar"),
  235. },
  236. },
  237. },
  238. FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
  239. }, nil)
  240. if err == nil {
  241. t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
  242. }
  243. }
  244. func TestImports(t *testing.T) {
  245. gen, err := New(&pluginpb.CodeGeneratorRequest{}, nil)
  246. if err != nil {
  247. t.Fatal(err)
  248. }
  249. g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
  250. g.P("package foo")
  251. g.P()
  252. for _, importPath := range []GoImportPath{
  253. "golang.org/x/foo",
  254. // Multiple references to the same package.
  255. "golang.org/x/bar",
  256. "golang.org/x/bar",
  257. // Reference to a different package with the same basename.
  258. "golang.org/y/bar",
  259. "golang.org/x/baz",
  260. // Reference to a package conflicting with a predeclared identifier.
  261. "golang.org/z/string",
  262. } {
  263. g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
  264. }
  265. want := `package foo
  266. import (
  267. bar "golang.org/x/bar"
  268. baz "golang.org/x/baz"
  269. bar1 "golang.org/y/bar"
  270. string1 "golang.org/z/string"
  271. )
  272. var _ = X // "golang.org/x/foo"
  273. var _ = bar.X // "golang.org/x/bar"
  274. var _ = bar.X // "golang.org/x/bar"
  275. var _ = bar1.X // "golang.org/y/bar"
  276. var _ = baz.X // "golang.org/x/baz"
  277. var _ = string1.X // "golang.org/z/string"
  278. `
  279. got, err := g.Content()
  280. if err != nil {
  281. t.Fatalf("g.Content() = %v", err)
  282. }
  283. if diff := cmp.Diff(string(want), string(got)); diff != "" {
  284. t.Fatalf("content mismatch (-want +got):\n%s", diff)
  285. }
  286. }
  287. func TestImportRewrites(t *testing.T) {
  288. gen, err := New(&pluginpb.CodeGeneratorRequest{}, &Options{
  289. ImportRewriteFunc: func(i GoImportPath) GoImportPath {
  290. return "prefix/" + i
  291. },
  292. })
  293. if err != nil {
  294. t.Fatal(err)
  295. }
  296. g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo")
  297. g.P("package foo")
  298. g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: "golang.org/x/bar"})
  299. want := `package foo
  300. import (
  301. bar "prefix/golang.org/x/bar"
  302. )
  303. var _ = bar.X
  304. `
  305. got, err := g.Content()
  306. if err != nil {
  307. t.Fatalf("g.Content() = %v", err)
  308. }
  309. if diff := cmp.Diff(string(want), string(got)); diff != "" {
  310. t.Fatalf("content mismatch (-want +got):\n%s", diff)
  311. }
  312. }