github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/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/label"
    25  	"github.com/bazelbuild/bazel-gazelle/pathtools"
    26  	"github.com/bazelbuild/bazel-gazelle/repo"
    27  	"github.com/bazelbuild/bazel-gazelle/resolve"
    28  	"github.com/bazelbuild/bazel-gazelle/rule"
    29  	bzl "github.com/bazelbuild/buildtools/build"
    30  	"golang.org/x/tools/go/vcs"
    31  )
    32  
    33  func TestResolveGo(t *testing.T) {
    34  	type buildFile struct {
    35  		rel, content string
    36  	}
    37  	type testCase struct {
    38  		desc             string
    39  		index            []buildFile
    40  		old              buildFile
    41  		want             string
    42  		skipIndex        bool
    43  		namingConvention namingConvention
    44  	}
    45  	for _, tc := range []testCase{
    46  		{
    47  			desc: "std",
    48  			index: []buildFile{{
    49  				rel: "bad",
    50  				content: `
    51  go_library(
    52      name = "go_default_library",
    53      importpath = "fmt",
    54  )
    55  `,
    56  			}},
    57  			old: buildFile{
    58  				content: `
    59  go_binary(
    60      name = "dep",
    61      _imports = ["fmt"],
    62  )
    63  `,
    64  			},
    65  			want: `go_binary(name = "dep")`,
    66  		}, {
    67  			desc: "self_import",
    68  			old: buildFile{content: `
    69  go_library(
    70      name = "go_default_library",
    71      importpath = "foo",
    72      _imports = ["foo"],
    73  )
    74  `},
    75  			want: `
    76  go_library(
    77      name = "go_default_library",
    78      importpath = "foo",
    79  )
    80  `,
    81  		}, {
    82  			desc: "self_import_embed",
    83  			old: buildFile{content: `
    84  go_library(
    85      name = "a",
    86      embeds = [":b"],
    87      importpath = "x",
    88  )
    89  
    90  go_library(
    91      name = "b",
    92      importpath = "x",
    93      _imports = ["x"],
    94  )
    95  `},
    96  			want: `
    97  go_library(
    98      name = "a",
    99      embeds = [":b"],
   100      importpath = "x",
   101  )
   102  
   103  go_library(
   104      name = "b",
   105      importpath = "x",
   106  )
   107  `,
   108  		}, {
   109  			desc: "override",
   110  			index: []buildFile{{
   111  				content: `
   112  # gazelle:resolve go go github.com/golang/protobuf/ptypes //:good
   113  go_library(
   114      name = "bad",
   115      importpath = "github.com/golang/protobuf/ptypes",
   116  )
   117  `,
   118  			}},
   119  			old: buildFile{
   120  				rel: "test",
   121  				content: `
   122  go_library(
   123      name = "a",
   124      importpath = "a",
   125      _imports = ["github.com/golang/protobuf/ptypes"],
   126  )
   127  `,
   128  			},
   129  			want: `
   130  go_library(
   131      name = "a",
   132      importpath = "a",
   133      deps = ["//:good"],
   134  )
   135  `,
   136  		}, {
   137  			desc: "same_package",
   138  			old: buildFile{content: `
   139  go_library(
   140      name = "a",
   141      importpath = "foo",
   142  )
   143  
   144  go_binary(
   145      name = "b",
   146      _imports = ["foo"],
   147  )
   148  `},
   149  			want: `
   150  go_library(
   151      name = "a",
   152      importpath = "foo",
   153  )
   154  
   155  go_binary(
   156      name = "b",
   157      deps = [":a"],
   158  )
   159  `,
   160  		}, {
   161  			desc: "different_package",
   162  			index: []buildFile{{
   163  				rel: "a",
   164  				content: `
   165  go_library(
   166      name = "a_lib",
   167      importpath = "aa",
   168  )
   169  `,
   170  			}},
   171  			old: buildFile{
   172  				rel: "b",
   173  				content: `
   174  go_binary(
   175      name = "bin",
   176      _imports = ["aa"],
   177  )
   178  `,
   179  			},
   180  			want: `
   181  go_binary(
   182      name = "bin",
   183      deps = ["//a:a_lib"],
   184  )
   185  `,
   186  		}, {
   187  			desc: "multiple_rules_ambiguous",
   188  			index: []buildFile{{
   189  				rel: "foo",
   190  				content: `
   191  go_library(
   192      name = "a",
   193      importpath = "example.com/foo",
   194  )
   195  
   196  go_library(
   197      name = "b",
   198      importpath = "example.com/foo",
   199  )
   200  `,
   201  			}},
   202  			old: buildFile{
   203  				content: `
   204  go_binary(
   205      name = "bin",
   206      _imports = ["example.com/foo"],
   207  )
   208  `,
   209  			},
   210  			// an error should be reported, and no dependency should be emitted
   211  			want: `go_binary(name = "bin")`,
   212  		}, {
   213  			desc: "vendor_not_visible",
   214  			index: []buildFile{
   215  				{
   216  					rel: "",
   217  					content: `
   218  go_library(
   219      name = "root",
   220      importpath = "example.com/foo",
   221  )
   222  `,
   223  				}, {
   224  					rel: "a/vendor/foo",
   225  					content: `
   226  go_library(
   227      name = "vendored",
   228      importpath = "example.com/foo",
   229  )
   230  `,
   231  				},
   232  			},
   233  			old: buildFile{
   234  				rel: "b",
   235  				content: `
   236  go_binary(
   237      name = "bin",
   238      _imports = ["example.com/foo"],
   239  )
   240  `,
   241  			},
   242  			want: `
   243  go_binary(
   244      name = "bin",
   245      deps = ["//:root"],
   246  )
   247  `,
   248  		}, {
   249  			desc: "vendor_supercedes_nonvendor",
   250  			index: []buildFile{
   251  				{
   252  					rel: "",
   253  					content: `
   254  go_library(
   255      name = "root",
   256      importpath = "example.com/foo",
   257  )
   258  `,
   259  				}, {
   260  					rel: "vendor/foo",
   261  					content: `
   262  go_library(
   263      name = "vendored",
   264      importpath = "example.com/foo",
   265  )
   266  `,
   267  				},
   268  			},
   269  			old: buildFile{
   270  				rel: "sub",
   271  				content: `
   272  go_binary(
   273      name = "bin",
   274      _imports = ["example.com/foo"],
   275  )
   276  `,
   277  			},
   278  			want: `
   279  go_binary(
   280      name = "bin",
   281      deps = ["//vendor/foo:vendored"],
   282  )
   283  `,
   284  		}, {
   285  			desc: "deep_vendor_shallow_vendor",
   286  			index: []buildFile{
   287  				{
   288  					rel: "shallow/vendor",
   289  					content: `
   290  go_library(
   291      name = "shallow",
   292      importpath = "example.com/foo",
   293  )
   294  `,
   295  				}, {
   296  					rel: "shallow/deep/vendor",
   297  					content: `
   298  go_library(
   299      name = "deep",
   300      importpath = "example.com/foo",
   301  )
   302  `,
   303  				},
   304  			},
   305  			old: buildFile{
   306  				rel: "shallow/deep",
   307  				content: `
   308  go_binary(
   309      name = "bin",
   310      _imports = ["example.com/foo"],
   311  )
   312  `,
   313  			},
   314  			want: `
   315  go_binary(
   316      name = "bin",
   317      deps = ["//shallow/deep/vendor:deep"],
   318  )
   319  `,
   320  		}, {
   321  			desc: "nested_vendor",
   322  			index: []buildFile{
   323  				{
   324  					rel: "vendor/a",
   325  					content: `
   326  go_library(
   327      name = "a",
   328      importpath = "a",
   329  )
   330  `,
   331  				}, {
   332  					rel: "vendor/b/vendor/a",
   333  					content: `
   334  go_library(
   335      name = "a",
   336      importpath = "a",
   337  )
   338  `,
   339  				},
   340  			},
   341  			old: buildFile{
   342  				rel: "vendor/b/c",
   343  				content: `
   344  go_binary(
   345      name = "bin",
   346      _imports = ["a"],
   347  )
   348  `,
   349  			},
   350  			want: `
   351  go_binary(
   352      name = "bin",
   353      deps = ["//vendor/b/vendor/a"],
   354  )
   355  `,
   356  		}, {
   357  			desc: "skip_self_embed",
   358  			old: buildFile{
   359  				content: `
   360  go_library(
   361      name = "go_default_library",
   362      srcs = ["lib.go"],
   363      importpath = "example.com/repo/lib",
   364  )
   365  
   366  go_test(
   367      name = "go_default_test",
   368      embed = [":go_default_library"],
   369      _imports = ["example.com/repo/lib"],
   370  )
   371  `,
   372  			},
   373  			want: `
   374  go_library(
   375      name = "go_default_library",
   376      srcs = ["lib.go"],
   377      importpath = "example.com/repo/lib",
   378  )
   379  
   380  go_test(
   381      name = "go_default_test",
   382      embed = [":go_default_library"],
   383  )
   384  `,
   385  		}, {
   386  			desc: "binary_embed",
   387  			old: buildFile{content: `
   388  go_library(
   389      name = "a",
   390      importpath = "a",
   391  )
   392  
   393  go_library(
   394      name = "b",
   395      embed = [":a"],
   396  )
   397  
   398  go_binary(
   399      name = "c",
   400      embed = [":a"],
   401      importpath = "a",
   402  )
   403  
   404  go_library(
   405      name = "d",
   406      _imports = ["a"],
   407  )
   408  `},
   409  			want: `
   410  go_library(
   411      name = "a",
   412      importpath = "a",
   413  )
   414  
   415  go_library(
   416      name = "b",
   417      embed = [":a"],
   418  )
   419  
   420  go_binary(
   421      name = "c",
   422      embed = [":a"],
   423      importpath = "a",
   424  )
   425  
   426  go_library(
   427      name = "d",
   428      deps = [":b"],
   429  )
   430  `,
   431  		}, {
   432  			desc: "gazelle_special",
   433  			old: buildFile{content: `
   434  go_library(
   435      name = "go_default_library",
   436      _imports = [
   437          "github.com/bazelbuild/bazel-gazelle/language",
   438          "github.com/bazelbuild/rules_go/go/tools/bazel",
   439      ],
   440  )
   441  `},
   442  			want: `
   443  go_library(
   444      name = "go_default_library",
   445      deps = [
   446          "@bazel_gazelle//language:go_default_library",
   447          "@io_bazel_rules_go//go/tools/bazel:go_default_library",
   448      ],
   449  )
   450  `,
   451  		}, {
   452  			desc:      "local_unknown",
   453  			skipIndex: true,
   454  			old: buildFile{content: `
   455  go_binary(
   456      name = "bin",
   457      _imports = [
   458          "example.com/repo/resolve",
   459          "example.com/repo/resolve/sub",
   460      ],
   461  )
   462  `},
   463  			want: `
   464  go_binary(
   465      name = "bin",
   466      deps = [
   467          ":resolve",
   468          "//sub",
   469      ],
   470  )
   471  `,
   472  		}, {
   473  			desc: "local_relative",
   474  			index: []buildFile{
   475  				{
   476  					rel: "a",
   477  					content: `
   478  go_library(
   479      name = "go_default_library",
   480      importpath = "example.com/repo/resolve/a",
   481  )
   482  `,
   483  				},
   484  				{
   485  					rel: "a/b",
   486  					content: `
   487  go_library(
   488      name = "go_default_library",
   489      importpath = "example.com/repo/resolve/a/b",
   490  )
   491  `,
   492  				},
   493  				{
   494  					rel: "c",
   495  					content: `
   496  go_library(
   497      name = "go_default_library",
   498      importpath = "example.com/repo/resolve/c",
   499  )
   500  `,
   501  				},
   502  			},
   503  			old: buildFile{
   504  				rel: "a",
   505  				content: `
   506  go_binary(
   507      name = "bin",
   508      _imports = [
   509          ".",
   510          "./b",
   511          "../c",
   512      ],
   513  )
   514  `,
   515  			},
   516  			want: `
   517  go_binary(
   518      name = "bin",
   519      deps = [
   520          ":go_default_library",
   521          "//a/b:go_default_library",
   522          "//c:go_default_library",
   523      ],
   524  )
   525  `,
   526  		}, {
   527  			desc: "vendor_no_index",
   528  			old: buildFile{content: `
   529  go_binary(
   530      name = "bin",
   531      _imports = ["example.com/outside/prefix"],
   532  )
   533  `},
   534  			want: `
   535  go_binary(
   536      name = "bin",
   537      deps = ["//vendor/example.com/outside/prefix"],
   538  )
   539  `,
   540  		}, {
   541  			desc:             "vendor with go_naming_convention=import",
   542  			namingConvention: importNamingConvention,
   543  			old: buildFile{content: `
   544  go_binary(
   545      name = "bin",
   546      _imports = ["example.com/outside/prefix"],
   547  )
   548  `},
   549  			want: `
   550  go_binary(
   551      name = "bin",
   552      deps = ["//vendor/example.com/outside/prefix"],
   553  )
   554  `,
   555  		}, {
   556  			desc: "test_and_library_not_indexed",
   557  			index: []buildFile{{
   558  				rel: "foo",
   559  				content: `
   560  go_test(
   561      name = "go_default_test",
   562      importpath = "example.com/foo",
   563  )
   564  
   565  go_binary(
   566      name = "cmd",
   567      importpath = "example.com/foo",
   568  )
   569  `,
   570  			}},
   571  			old: buildFile{content: `
   572  go_binary(
   573      name = "bin",
   574      _imports = ["example.com/foo"],
   575  )
   576  `},
   577  			want: `
   578  go_binary(
   579      name = "bin",
   580      deps = ["//vendor/example.com/foo"],
   581  )`,
   582  		}, {
   583  			desc: "proto_override",
   584  			index: []buildFile{{
   585  				rel: "",
   586  				content: `
   587  # gazelle:resolve proto go google/rpc/status.proto :good
   588  
   589  proto_library(
   590      name = "bad_proto",
   591      srcs = ["google/rpc/status.proto"],
   592  )
   593  
   594  go_proto_library(
   595      name = "bad_go_proto",
   596      proto = ":bad_proto",
   597      importpath = "bad",
   598  )
   599  `,
   600  			}},
   601  			old: buildFile{
   602  				rel: "test",
   603  				content: `
   604  go_proto_library(
   605      name = "dep_go_proto",
   606      importpath = "test",
   607      _imports = ["google/rpc/status.proto"],
   608  )
   609  `,
   610  			},
   611  			want: `
   612  go_proto_library(
   613      name = "dep_go_proto",
   614      importpath = "test",
   615      deps = ["//:good"],
   616  )
   617  `,
   618  		}, {
   619  			desc: "proto_override_regexp",
   620  			index: []buildFile{{
   621  				rel: "",
   622  				content: `
   623  # gazelle:resolve_regexp proto go google/rpc/.*\.proto :good
   624  
   625  proto_library(
   626      name = "bad_proto",
   627      srcs = ["google/rpc/status.proto"],
   628  )
   629  
   630  go_proto_library(
   631      name = "bad_go_proto",
   632      proto = ":bad_proto",
   633      importpath = "bad",
   634  )
   635  `,
   636  			}},
   637  			old: buildFile{
   638  				rel: "test",
   639  				content: `
   640  go_proto_library(
   641      name = "dep_go_proto",
   642      importpath = "test",
   643      _imports = [
   644  		"google/rpc/status.proto",
   645  		"google/rpc/status1.proto",
   646  		"google/rpc/status2.proto",
   647  	],
   648  )
   649  `,
   650  			},
   651  			want: `
   652  go_proto_library(
   653      name = "dep_go_proto",
   654      importpath = "test",
   655      deps = ["//:good"],
   656  )
   657  `,
   658  		}, {
   659  			desc: "proto_index",
   660  			index: []buildFile{{
   661  				rel: "sub",
   662  				content: `
   663  proto_library(
   664      name = "foo_proto",
   665      srcs = ["bar.proto"],
   666  )
   667  
   668  go_proto_library(
   669      name = "foo_go_proto",
   670      importpath = "example.com/foo",
   671      proto = ":foo_proto",
   672  )
   673  
   674  go_library(
   675      name = "embed",
   676      embed = [":foo_go_proto"],
   677      importpath = "example.com/foo",
   678  )
   679  `,
   680  			}},
   681  			old: buildFile{content: `
   682  go_proto_library(
   683      name = "dep_proto",
   684      _imports = ["sub/bar.proto"],
   685  )
   686  `},
   687  			want: `
   688  go_proto_library(
   689      name = "dep_proto",
   690      deps = ["//sub:embed"],
   691  )
   692  `,
   693  		}, {
   694  			desc: "proto_embed",
   695  			old: buildFile{content: `
   696  proto_library(
   697      name = "foo_proto",
   698      srcs = ["foo.proto"],
   699  )
   700  
   701  go_proto_library(
   702      name = "foo_go_proto",
   703      importpath = "example.com/repo/foo",
   704      proto = ":foo_proto",
   705  )
   706  
   707  go_library(
   708      name = "foo_embedder",
   709      embed = [":foo_go_proto"],
   710  )
   711  
   712  proto_library(
   713      name = "bar_proto",
   714      srcs = ["bar.proto"],
   715      _imports = ["foo.proto"],
   716  )
   717  
   718  go_proto_library(
   719      name = "bar_go_proto",
   720      proto = ":bar_proto",
   721      _imports = ["foo.proto"],
   722  )
   723  `},
   724  			want: `
   725  proto_library(
   726      name = "foo_proto",
   727      srcs = ["foo.proto"],
   728  )
   729  
   730  go_proto_library(
   731      name = "foo_go_proto",
   732      importpath = "example.com/repo/foo",
   733      proto = ":foo_proto",
   734  )
   735  
   736  go_library(
   737      name = "foo_embedder",
   738      embed = [":foo_go_proto"],
   739  )
   740  
   741  proto_library(
   742      name = "bar_proto",
   743      srcs = ["bar.proto"],
   744      deps = [":foo_proto"],
   745  )
   746  
   747  go_proto_library(
   748      name = "bar_go_proto",
   749      proto = ":bar_proto",
   750      deps = [":foo_embedder"],
   751  )
   752  `,
   753  		}, {
   754  			desc: "proto_dedup",
   755  			index: []buildFile{{
   756  				rel: "sub",
   757  				content: `
   758  proto_library(
   759      name = "foo_proto",
   760      srcs = [
   761          "a.proto",
   762          "b.proto",
   763      ],
   764  )
   765  
   766  go_proto_library(
   767      name = "foo_go_proto",
   768      proto = ":foo_proto",
   769      importpath = "sub",
   770  )
   771  `,
   772  			}},
   773  			old: buildFile{content: `
   774  go_proto_library(
   775      name = "dep_proto",
   776      _imports = [
   777          "sub/a.proto",
   778          "sub/b.proto",
   779      ],
   780  )
   781  `},
   782  			want: `
   783  go_proto_library(
   784      name = "dep_proto",
   785      deps = ["//sub:foo_go_proto"],
   786  )
   787  `,
   788  		}, {
   789  			desc: "proto_wkt_cross_resolve",
   790  			old: buildFile{content: `
   791  go_proto_library(
   792      name = "wkts_go_proto",
   793      _imports = [
   794          "google/protobuf/any.proto",
   795          "google/protobuf/api.proto",
   796          "google/protobuf/compiler/plugin.proto",
   797          "google/protobuf/descriptor.proto",
   798          "google/protobuf/duration.proto",
   799          "google/protobuf/empty.proto",
   800          "google/protobuf/field_mask.proto",
   801          "google/protobuf/source_context.proto",
   802          "google/protobuf/struct.proto",
   803          "google/protobuf/timestamp.proto",
   804          "google/protobuf/type.proto",
   805          "google/protobuf/wrappers.proto",
   806     ],
   807  )
   808  
   809  go_library(
   810      name = "wkts_go_lib",
   811      _imports = [
   812          "github.com/golang/protobuf/ptypes/any",
   813          "google.golang.org/genproto/protobuf/api",
   814          "github.com/golang/protobuf/protoc-gen-go/descriptor",
   815          "github.com/golang/protobuf/ptypes/duration",
   816          "github.com/golang/protobuf/ptypes/empty",
   817          "google.golang.org/genproto/protobuf/field_mask",
   818          "google.golang.org/genproto/protobuf/source_context",
   819          "github.com/golang/protobuf/ptypes/struct",
   820          "github.com/golang/protobuf/ptypes/timestamp",
   821          "github.com/golang/protobuf/ptypes/wrappers",
   822          "github.com/golang/protobuf/protoc-gen-go/plugin",
   823          "google.golang.org/genproto/protobuf/ptype",
   824     ],
   825  )
   826  `},
   827  			want: `
   828  go_proto_library(name = "wkts_go_proto")
   829  
   830  go_library(
   831      name = "wkts_go_lib",
   832      deps = [
   833          "//vendor/github.com/golang/protobuf/protoc-gen-go/descriptor",
   834          "//vendor/github.com/golang/protobuf/protoc-gen-go/plugin",
   835          "//vendor/github.com/golang/protobuf/ptypes/any",
   836          "//vendor/github.com/golang/protobuf/ptypes/duration",
   837          "//vendor/github.com/golang/protobuf/ptypes/empty",
   838          "//vendor/github.com/golang/protobuf/ptypes/struct",
   839          "//vendor/github.com/golang/protobuf/ptypes/timestamp",
   840          "//vendor/github.com/golang/protobuf/ptypes/wrappers",
   841          "//vendor/google.golang.org/genproto/protobuf/api",
   842          "//vendor/google.golang.org/genproto/protobuf/field_mask",
   843          "//vendor/google.golang.org/genproto/protobuf/ptype",
   844          "//vendor/google.golang.org/genproto/protobuf/source_context",
   845      ],
   846  )
   847  `,
   848  		}, {
   849  			desc: "proto_self_import",
   850  			old: buildFile{content: `
   851  proto_library(
   852      name = "foo_proto",
   853      srcs = [
   854          "a.proto",
   855          "b.proto",
   856      ],
   857  )
   858  
   859  go_proto_library(
   860      name = "foo_go_proto",
   861      importpath = "foo",
   862      proto = ":foo_proto",
   863      _imports = ["a.proto"],
   864  )
   865  
   866  go_library(
   867      name = "go_default_library",
   868      embed = [":foo_go_proto"],
   869      importpath = "foo",
   870  )
   871  `},
   872  			want: `
   873  proto_library(
   874      name = "foo_proto",
   875      srcs = [
   876          "a.proto",
   877          "b.proto",
   878      ],
   879  )
   880  
   881  go_proto_library(
   882      name = "foo_go_proto",
   883      importpath = "foo",
   884      proto = ":foo_proto",
   885  )
   886  
   887  go_library(
   888      name = "go_default_library",
   889      embed = [":foo_go_proto"],
   890      importpath = "foo",
   891  )
   892  `,
   893  		}, {
   894  			desc: "proto_import_prefix_and_strip_import_prefix",
   895  			index: []buildFile{{
   896  				rel: "sub",
   897  				content: `
   898  proto_library(
   899      name = "foo_proto",
   900      srcs = ["bar.proto"],
   901      import_prefix = "foo/",
   902      strip_import_prefix = "/sub",
   903  )
   904  
   905  go_proto_library(
   906      name = "foo_go_proto",
   907      importpath = "example.com/foo",
   908      proto = ":foo_proto",
   909  )
   910  
   911  go_library(
   912      name = "embed",
   913      embed = [":foo_go_proto"],
   914      importpath = "example.com/foo",
   915  )
   916  `,
   917  			}},
   918  			old: buildFile{content: `
   919  go_proto_library(
   920      name = "dep_proto",
   921      _imports = ["foo/bar.proto"],
   922  )
   923  `},
   924  			want: `
   925  go_proto_library(
   926      name = "dep_proto",
   927      deps = ["//sub:embed"],
   928  )
   929  `,
   930  		},
   931  	} {
   932  		t.Run(tc.desc, func(t *testing.T) {
   933  			c, langs, cexts := testConfig(
   934  				t,
   935  				"-go_prefix=example.com/repo/resolve",
   936  				fmt.Sprintf("-go_naming_convention=%s", tc.namingConvention),
   937  				"-external=vendored", fmt.Sprintf("-index=%v", !tc.skipIndex))
   938  			mrslv := make(mapResolver)
   939  			exts := make([]interface{}, 0, len(langs))
   940  			for _, lang := range langs {
   941  				for kind := range lang.Kinds() {
   942  					mrslv[kind] = lang
   943  				}
   944  				exts = append(exts, lang)
   945  			}
   946  			ix := resolve.NewRuleIndex(mrslv.Resolver, exts...)
   947  			rc := testRemoteCache(nil)
   948  
   949  			for _, bf := range tc.index {
   950  				buildPath := filepath.Join(filepath.FromSlash(bf.rel), "BUILD.bazel")
   951  				f, err := rule.LoadData(buildPath, bf.rel, []byte(bf.content))
   952  				if err != nil {
   953  					t.Fatal(err)
   954  				}
   955  				if bf.rel == "" {
   956  					for _, cext := range cexts {
   957  						cext.Configure(c, "", f)
   958  					}
   959  				}
   960  				for _, r := range f.Rules {
   961  					ix.AddRule(c, r, f)
   962  				}
   963  			}
   964  			buildPath := filepath.Join(filepath.FromSlash(tc.old.rel), "BUILD.bazel")
   965  			f, err := rule.LoadData(buildPath, tc.old.rel, []byte(tc.old.content))
   966  			if err != nil {
   967  				t.Fatal(err)
   968  			}
   969  			imports := make([]interface{}, len(f.Rules))
   970  			for i, r := range f.Rules {
   971  				imports[i] = convertImportsAttr(r)
   972  				ix.AddRule(c, r, f)
   973  			}
   974  			ix.Finish()
   975  			for i, r := range f.Rules {
   976  				mrslv.Resolver(r, "").Resolve(c, ix, rc, r, imports[i], label.New("", tc.old.rel, r.Name()))
   977  			}
   978  			f.Sync()
   979  			got := strings.TrimSpace(string(bzl.Format(f.File)))
   980  			want := strings.TrimSpace(tc.want)
   981  			if got != want {
   982  				t.Errorf("got:\n%s\nwant:\n%s", got, want)
   983  			}
   984  		})
   985  	}
   986  }
   987  
   988  func TestResolveDisableGlobal(t *testing.T) {
   989  	c, langs, _ := testConfig(
   990  		t,
   991  		"-go_prefix=example.com/repo",
   992  		"-proto=disable_global")
   993  	exts := make([]interface{}, 0, len(langs))
   994  	for _, lang := range langs {
   995  		exts = append(exts, lang)
   996  	}
   997  	ix := resolve.NewRuleIndex(nil, exts...)
   998  	ix.Finish()
   999  	rc := testRemoteCache([]repo.Repo{
  1000  		{
  1001  			Name:     "com_github_golang_protobuf",
  1002  			GoPrefix: "github.com/golang/protobuf",
  1003  		}, {
  1004  			Name:     "org_golang_google_genproto",
  1005  			GoPrefix: "golang.org/google/genproto",
  1006  		},
  1007  	})
  1008  	gl := langs[1].(*goLang)
  1009  	oldContent := []byte(`
  1010  go_library(
  1011      name = "go_default_library",
  1012      importpath = "foo",
  1013      _imports = [
  1014          "github.com/golang/protobuf/ptypes/any",
  1015          "google.golang.org/genproto/protobuf/api",
  1016          "github.com/golang/protobuf/protoc-gen-go/descriptor",
  1017          "github.com/golang/protobuf/ptypes/duration",
  1018          "github.com/golang/protobuf/ptypes/empty",
  1019          "google.golang.org/genproto/protobuf/field_mask",
  1020          "google.golang.org/genproto/protobuf/source_context",
  1021          "github.com/golang/protobuf/ptypes/struct",
  1022          "github.com/golang/protobuf/ptypes/timestamp",
  1023          "github.com/golang/protobuf/ptypes/wrappers",
  1024          "github.com/golang/protobuf/protoc-gen-go/plugin",
  1025          "google.golang.org/genproto/protobuf/ptype",
  1026          "google.golang.org/genproto/googleapis/api/annotations",
  1027          "google.golang.org/genproto/googleapis/rpc/status",
  1028          "google.golang.org/genproto/googleapis/type/latlng",
  1029          "github.com/golang/protobuf/jsonpb",
  1030          "github.com/golang/protobuf/descriptor",
  1031          "github.com/golang/protobuf/ptypes",
  1032      ],
  1033  )
  1034  `)
  1035  	f, err := rule.LoadData("BUILD.bazel", "", oldContent)
  1036  	if err != nil {
  1037  		t.Fatal(err)
  1038  	}
  1039  	for _, r := range f.Rules {
  1040  		imports := convertImportsAttr(r)
  1041  		gl.Resolve(c, ix, rc, r, imports, label.New("", "", r.Name()))
  1042  	}
  1043  	f.Sync()
  1044  	got := strings.TrimSpace(string(bzl.Format(f.File)))
  1045  	want := strings.TrimSpace(`
  1046  go_library(
  1047      name = "go_default_library",
  1048      importpath = "foo",
  1049      deps = [
  1050          "@com_github_golang_protobuf//descriptor:go_default_library",
  1051          "@com_github_golang_protobuf//jsonpb:go_default_library",
  1052          "@com_github_golang_protobuf//protoc-gen-go/descriptor:go_default_library",
  1053          "@com_github_golang_protobuf//protoc-gen-go/plugin:go_default_library",
  1054          "@com_github_golang_protobuf//ptypes:go_default_library",
  1055          "@com_github_golang_protobuf//ptypes/any:go_default_library",
  1056          "@com_github_golang_protobuf//ptypes/duration:go_default_library",
  1057          "@com_github_golang_protobuf//ptypes/empty:go_default_library",
  1058          "@com_github_golang_protobuf//ptypes/struct:go_default_library",
  1059          "@com_github_golang_protobuf//ptypes/timestamp:go_default_library",
  1060          "@com_github_golang_protobuf//ptypes/wrappers:go_default_library",
  1061          "@org_golang_google_genproto//googleapis/api/annotations:go_default_library",
  1062          "@org_golang_google_genproto//googleapis/rpc/status:go_default_library",
  1063          "@org_golang_google_genproto//googleapis/type/latlng:go_default_library",
  1064          "@org_golang_google_genproto//protobuf/api:go_default_library",
  1065          "@org_golang_google_genproto//protobuf/field_mask:go_default_library",
  1066          "@org_golang_google_genproto//protobuf/ptype:go_default_library",
  1067          "@org_golang_google_genproto//protobuf/source_context:go_default_library",
  1068      ],
  1069  )
  1070  `)
  1071  	if got != want {
  1072  		t.Errorf("got:\n%s\nwant:%s", got, want)
  1073  	}
  1074  }
  1075  
  1076  func TestResolveExternal(t *testing.T) {
  1077  	c, langs, _ := testConfig(
  1078  		t,
  1079  		"-go_prefix=example.com/local")
  1080  	gc := getGoConfig(c)
  1081  	ix := resolve.NewRuleIndex(nil)
  1082  	ix.Finish()
  1083  	gl := langs[1].(*goLang)
  1084  	for _, tc := range []struct {
  1085  		desc, importpath         string
  1086  		repos                    []repo.Repo
  1087  		moduleMode               bool
  1088  		depMode                  dependencyMode
  1089  		namingConvention         namingConvention
  1090  		namingConventionExternal namingConvention
  1091  		repoNamingConvention     map[string]namingConvention
  1092  		want                     string
  1093  	}{
  1094  		{
  1095  			desc:       "top",
  1096  			importpath: "example.com/repo",
  1097  			want:       "@com_example_repo//:go_default_library",
  1098  		}, {
  1099  			desc:             "top_import_naming_convention",
  1100  			namingConvention: goDefaultLibraryNamingConvention,
  1101  			repoNamingConvention: map[string]namingConvention{
  1102  				"com_example_repo": importNamingConvention,
  1103  			},
  1104  			importpath: "example.com/repo",
  1105  			want:       "@com_example_repo//:repo",
  1106  		}, {
  1107  			desc:             "top_import_alias_naming_convention",
  1108  			namingConvention: goDefaultLibraryNamingConvention,
  1109  			repoNamingConvention: map[string]namingConvention{
  1110  				"com_example_repo": importAliasNamingConvention,
  1111  			},
  1112  			importpath: "example.com/repo",
  1113  			want:       "@com_example_repo//:go_default_library",
  1114  		}, {
  1115  			desc:       "sub",
  1116  			importpath: "example.com/repo/lib",
  1117  			want:       "@com_example_repo//lib:go_default_library",
  1118  		}, {
  1119  			desc:             "sub_import_alias_naming_convention",
  1120  			namingConvention: importNamingConvention,
  1121  			repoNamingConvention: map[string]namingConvention{
  1122  				"com_example_repo": importAliasNamingConvention,
  1123  			},
  1124  			importpath: "example.com/repo/lib",
  1125  			want:       "@com_example_repo//lib",
  1126  		}, {
  1127  			desc: "custom_repo",
  1128  			repos: []repo.Repo{{
  1129  				Name:     "custom_repo_name",
  1130  				GoPrefix: "example.com/repo",
  1131  			}},
  1132  			importpath: "example.com/repo/lib",
  1133  			want:       "@custom_repo_name//lib:go_default_library",
  1134  		}, {
  1135  			desc: "custom_repo_import_naming_convention",
  1136  			repos: []repo.Repo{{
  1137  				Name:     "custom_repo_name",
  1138  				GoPrefix: "example.com/repo",
  1139  			}},
  1140  			repoNamingConvention: map[string]namingConvention{
  1141  				"custom_repo_name": importNamingConvention,
  1142  			},
  1143  			importpath: "example.com/repo/lib",
  1144  			want:       "@custom_repo_name//lib",
  1145  		}, {
  1146  			desc: "custom_repo_naming_convention_extern_import",
  1147  			repos: []repo.Repo{{
  1148  				Name:     "custom_repo_name",
  1149  				GoPrefix: "example.com/repo",
  1150  			}},
  1151  			namingConventionExternal: importNamingConvention,
  1152  			importpath:               "example.com/repo/lib",
  1153  			want:                     "@custom_repo_name//lib",
  1154  		}, {
  1155  			desc: "custom_repo_naming_convention_extern_default",
  1156  			repos: []repo.Repo{{
  1157  				Name:     "custom_repo_name",
  1158  				GoPrefix: "example.com/repo",
  1159  			}},
  1160  			namingConventionExternal: goDefaultLibraryNamingConvention,
  1161  			importpath:               "example.com/repo/lib",
  1162  			want:                     "@custom_repo_name//lib:go_default_library",
  1163  		}, {
  1164  			desc:       "qualified",
  1165  			importpath: "example.com/repo.git/lib",
  1166  			want:       "@com_example_repo_git//lib:go_default_library",
  1167  		}, {
  1168  			desc:       "domain",
  1169  			importpath: "example.com/lib",
  1170  			want:       "@com_example//lib:go_default_library",
  1171  		}, {
  1172  			desc:       "same_prefix",
  1173  			importpath: "example.com/local/ext",
  1174  			repos: []repo.Repo{{
  1175  				Name:     "local_ext",
  1176  				GoPrefix: "example.com/local/ext",
  1177  			}},
  1178  			want: "@local_ext//:go_default_library",
  1179  		}, {
  1180  			desc:       "module_mode_unknown",
  1181  			importpath: "example.com/repo/v2/foo",
  1182  			moduleMode: true,
  1183  			want:       "@com_example_repo_v2//foo:go_default_library",
  1184  		}, {
  1185  			desc:       "module_mode_known",
  1186  			importpath: "example.com/repo/v2/foo",
  1187  			repos: []repo.Repo{{
  1188  				Name:     "custom_repo",
  1189  				GoPrefix: "example.com/repo",
  1190  			}},
  1191  			moduleMode: true,
  1192  			want:       "@custom_repo//v2/foo:go_default_library",
  1193  		}, {
  1194  			desc:       "min_module_compat",
  1195  			importpath: "example.com/foo",
  1196  			repos: []repo.Repo{{
  1197  				Name:     "com_example_foo_v2",
  1198  				GoPrefix: "example.com/foo/v2",
  1199  			}},
  1200  			moduleMode: true,
  1201  			want:       "@com_example_foo_v2//:go_default_library",
  1202  		}, {
  1203  			desc:       "min_module_compat_both",
  1204  			importpath: "example.com/foo",
  1205  			repos: []repo.Repo{
  1206  				{
  1207  					Name:     "com_example_foo",
  1208  					GoPrefix: "example.com/foo",
  1209  				}, {
  1210  					Name:     "com_example_foo_v2",
  1211  					GoPrefix: "example.com/foo/v2",
  1212  				},
  1213  			},
  1214  			moduleMode: true,
  1215  			want:       "@com_example_foo//:go_default_library",
  1216  		}, {
  1217  			desc:       "static_mode_known",
  1218  			importpath: "example.com/repo/v2/foo",
  1219  			repos: []repo.Repo{{
  1220  				Name:     "custom_repo",
  1221  				GoPrefix: "example.com/repo",
  1222  			}},
  1223  			depMode: staticMode,
  1224  			want:    "@custom_repo//v2/foo:go_default_library",
  1225  		}, {
  1226  			desc:       "static_mode_unknown",
  1227  			importpath: "example.com/repo/v2/foo",
  1228  			depMode:    staticMode,
  1229  			want:       "",
  1230  		},
  1231  	} {
  1232  		t.Run(tc.desc, func(t *testing.T) {
  1233  			gc.depMode = tc.depMode
  1234  			gc.moduleMode = tc.moduleMode
  1235  			gc.goNamingConvention = tc.namingConvention
  1236  			gc.goNamingConventionExternal = tc.namingConventionExternal
  1237  			gc.repoNamingConvention = tc.repoNamingConvention
  1238  			rc := testRemoteCache(tc.repos)
  1239  			r := rule.NewRule("go_library", "x")
  1240  			imports := rule.PlatformStrings{Generic: []string{tc.importpath}}
  1241  			gl.Resolve(c, ix, rc, r, imports, label.New("", "", "x"))
  1242  			deps := r.AttrStrings("deps")
  1243  			if tc.want == "" {
  1244  				if len(deps) != 0 {
  1245  					t.Fatalf("deps: got %d; want 0", len(deps))
  1246  				}
  1247  				return
  1248  			} else if len(deps) != 1 {
  1249  				t.Fatalf("deps: got %d; want 1", len(deps))
  1250  			}
  1251  			if deps[0] != tc.want {
  1252  				t.Errorf("got %s; want %s", deps[0], tc.want)
  1253  			}
  1254  		})
  1255  	}
  1256  }
  1257  
  1258  func testRemoteCache(knownRepos []repo.Repo) *repo.RemoteCache {
  1259  	rc, _ := repo.NewRemoteCache(knownRepos)
  1260  	rc.RepoRootForImportPath = stubRepoRootForImportPath
  1261  	rc.HeadCmd = func(_, _ string) (string, error) {
  1262  		return "", fmt.Errorf("HeadCmd not supported in test")
  1263  	}
  1264  	rc.ModInfo = stubModInfo
  1265  	return rc
  1266  }
  1267  
  1268  // stubRepoRootForImportPath is a stub implementation of vcs.RepoRootForImportPath
  1269  func stubRepoRootForImportPath(importPath string, verbose bool) (*vcs.RepoRoot, error) {
  1270  	if pathtools.HasPrefix(importPath, "example.com/repo.git") {
  1271  		return &vcs.RepoRoot{
  1272  			VCS:  vcs.ByCmd("git"),
  1273  			Repo: "https://example.com/repo.git",
  1274  			Root: "example.com/repo.git",
  1275  		}, nil
  1276  	}
  1277  
  1278  	if pathtools.HasPrefix(importPath, "example.com/repo") {
  1279  		return &vcs.RepoRoot{
  1280  			VCS:  vcs.ByCmd("git"),
  1281  			Repo: "https://example.com/repo.git",
  1282  			Root: "example.com/repo",
  1283  		}, nil
  1284  	}
  1285  
  1286  	if pathtools.HasPrefix(importPath, "example.com") {
  1287  		return &vcs.RepoRoot{
  1288  			VCS:  vcs.ByCmd("git"),
  1289  			Repo: "https://example.com",
  1290  			Root: "example.com",
  1291  		}, nil
  1292  	}
  1293  
  1294  	return nil, fmt.Errorf("could not resolve import path: %q", importPath)
  1295  }
  1296  
  1297  // stubModInfo is a stub implementation of RemoteCache.ModInfo.
  1298  func stubModInfo(importPath string) (string, error) {
  1299  	if pathtools.HasPrefix(importPath, "example.com/repo/v2") {
  1300  		return "example.com/repo/v2", nil
  1301  	}
  1302  	if pathtools.HasPrefix(importPath, "example.com/repo") {
  1303  		return "example.com/repo", nil
  1304  	}
  1305  	return "", fmt.Errorf("could not find module for import path: %q", importPath)
  1306  }
  1307  
  1308  func convertImportsAttr(r *rule.Rule) interface{} {
  1309  	kind := r.Kind()
  1310  	value := r.AttrStrings("_imports")
  1311  	r.DelAttr("_imports")
  1312  	if _, ok := goKinds[kind]; ok {
  1313  		return rule.PlatformStrings{Generic: value}
  1314  	} else {
  1315  		// proto_library
  1316  		return value
  1317  	}
  1318  }
  1319  
  1320  type mapResolver map[string]resolve.Resolver
  1321  
  1322  func (mr mapResolver) Resolver(r *rule.Rule, f string) resolve.Resolver {
  1323  	return mr[r.Kind()]
  1324  }