github.com/whiteCcinn/protobuf-go@v1.0.9/compiler/protogen/protogen_test.go (about)

     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  
     5  package protogen
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  
    14  	"github.com/whiteCcinn/protobuf-go/proto"
    15  	"github.com/whiteCcinn/protobuf-go/reflect/protoreflect"
    16  
    17  	"github.com/whiteCcinn/protobuf-go/types/descriptorpb"
    18  	"github.com/whiteCcinn/protobuf-go/types/pluginpb"
    19  )
    20  
    21  func TestPluginParameters(t *testing.T) {
    22  	var flags flag.FlagSet
    23  	value := flags.Int("integer", 0, "")
    24  	const params = "integer=2"
    25  	_, err := Options{
    26  		ParamFunc: flags.Set,
    27  	}.New(&pluginpb.CodeGeneratorRequest{
    28  		Parameter: proto.String(params),
    29  	})
    30  	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  
    38  func 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, "")
    45  		_, err := Options{
    46  			ParamFunc: flags.Set,
    47  		}.New(&pluginpb.CodeGeneratorRequest{
    48  			Parameter: proto.String(parameter),
    49  		})
    50  		if err == nil {
    51  			t.Errorf("New(generator parameters %q): want error, got nil", parameter)
    52  		}
    53  	}
    54  }
    55  
    56  func TestNoGoPackage(t *testing.T) {
    57  	_, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
    58  		ProtoFile: []*descriptorpb.FileDescriptorProto{
    59  			{
    60  				Name:    proto.String("testdata/go_package/no_go_package.proto"),
    61  				Syntax:  proto.String(protoreflect.Proto3.String()),
    62  				Package: proto.String("goproto.testdata"),
    63  			},
    64  		},
    65  	})
    66  	if err == nil {
    67  		t.Fatalf("missing go_package option: New(req) = nil, want error")
    68  	}
    69  }
    70  
    71  func TestInvalidImportPath(t *testing.T) {
    72  	_, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
    73  		ProtoFile: []*descriptorpb.FileDescriptorProto{
    74  			{
    75  				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  				},
    81  			},
    82  		},
    83  	})
    84  	if err == nil {
    85  		t.Fatalf("missing go_package option: New(req) = nil, want error")
    86  	}
    87  }
    88  
    89  func TestPackageNamesAndPaths(t *testing.T) {
    90  	const (
    91  		filename         = "dir/filename.proto"
    92  		protoPackageName = "proto.package"
    93  	)
    94  	for _, test := range []struct {
    95  		desc            string
    96  		parameter       string
    97  		goPackageOption string
    98  		generate        bool
    99  		wantPackageName GoPackageName
   100  		wantImportPath  GoImportPath
   101  		wantFilename    string
   102  	}{
   103  		{
   104  			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",
   110  		},
   111  		{
   112  			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  		{
   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  			wantFilename:    "golang.org/x/foo/filename",
   126  		},
   127  		{
   128  			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",
   134  			wantFilename:    "golang.org/x/bar/filename",
   135  		},
   136  		{
   137  			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",
   143  			wantFilename:    "golang.org/x/bar/filename",
   144  		},
   145  		{
   146  			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,
   167  			wantPackageName: "foo",
   168  			wantImportPath:  "golang.org/x/foo",
   169  			wantFilename:    "foo/filename",
   170  		},
   171  	} {
   172  		context := fmt.Sprintf(`
   173  TEST: %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{
   182  			Parameter: proto.String(test.parameter),
   183  			ProtoFile: []*descriptorpb.FileDescriptorProto{
   184  				{
   185  					Name:    proto.String(filename),
   186  					Package: proto.String(protoPackageName),
   187  					Options: &descriptorpb.FileOptions{
   188  						GoPackage: proto.String(test.goPackageOption),
   189  					},
   190  				},
   191  			},
   192  		}
   193  		if test.generate {
   194  			req.FileToGenerate = []string{filename}
   195  		}
   196  		gen, err := Options{}.New(req)
   197  		if err != nil {
   198  			t.Errorf("%vNew(req) = %v", context, err)
   199  			continue
   200  		}
   201  		gotFile, ok := gen.FilesByPath[filename]
   202  		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  		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)
   216  		}
   217  	}
   218  }
   219  
   220  func TestPackageNameInference(t *testing.T) {
   221  	gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
   222  		Parameter: proto.String("Mdir/file1.proto=path/to/file1"),
   223  		ProtoFile: []*descriptorpb.FileDescriptorProto{
   224  			{
   225  				Name:    proto.String("dir/file1.proto"),
   226  				Package: proto.String("proto.package"),
   227  			},
   228  			{
   229  				Name:    proto.String("dir/file2.proto"),
   230  				Package: proto.String("proto.package"),
   231  				Options: &descriptorpb.FileOptions{
   232  					GoPackage: proto.String("path/to/file2"),
   233  				},
   234  			},
   235  		},
   236  		FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
   237  	})
   238  	if err != nil {
   239  		t.Fatalf("New(req) = %v", err)
   240  	}
   241  	if f1, ok := gen.FilesByPath["dir/file1.proto"]; !ok {
   242  		t.Errorf("missing file info for dir/file1.proto")
   243  	} else if f1.GoPackageName != "file1" {
   244  		t.Errorf("dir/file1.proto: GoPackageName=%v, want foo; package name should be derived from dir/file2.proto", f1.GoPackageName)
   245  	}
   246  }
   247  
   248  func TestInconsistentPackageNames(t *testing.T) {
   249  	_, err := Options{}.New(&pluginpb.CodeGeneratorRequest{
   250  		ProtoFile: []*descriptorpb.FileDescriptorProto{
   251  			{
   252  				Name:    proto.String("dir/file1.proto"),
   253  				Package: proto.String("proto.package"),
   254  				Options: &descriptorpb.FileOptions{
   255  					GoPackage: proto.String("golang.org/x/foo"),
   256  				},
   257  			},
   258  			{
   259  				Name:    proto.String("dir/file2.proto"),
   260  				Package: proto.String("proto.package"),
   261  				Options: &descriptorpb.FileOptions{
   262  					GoPackage: proto.String("golang.org/x/foo;bar"),
   263  				},
   264  			},
   265  		},
   266  		FileToGenerate: []string{"dir/file1.proto", "dir/file2.proto"},
   267  	})
   268  	if err == nil {
   269  		t.Fatalf("inconsistent package names for the same import path: New(req) = nil, want error")
   270  	}
   271  }
   272  
   273  func TestImports(t *testing.T) {
   274  	gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{})
   275  	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",
   289  		// Reference to a package conflicting with a predeclared identifier.
   290  		"golang.org/z/string",
   291  	} {
   292  		g.P("var _ = ", GoIdent{GoName: "X", GoImportPath: importPath}, " // ", importPath)
   293  	}
   294  	want := `package foo
   295  
   296  import (
   297  	bar "golang.org/x/bar"
   298  	baz "golang.org/x/baz"
   299  	bar1 "golang.org/y/bar"
   300  	string1 "golang.org/z/string"
   301  )
   302  
   303  var _ = X         // "golang.org/x/foo"
   304  var _ = bar.X     // "golang.org/x/bar"
   305  var _ = bar.X     // "golang.org/x/bar"
   306  var _ = bar1.X    // "golang.org/y/bar"
   307  var _ = baz.X     // "golang.org/x/baz"
   308  var _ = string1.X // "golang.org/z/string"
   309  `
   310  	got, err := g.Content()
   311  	if err != nil {
   312  		t.Fatalf("g.Content() = %v", err)
   313  	}
   314  	if diff := cmp.Diff(string(want), string(got)); diff != "" {
   315  		t.Fatalf("content mismatch (-want +got):\n%s", diff)
   316  	}
   317  }
   318  
   319  func TestImportRewrites(t *testing.T) {
   320  	gen, err := Options{
   321  		ImportRewriteFunc: func(i GoImportPath) GoImportPath {
   322  			return "prefix/" + i
   323  		},
   324  	}.New(&pluginpb.CodeGeneratorRequest{})
   325  	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  
   333  import (
   334  	bar "prefix/golang.org/x/bar"
   335  )
   336  
   337  var _ = bar.X
   338  `
   339  	got, err := g.Content()
   340  	if err != nil {
   341  		t.Fatalf("g.Content() = %v", err)
   342  	}
   343  	if diff := cmp.Diff(string(want), string(got)); diff != "" {
   344  		t.Fatalf("content mismatch (-want +got):\n%s", diff)
   345  	}
   346  }