github.com/wolfd/bazel-gazelle@v0.14.0/internal/language/go/resolve_test.go (about)

     1  /* Copyright 2018 The Bazel Authors. All rights reserved.
     2  
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7     http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package golang
    17  
    18  import (
    19  	"fmt"
    20  	"path/filepath"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/bazelbuild/bazel-gazelle/internal/config"
    25  	"github.com/bazelbuild/bazel-gazelle/internal/label"
    26  	"github.com/bazelbuild/bazel-gazelle/internal/language/proto"
    27  	"github.com/bazelbuild/bazel-gazelle/internal/repos"
    28  	"github.com/bazelbuild/bazel-gazelle/internal/resolve"
    29  	"github.com/bazelbuild/bazel-gazelle/internal/rule"
    30  	bzl "github.com/bazelbuild/buildtools/build"
    31  	"golang.org/x/tools/go/vcs"
    32  )
    33  
    34  func TestResolveGo(t *testing.T) {
    35  	type buildFile struct {
    36  		rel, content string
    37  	}
    38  	type testCase struct {
    39  		desc  string
    40  		index []buildFile
    41  		old   buildFile
    42  		want  string
    43  	}
    44  	for _, tc := range []testCase{
    45  		{
    46  			desc: "std",
    47  			index: []buildFile{{
    48  				rel: "bad",
    49  				content: `
    50  go_library(
    51      name = "go_default_library",
    52      importpath = "fmt",
    53  )
    54  `,
    55  			}},
    56  			old: buildFile{
    57  				content: `
    58  go_binary(
    59      name = "dep",
    60      _imports = ["fmt"],
    61  )
    62  `,
    63  			},
    64  			want: `go_binary(name = "dep")`,
    65  		}, {
    66  			desc: "self_import",
    67  			old: buildFile{content: `
    68  go_library(
    69      name = "go_default_library",
    70      importpath = "foo",
    71      _imports = ["foo"],
    72  )
    73  `},
    74  			want: `
    75  go_library(
    76      name = "go_default_library",
    77      importpath = "foo",
    78  )
    79  `,
    80  		}, {
    81  			desc: "self_import_embed",
    82  			old: buildFile{content: `
    83  go_library(
    84      name = "a",
    85      embeds = [":b"],
    86      importpath = "x",
    87  )
    88  
    89  go_library(
    90      name = "b",
    91      importpath = "x",
    92      _imports = ["x"],
    93  )
    94  `},
    95  			want: `
    96  go_library(
    97      name = "a",
    98      embeds = [":b"],
    99      importpath = "x",
   100  )
   101  
   102  go_library(
   103      name = "b",
   104      importpath = "x",
   105  )
   106  `,
   107  		}, {
   108  			desc: "same_package",
   109  			old: buildFile{content: `
   110  go_library(
   111      name = "a",
   112      importpath = "foo",
   113  )
   114  
   115  go_binary(
   116      name = "b",
   117      _imports = ["foo"],
   118  )
   119  `},
   120  			want: `
   121  go_library(
   122      name = "a",
   123      importpath = "foo",
   124  )
   125  
   126  go_binary(
   127      name = "b",
   128      deps = [":a"],
   129  )
   130  `,
   131  		}, {
   132  			desc: "different_package",
   133  			index: []buildFile{{
   134  				rel: "a",
   135  				content: `
   136  go_library(
   137      name = "a_lib",
   138      importpath = "aa",
   139  )
   140  `,
   141  			}},
   142  			old: buildFile{
   143  				rel: "b",
   144  				content: `
   145  go_binary(
   146      name = "bin",
   147      _imports = ["aa"],
   148  )
   149  `,
   150  			},
   151  			want: `
   152  go_binary(
   153      name = "bin",
   154      deps = ["//a:a_lib"],
   155  )
   156  `,
   157  		}, {
   158  			desc: "multiple_rules_ambiguous",
   159  			index: []buildFile{{
   160  				rel: "foo",
   161  				content: `
   162  go_library(
   163      name = "a",
   164      importpath = "example.com/foo",
   165  )
   166  
   167  go_library(
   168      name = "b",
   169      importpath = "example.com/foo",
   170  )
   171  `,
   172  			}},
   173  			old: buildFile{content: `
   174  go_binary(
   175      name = "bin",
   176      _imports = ["example.com/foo"],
   177  )
   178  `,
   179  			},
   180  			// an error should be reported, and no dependency should be emitted
   181  			want: `go_binary(name = "bin")`,
   182  		}, {
   183  			desc: "vendor_not_visible",
   184  			index: []buildFile{
   185  				{
   186  					rel: "",
   187  					content: `
   188  go_library(
   189      name = "root",
   190      importpath = "example.com/foo",
   191  )
   192  `,
   193  				}, {
   194  					rel: "a/vendor/foo",
   195  					content: `
   196  go_library(
   197      name = "vendored",
   198      importpath = "example.com/foo",
   199  )
   200  `,
   201  				},
   202  			},
   203  			old: buildFile{
   204  				rel: "b",
   205  				content: `
   206  go_binary(
   207      name = "bin",
   208      _imports = ["example.com/foo"],
   209  )
   210  `,
   211  			},
   212  			want: `
   213  go_binary(
   214      name = "bin",
   215      deps = ["//:root"],
   216  )
   217  `,
   218  		}, {
   219  			desc: "vendor_supercedes_nonvendor",
   220  			index: []buildFile{
   221  				{
   222  					rel: "",
   223  					content: `
   224  go_library(
   225      name = "root",
   226      importpath = "example.com/foo",
   227  )
   228  `,
   229  				}, {
   230  					rel: "vendor/foo",
   231  					content: `
   232  go_library(
   233      name = "vendored",
   234      importpath = "example.com/foo",
   235  )
   236  `,
   237  				},
   238  			},
   239  			old: buildFile{
   240  				rel: "sub",
   241  				content: `
   242  go_binary(
   243      name = "bin",
   244      _imports = ["example.com/foo"],
   245  )
   246  `,
   247  			},
   248  			want: `
   249  go_binary(
   250      name = "bin",
   251      deps = ["//vendor/foo:vendored"],
   252  )
   253  `,
   254  		}, {
   255  			desc: "deep_vendor_shallow_vendor",
   256  			index: []buildFile{
   257  				{
   258  					rel: "shallow/vendor",
   259  					content: `
   260  go_library(
   261      name = "shallow",
   262      importpath = "example.com/foo",
   263  )
   264  `,
   265  				}, {
   266  					rel: "shallow/deep/vendor",
   267  					content: `
   268  go_library(
   269      name = "deep",
   270      importpath = "example.com/foo",
   271  )
   272  `,
   273  				},
   274  			},
   275  			old: buildFile{
   276  				rel: "shallow/deep",
   277  				content: `
   278  go_binary(
   279      name = "bin",
   280      _imports = ["example.com/foo"],
   281  )
   282  `,
   283  			},
   284  			want: `
   285  go_binary(
   286      name = "bin",
   287      deps = ["//shallow/deep/vendor:deep"],
   288  )
   289  `,
   290  		}, {
   291  			desc: "nested_vendor",
   292  			index: []buildFile{
   293  				{
   294  					rel: "vendor/a",
   295  					content: `
   296  go_library(
   297      name = "a",
   298      importpath = "a",
   299  )
   300  `,
   301  				}, {
   302  					rel: "vendor/b/vendor/a",
   303  					content: `
   304  go_library(
   305      name = "a",
   306      importpath = "a",
   307  )
   308  `,
   309  				},
   310  			},
   311  			old: buildFile{
   312  				rel: "vendor/b/c",
   313  				content: `
   314  go_binary(
   315      name = "bin",
   316      _imports = ["a"],
   317  )
   318  `,
   319  			},
   320  			want: `
   321  go_binary(
   322      name = "bin",
   323      deps = ["//vendor/b/vendor/a"],
   324  )
   325  `,
   326  		}, {
   327  			desc: "skip_self_embed",
   328  			old: buildFile{content: `
   329  go_library(
   330      name = "go_default_library",
   331      srcs = ["lib.go"],
   332      importpath = "example.com/repo/lib",
   333  )
   334  
   335  go_test(
   336      name = "go_default_test",
   337      embed = [":go_default_library"],
   338      _imports = ["example.com/repo/lib"],
   339  )
   340  `,
   341  			},
   342  			want: `
   343  go_library(
   344      name = "go_default_library",
   345      srcs = ["lib.go"],
   346      importpath = "example.com/repo/lib",
   347  )
   348  
   349  go_test(
   350      name = "go_default_test",
   351      embed = [":go_default_library"],
   352  )
   353  `,
   354  		}, {
   355  			desc: "binary_embed",
   356  			old: buildFile{content: `
   357  go_library(
   358      name = "a",
   359      importpath = "a",
   360  )
   361  
   362  go_library(
   363      name = "b",
   364      embed = [":a"],
   365  )
   366  
   367  go_binary(
   368      name = "c",
   369      embed = [":a"],
   370      importpath = "a",
   371  )
   372  
   373  go_library(
   374      name = "d",
   375      _imports = ["a"],
   376  )
   377  `},
   378  			want: `
   379  go_library(
   380      name = "a",
   381      importpath = "a",
   382  )
   383  
   384  go_library(
   385      name = "b",
   386      embed = [":a"],
   387  )
   388  
   389  go_binary(
   390      name = "c",
   391      embed = [":a"],
   392      importpath = "a",
   393  )
   394  
   395  go_library(
   396      name = "d",
   397      deps = [":b"],
   398  )
   399  `,
   400  		}, {
   401  			desc: "local_unknown",
   402  			old: buildFile{content: `
   403  go_binary(
   404      name = "bin",
   405      _imports = [
   406          "example.com/repo/resolve",
   407          "example.com/repo/resolve/sub",
   408      ],
   409  )
   410  `},
   411  			want: `
   412  go_binary(
   413      name = "bin",
   414      deps = [
   415          ":go_default_library",
   416          "//sub:go_default_library",
   417      ],
   418  )
   419  `,
   420  		}, {
   421  			desc: "local_relative",
   422  			old: buildFile{
   423  				rel: "a",
   424  				content: `
   425  go_binary(
   426      name = "bin",
   427      _imports = [
   428          ".",
   429          "./b",
   430          "../c",
   431      ],
   432  )
   433  `,
   434  			},
   435  			want: `
   436  go_binary(
   437      name = "bin",
   438      deps = [
   439          ":go_default_library",
   440          "//a/b:go_default_library",
   441          "//c:go_default_library",
   442      ],
   443  )
   444  `,
   445  		}, {
   446  			desc: "vendor_no_index",
   447  			old: buildFile{content: `
   448  go_binary(
   449      name = "bin",
   450      _imports = ["example.com/outside/prefix"],
   451  )
   452  `},
   453  			want: `
   454  go_binary(
   455      name = "bin",
   456      deps = ["//vendor/example.com/outside/prefix:go_default_library"],
   457  )
   458  `,
   459  		}, {
   460  			desc: "test_and_library_not_indexed",
   461  			index: []buildFile{{
   462  				rel: "foo",
   463  				content: `
   464  go_test(
   465      name = "go_default_test",
   466      importpath = "example.com/foo",
   467  )
   468  
   469  go_binary(
   470      name = "cmd",
   471      importpath = "example.com/foo",
   472  )
   473  `,
   474  			}},
   475  			old: buildFile{content: `
   476  go_binary(
   477      name = "bin",
   478      _imports = ["example.com/foo"],
   479  )
   480  `},
   481  			want: `
   482  go_binary(
   483      name = "bin",
   484      deps = ["//vendor/example.com/foo:go_default_library"],
   485  )`,
   486  		}, {
   487  			desc: "proto_index",
   488  			index: []buildFile{{
   489  				rel: "sub",
   490  				content: `
   491  proto_library(
   492      name = "foo_proto",
   493      srcs = ["bar.proto"],
   494  )
   495  
   496  go_proto_library(
   497      name = "foo_go_proto",
   498      importpath = "example.com/foo",
   499      proto = ":foo_proto",
   500  )
   501  
   502  go_library(
   503      name = "embed",
   504      embed = [":foo_go_proto"],
   505      importpath = "example.com/foo",
   506  )
   507  `,
   508  			}},
   509  			old: buildFile{content: `
   510  go_proto_library(
   511      name = "dep_proto",
   512      _imports = ["sub/bar.proto"],
   513  )
   514  `},
   515  			want: `
   516  go_proto_library(
   517      name = "dep_proto",
   518      deps = ["//sub:embed"],
   519  )
   520  `,
   521  		}, {
   522  			desc: "proto_embed",
   523  			old: buildFile{content: `
   524  proto_library(
   525      name = "foo_proto",
   526      srcs = ["foo.proto"],
   527  )
   528  
   529  go_proto_library(
   530      name = "foo_go_proto",
   531      importpath = "example.com/repo/foo",
   532      proto = ":foo_proto",
   533  )
   534  
   535  go_library(
   536      name = "foo_embedder",
   537      embed = [":foo_go_proto"],
   538  )
   539  
   540  proto_library(
   541      name = "bar_proto",
   542      srcs = ["bar.proto"],
   543      _imports = ["foo.proto"],
   544  )
   545  
   546  go_proto_library(
   547      name = "bar_go_proto",
   548      proto = ":bar_proto",
   549      _imports = ["foo.proto"],
   550  )
   551  `},
   552  			want: `
   553  proto_library(
   554      name = "foo_proto",
   555      srcs = ["foo.proto"],
   556  )
   557  
   558  go_proto_library(
   559      name = "foo_go_proto",
   560      importpath = "example.com/repo/foo",
   561      proto = ":foo_proto",
   562  )
   563  
   564  go_library(
   565      name = "foo_embedder",
   566      embed = [":foo_go_proto"],
   567  )
   568  
   569  proto_library(
   570      name = "bar_proto",
   571      srcs = ["bar.proto"],
   572      deps = [":foo_proto"],
   573  )
   574  
   575  go_proto_library(
   576      name = "bar_go_proto",
   577      proto = ":bar_proto",
   578      deps = [":foo_embedder"],
   579  )
   580  `,
   581  		}, {
   582  			desc: "proto_wkt",
   583  			old: buildFile{content: `
   584  go_proto_library(
   585      name = "wkts_go_proto",
   586      _imports = [
   587          "google/protobuf/any.proto",
   588          "google/protobuf/api.proto",
   589          "google/protobuf/compiler_plugin.proto",
   590          "google/protobuf/descriptor.proto",
   591          "google/protobuf/duration.proto",
   592          "google/protobuf/empty.proto",
   593          "google/protobuf/field_mask.proto",
   594          "google/protobuf/source_context.proto",
   595          "google/protobuf/struct.proto",
   596          "google/protobuf/timestamp.proto",
   597          "google/protobuf/type.proto",
   598          "google/protobuf/wrappers.proto",
   599          "google/api/http.proto",
   600          "google/rpc/status.proto",
   601          "google/type/latlng.proto",
   602     ],
   603  )
   604  
   605  go_library(
   606      name = "wkts_go_lib",
   607      _imports = [
   608          "github.com/golang/protobuf/ptypes/any",
   609          "google.golang.org/genproto/protobuf/api",
   610          "github.com/golang/protobuf/protoc-gen-go/descriptor",
   611          "github.com/golang/protobuf/ptypes/duration",
   612          "github.com/golang/protobuf/ptypes/empty",
   613          "google.golang.org/genproto/protobuf/field_mask",
   614          "google.golang.org/genproto/protobuf/source_context",
   615          "github.com/golang/protobuf/ptypes/struct",
   616          "github.com/golang/protobuf/ptypes/timestamp",
   617          "github.com/golang/protobuf/ptypes/wrappers",
   618          "github.com/golang/protobuf/protoc-gen-go/plugin",
   619          "google.golang.org/genproto/protobuf/ptype",
   620          "google.golang.org/genproto/googleapis/api/annotations",
   621          "google.golang.org/genproto/googleapis/rpc/status",
   622          "google.golang.org/genproto/googleapis/type/latlng",
   623     ],
   624  )
   625  `},
   626  			want: `
   627  go_proto_library(
   628      name = "wkts_go_proto",
   629      deps = [
   630          "@go_googleapis//google/api:annotations_go_proto",
   631          "@go_googleapis//google/rpc:status_go_proto",
   632          "@go_googleapis//google/type:latlng_go_proto",
   633      ],
   634  )
   635  
   636  go_library(
   637      name = "wkts_go_lib",
   638      deps = [
   639          "@go_googleapis//google/api:annotations_go_proto",
   640          "@go_googleapis//google/rpc:status_go_proto",
   641          "@go_googleapis//google/type:latlng_go_proto",
   642          "@io_bazel_rules_go//proto/wkt:any_go_proto",
   643          "@io_bazel_rules_go//proto/wkt:api_go_proto",
   644          "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
   645          "@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
   646          "@io_bazel_rules_go//proto/wkt:duration_go_proto",
   647          "@io_bazel_rules_go//proto/wkt:empty_go_proto",
   648          "@io_bazel_rules_go//proto/wkt:field_mask_go_proto",
   649          "@io_bazel_rules_go//proto/wkt:source_context_go_proto",
   650          "@io_bazel_rules_go//proto/wkt:struct_go_proto",
   651          "@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
   652          "@io_bazel_rules_go//proto/wkt:type_go_proto",
   653          "@io_bazel_rules_go//proto/wkt:wrappers_go_proto",
   654      ],
   655  )
   656  `,
   657  		}, {
   658  			desc: "proto_ptypes",
   659  			old: buildFile{content: `
   660  go_library(
   661      name = "go_default_library",
   662      _imports = [
   663          "github.com/golang/protobuf/jsonpb",
   664          "github.com/golang/protobuf/descriptor",
   665          "github.com/golang/protobuf/ptypes",
   666      ],
   667  )
   668  `},
   669  			want: `
   670  go_library(
   671      name = "go_default_library",
   672      deps = [
   673          "@com_github_golang_protobuf//descriptor:go_default_library_gen",
   674          "@com_github_golang_protobuf//jsonpb:go_default_library_gen",
   675          "@com_github_golang_protobuf//ptypes:go_default_library_gen",
   676      ],
   677  )
   678  `,
   679  		}, {
   680  			desc: "proto_self_import",
   681  			old: buildFile{content: `
   682  proto_library(
   683      name = "foo_proto",
   684      srcs = [
   685          "a.proto",
   686          "b.proto",
   687      ],
   688  )
   689  
   690  go_proto_library(
   691      name = "foo_go_proto",
   692      importpath = "foo",
   693      proto = ":foo_proto",
   694      _imports = ["a.proto"],
   695  )
   696  
   697  go_library(
   698      name = "go_default_library",
   699      embed = [":foo_go_proto"],
   700      importpath = "foo",
   701  )
   702  `},
   703  			want: `
   704  proto_library(
   705      name = "foo_proto",
   706      srcs = [
   707          "a.proto",
   708          "b.proto",
   709      ],
   710  )
   711  
   712  go_proto_library(
   713      name = "foo_go_proto",
   714      importpath = "foo",
   715      proto = ":foo_proto",
   716  )
   717  
   718  go_library(
   719      name = "go_default_library",
   720      embed = [":foo_go_proto"],
   721      importpath = "foo",
   722  )
   723  `,
   724  		},
   725  	} {
   726  		t.Run(tc.desc, func(t *testing.T) {
   727  			c, _, langs := testConfig()
   728  			gc := getGoConfig(c)
   729  			gc.prefix = "example.com/repo/resolve"
   730  			gc.depMode = vendorMode
   731  			kindToResolver := make(map[string]resolve.Resolver)
   732  			for _, lang := range langs {
   733  				for kind := range lang.Kinds() {
   734  					kindToResolver[kind] = lang
   735  				}
   736  			}
   737  			ix := resolve.NewRuleIndex(kindToResolver)
   738  			rc := testRemoteCache(nil)
   739  
   740  			for _, bf := range tc.index {
   741  				buildPath := filepath.Join(filepath.FromSlash(bf.rel), "BUILD.bazel")
   742  				f, err := rule.LoadData(buildPath, bf.rel, []byte(bf.content))
   743  				if err != nil {
   744  					t.Fatal(err)
   745  				}
   746  				for _, r := range f.Rules {
   747  					ix.AddRule(c, r, f)
   748  				}
   749  			}
   750  			buildPath := filepath.Join(filepath.FromSlash(tc.old.rel), "BUILD.bazel")
   751  			f, err := rule.LoadData(buildPath, tc.old.rel, []byte(tc.old.content))
   752  			if err != nil {
   753  				t.Fatal(err)
   754  			}
   755  			for _, r := range f.Rules {
   756  				convertImportsAttr(r)
   757  				ix.AddRule(c, r, f)
   758  			}
   759  			ix.Finish()
   760  			for _, r := range f.Rules {
   761  				kindToResolver[r.Kind()].Resolve(c, ix, rc, r, label.New("", tc.old.rel, r.Name()))
   762  			}
   763  			f.Sync()
   764  			got := strings.TrimSpace(string(bzl.Format(f.File)))
   765  			want := strings.TrimSpace(tc.want)
   766  			if got != want {
   767  				t.Errorf("got:\n%s\nwant:\n%s", got, want)
   768  			}
   769  		})
   770  	}
   771  }
   772  
   773  func TestResolveDisableGlobal(t *testing.T) {
   774  	c, _, langs := testConfig()
   775  	gc := getGoConfig(c)
   776  	gc.prefix = "example.com/repo"
   777  	gc.prefixSet = true
   778  	proto.GetProtoConfig(c).Mode = proto.DisableGlobalMode
   779  	ix := resolve.NewRuleIndex(nil)
   780  	ix.Finish()
   781  	rc := testRemoteCache([]repos.Repo{
   782  		{
   783  			Name:     "com_github_golang_protobuf",
   784  			GoPrefix: "github.com/golang/protobuf",
   785  		}, {
   786  			Name:     "org_golang_google_genproto",
   787  			GoPrefix: "golang.org/google/genproto",
   788  		},
   789  	})
   790  	gl := langs[1].(*goLang)
   791  	oldContent := []byte(`
   792  go_library(
   793      name = "go_default_library",
   794      importpath = "foo",
   795      _imports = [
   796          "github.com/golang/protobuf/ptypes/any",
   797          "google.golang.org/genproto/protobuf/api",
   798          "github.com/golang/protobuf/protoc-gen-go/descriptor",
   799          "github.com/golang/protobuf/ptypes/duration",
   800          "github.com/golang/protobuf/ptypes/empty",
   801          "google.golang.org/genproto/protobuf/field_mask",
   802          "google.golang.org/genproto/protobuf/source_context",
   803          "github.com/golang/protobuf/ptypes/struct",
   804          "github.com/golang/protobuf/ptypes/timestamp",
   805          "github.com/golang/protobuf/ptypes/wrappers",
   806          "github.com/golang/protobuf/protoc-gen-go/plugin",
   807          "google.golang.org/genproto/protobuf/ptype",
   808          "google.golang.org/genproto/googleapis/api/annotations",
   809          "google.golang.org/genproto/googleapis/rpc/status",
   810          "google.golang.org/genproto/googleapis/type/latlng",
   811          "github.com/golang/protobuf/jsonpb",
   812          "github.com/golang/protobuf/descriptor",
   813          "github.com/golang/protobuf/ptypes",
   814      ],
   815  )
   816  `)
   817  	f, err := rule.LoadData("BUILD.bazel", "", oldContent)
   818  	if err != nil {
   819  		t.Fatal(err)
   820  	}
   821  	for _, r := range f.Rules {
   822  		convertImportsAttr(r)
   823  		gl.Resolve(c, ix, rc, r, label.New("", "", r.Name()))
   824  	}
   825  	f.Sync()
   826  	got := strings.TrimSpace(string(bzl.Format(f.File)))
   827  	want := strings.TrimSpace(`
   828  go_library(
   829      name = "go_default_library",
   830      importpath = "foo",
   831      deps = [
   832          "@com_github_golang_protobuf//descriptor:go_default_library",
   833          "@com_github_golang_protobuf//jsonpb:go_default_library",
   834          "@com_github_golang_protobuf//protoc-gen-go/descriptor:go_default_library",
   835          "@com_github_golang_protobuf//protoc-gen-go/plugin:go_default_library",
   836          "@com_github_golang_protobuf//ptypes:go_default_library",
   837          "@com_github_golang_protobuf//ptypes/any:go_default_library",
   838          "@com_github_golang_protobuf//ptypes/duration:go_default_library",
   839          "@com_github_golang_protobuf//ptypes/empty:go_default_library",
   840          "@com_github_golang_protobuf//ptypes/struct:go_default_library",
   841          "@com_github_golang_protobuf//ptypes/timestamp:go_default_library",
   842          "@com_github_golang_protobuf//ptypes/wrappers:go_default_library",
   843          "@org_golang_google_genproto//googleapis/api/annotations:go_default_library",
   844          "@org_golang_google_genproto//googleapis/rpc/status:go_default_library",
   845          "@org_golang_google_genproto//googleapis/type/latlng:go_default_library",
   846          "@org_golang_google_genproto//protobuf/api:go_default_library",
   847          "@org_golang_google_genproto//protobuf/field_mask:go_default_library",
   848          "@org_golang_google_genproto//protobuf/ptype:go_default_library",
   849          "@org_golang_google_genproto//protobuf/source_context:go_default_library",
   850      ],
   851  )
   852  `)
   853  	if got != want {
   854  		t.Errorf("got:\n%s\nwant:%s", got, want)
   855  	}
   856  }
   857  
   858  func TestResolveExternal(t *testing.T) {
   859  	c, _, langs := testConfig()
   860  	gc := getGoConfig(c)
   861  	gc.prefix = "example.com/local"
   862  	ix := resolve.NewRuleIndex(nil)
   863  	ix.Finish()
   864  	gl := langs[1].(*goLang)
   865  	for _, tc := range []struct {
   866  		desc, importpath string
   867  		repos            []repos.Repo
   868  		want             string
   869  	}{
   870  		{
   871  			desc:       "top",
   872  			importpath: "example.com/repo",
   873  			want:       "@com_example_repo//:go_default_library",
   874  		}, {
   875  			desc:       "sub",
   876  			importpath: "example.com/repo/lib",
   877  			want:       "@com_example_repo//lib:go_default_library",
   878  		}, {
   879  			desc: "custom_repo",
   880  			repos: []repos.Repo{{
   881  				Name:     "custom_repo_name",
   882  				GoPrefix: "example.com/repo",
   883  			}},
   884  			importpath: "example.com/repo/lib",
   885  			want:       "@custom_repo_name//lib:go_default_library",
   886  		}, {
   887  			desc:       "qualified",
   888  			importpath: "example.com/repo.git/lib",
   889  			want:       "@com_example_repo_git//lib:go_default_library",
   890  		}, {
   891  			desc:       "domain",
   892  			importpath: "example.com/lib",
   893  			want:       "@com_example//lib:go_default_library",
   894  		},
   895  	} {
   896  		t.Run(tc.desc, func(t *testing.T) {
   897  			rc := testRemoteCache(tc.repos)
   898  			r := rule.NewRule("go_library", "x")
   899  			imports := rule.PlatformStrings{Generic: []string{tc.importpath}}
   900  			r.SetPrivateAttr(config.GazelleImportsKey, imports)
   901  			gl.Resolve(c, ix, rc, r, label.New("", "", "x"))
   902  			deps := r.AttrStrings("deps")
   903  			if len(deps) != 1 {
   904  				t.Fatalf("deps: got %d; want 1", len(deps))
   905  			}
   906  			if deps[0] != tc.want {
   907  				t.Errorf("got %s; want %s", deps[0], tc.want)
   908  			}
   909  		})
   910  	}
   911  }
   912  
   913  func testRemoteCache(knownRepos []repos.Repo) *repos.RemoteCache {
   914  	rc := repos.NewRemoteCache(knownRepos)
   915  	rc.RepoRootForImportPath = stubRepoRootForImportPath
   916  	rc.HeadCmd = func(remote, vcs string) (string, error) {
   917  		return "", fmt.Errorf("HeadCmd not supported in test")
   918  	}
   919  	return rc
   920  }
   921  
   922  // stubRepoRootForImportPath is a stub implementation of vcs.RepoRootForImportPath
   923  func stubRepoRootForImportPath(importpath string, verbose bool) (*vcs.RepoRoot, error) {
   924  	if strings.HasPrefix(importpath, "example.com/repo.git") {
   925  		return &vcs.RepoRoot{
   926  			VCS:  vcs.ByCmd("git"),
   927  			Repo: "https://example.com/repo.git",
   928  			Root: "example.com/repo.git",
   929  		}, nil
   930  	}
   931  
   932  	if strings.HasPrefix(importpath, "example.com/repo") {
   933  		return &vcs.RepoRoot{
   934  			VCS:  vcs.ByCmd("git"),
   935  			Repo: "https://example.com/repo.git",
   936  			Root: "example.com/repo",
   937  		}, nil
   938  	}
   939  
   940  	if strings.HasPrefix(importpath, "example.com") {
   941  		return &vcs.RepoRoot{
   942  			VCS:  vcs.ByCmd("git"),
   943  			Repo: "https://example.com",
   944  			Root: "example.com",
   945  		}, nil
   946  	}
   947  
   948  	return nil, fmt.Errorf("could not resolve import path: %q", importpath)
   949  }
   950  
   951  func convertImportsAttr(r *rule.Rule) {
   952  	kind := r.Kind()
   953  	value := r.AttrStrings("_imports")
   954  	r.DelAttr("_imports")
   955  	if _, ok := goKinds[kind]; ok {
   956  		r.SetPrivateAttr(config.GazelleImportsKey, rule.PlatformStrings{Generic: value})
   957  	} else if kind == "proto_library" {
   958  		r.SetPrivateAttr(config.GazelleImportsKey, value)
   959  	}
   960  }