github.com/afking/bazel-gazelle@v0.0.0-20180301150245-c02bc0f529e8/cmd/gazelle/integration_test.go (about)

     1  /* Copyright 2017 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  // This file contains integration tests for all of Gazelle. It's meant to test
    17  // common usage patterns and check for errors that are difficult to test in
    18  // unit tests.
    19  
    20  package main
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  	"testing"
    28  
    29  	"github.com/bazelbuild/bazel-gazelle/internal/config"
    30  	"github.com/bazelbuild/bazel-gazelle/internal/wspace"
    31  )
    32  
    33  type fileSpec struct {
    34  	path, content string
    35  }
    36  
    37  func createFiles(files []fileSpec) (string, error) {
    38  	dir, err := ioutil.TempDir(os.Getenv("TEST_TEMPDIR"), "integration_test")
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  
    43  	for _, f := range files {
    44  		path := filepath.Join(dir, filepath.FromSlash(f.path))
    45  		if strings.HasSuffix(f.path, "/") {
    46  			if err := os.MkdirAll(path, 0700); err != nil {
    47  				os.RemoveAll(dir)
    48  				return "", err
    49  			}
    50  			continue
    51  		}
    52  		if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
    53  			os.RemoveAll(dir)
    54  			return "", err
    55  		}
    56  		if err := ioutil.WriteFile(path, []byte(f.content), 0600); err != nil {
    57  			os.RemoveAll(dir)
    58  			return "", err
    59  		}
    60  	}
    61  	return dir, nil
    62  }
    63  
    64  // skipIfWorkspaceVisible skips the test if the WORKSPACE file for the
    65  // repository is visible. This happens in newer Bazel versions when tests
    66  // are run without sandboxing, since temp directories may be inside the
    67  // exec root.
    68  func skipIfWorkspaceVisible(t *testing.T, dir string) {
    69  	if parent, err := wspace.Find(dir); err == nil {
    70  		t.Skipf("WORKSPACE visible in parent %q of tmp %q", parent, dir)
    71  	}
    72  }
    73  
    74  func checkFiles(t *testing.T, dir string, files []fileSpec) {
    75  	for _, f := range files {
    76  		path := filepath.Join(dir, f.path)
    77  		if strings.HasSuffix(f.path, "/") {
    78  			if st, err := os.Stat(path); err != nil {
    79  				t.Errorf("could not stat %s: %v", f.path, err)
    80  			} else if !st.IsDir() {
    81  				t.Errorf("not a directory: %s", f.path)
    82  			}
    83  		} else {
    84  			want := f.content
    85  			if len(want) > 0 && want[0] == '\n' {
    86  				// Strip leading newline, added for readability.
    87  				want = want[1:]
    88  			}
    89  			gotBytes, err := ioutil.ReadFile(filepath.Join(dir, f.path))
    90  			if err != nil {
    91  				t.Errorf("could not read %s: %v", f.path, err)
    92  				continue
    93  			}
    94  			got := string(gotBytes)
    95  			if got != want {
    96  				t.Errorf("%s: got %s ; want %s", f.path, got, f.content)
    97  			}
    98  		}
    99  	}
   100  }
   101  
   102  func runGazelle(wd string, args []string) error {
   103  	oldWd, err := os.Getwd()
   104  	if err != nil {
   105  		return err
   106  	}
   107  	if err := os.Chdir(wd); err != nil {
   108  		return err
   109  	}
   110  	defer os.Chdir(oldWd)
   111  
   112  	return run(args)
   113  }
   114  
   115  func TestNoRepoRootOrWorkspace(t *testing.T) {
   116  	dir, err := createFiles(nil)
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  	defer os.RemoveAll(dir)
   121  	skipIfWorkspaceVisible(t, dir)
   122  	want := "-repo_root not specified"
   123  	if err := runGazelle(dir, nil); err == nil {
   124  		t.Fatalf("got success; want %q", want)
   125  	} else if !strings.Contains(err.Error(), want) {
   126  		t.Fatalf("got %q; want %q", err, want)
   127  	}
   128  }
   129  
   130  func TestNoGoPrefixArgOrRule(t *testing.T) {
   131  	dir, err := createFiles([]fileSpec{
   132  		{path: "WORKSPACE", content: ""},
   133  	})
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  	want := "-go_prefix not set"
   138  	if err := runGazelle(dir, nil); err == nil {
   139  		t.Fatalf("got success; want %q", want)
   140  	} else if !strings.Contains(err.Error(), want) {
   141  		t.Fatalf("got %q; want %q", err, want)
   142  	}
   143  }
   144  
   145  // TestSelectLabelsSorted checks that string lists in srcs and deps are sorted
   146  // using buildifier order, even if they are inside select expressions.
   147  // This applies to both new and existing lists and should preserve comments.
   148  // buildifier does not do this yet bazelbuild/buildtools#122, so we do this
   149  // in addition to calling build.Rewrite.
   150  func TestSelectLabelsSorted(t *testing.T) {
   151  	dir, err := createFiles([]fileSpec{
   152  		{path: "WORKSPACE"},
   153  		{
   154  			path: "BUILD",
   155  			content: `
   156  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   157  
   158  go_library(
   159      name = "go_default_library",
   160      srcs = select({
   161          "@io_bazel_rules_go//go/platform:linux": [
   162  						# top comment
   163  						"foo.go",  # side comment
   164  						# bar comment
   165  						"bar.go",
   166          ],
   167          "//conditions:default": [],
   168      }),
   169      importpath = "example.com/foo",
   170  )
   171  `,
   172  		},
   173  		{
   174  			path: "foo.go",
   175  			content: `
   176  // +build linux
   177  
   178  package foo
   179  
   180  import (
   181      _ "example.com/foo/outer"
   182      _ "example.com/foo/outer/inner"
   183      _ "github.com/jr_hacker/tools"
   184  )
   185  `,
   186  		},
   187  		{
   188  			path: "bar.go",
   189  			content: `// +build linux
   190  
   191  package foo
   192  `,
   193  		},
   194  		{path: "outer/outer.go", content: "package outer"},
   195  		{path: "outer/inner/inner.go", content: "package inner"},
   196  	})
   197  	want := `load("@io_bazel_rules_go//go:def.bzl", "go_library")
   198  
   199  go_library(
   200      name = "go_default_library",
   201      srcs = select({
   202          "@io_bazel_rules_go//go/platform:linux": [
   203              # top comment
   204              # bar comment
   205              "bar.go",
   206              "foo.go",  # side comment
   207          ],
   208          "//conditions:default": [],
   209      }),
   210      importpath = "example.com/foo",
   211      visibility = ["//visibility:public"],
   212      deps = select({
   213          "@io_bazel_rules_go//go/platform:linux": [
   214              "//outer:go_default_library",
   215              "//outer/inner:go_default_library",
   216              "@com_github_jr_hacker_tools//:go_default_library",
   217          ],
   218          "//conditions:default": [],
   219      }),
   220  )
   221  `
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	if err := runGazelle(dir, []string{"-go_prefix", "example.com/foo"}); err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	if got, err := ioutil.ReadFile(filepath.Join(dir, "BUILD")); err != nil {
   230  		t.Fatal(err)
   231  	} else if string(got) != want {
   232  		t.Fatalf("got %s ; want %s", string(got), want)
   233  	}
   234  }
   235  
   236  func TestFixAndUpdateChanges(t *testing.T) {
   237  	files := []fileSpec{
   238  		{path: "WORKSPACE"},
   239  		{
   240  			path: "BUILD",
   241  			content: `load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_prefix")
   242  load("@io_bazel_rules_go//go:def.bzl", "cgo_library", "go_test")
   243  
   244  go_prefix("example.com/foo")
   245  
   246  go_library(
   247      name = "go_default_library",
   248      srcs = [
   249          "extra.go",
   250          "pure.go",
   251      ],
   252      library = ":cgo_default_library",
   253      visibility = ["//visibility:default"],
   254  )
   255  
   256  cgo_library(
   257      name = "cgo_default_library",
   258      srcs = ["cgo.go"],
   259  )
   260  `,
   261  		},
   262  		{
   263  			path:    "pure.go",
   264  			content: "package foo",
   265  		},
   266  		{
   267  			path: "cgo.go",
   268  			content: `package foo
   269  
   270  import "C"
   271  `,
   272  		},
   273  	}
   274  
   275  	cases := []struct {
   276  		cmd, want string
   277  	}{
   278  		{
   279  			cmd: "update",
   280  			want: `load("@io_bazel_rules_go//go:def.bzl", "cgo_library", "go_library", "go_prefix")
   281  
   282  go_prefix("example.com/foo")
   283  
   284  go_library(
   285      name = "go_default_library",
   286      srcs = [
   287          "cgo.go",
   288          "pure.go",
   289      ],
   290      cgo = True,
   291      importpath = "example.com/foo",
   292      visibility = ["//visibility:default"],
   293  )
   294  
   295  cgo_library(
   296      name = "cgo_default_library",
   297      srcs = ["cgo.go"],
   298  )
   299  `,
   300  		}, {
   301  			cmd: "fix",
   302  			want: `load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_prefix")
   303  
   304  go_prefix("example.com/foo")
   305  
   306  go_library(
   307      name = "go_default_library",
   308      srcs = [
   309          "cgo.go",
   310          "pure.go",
   311      ],
   312      cgo = True,
   313      importpath = "example.com/foo",
   314      visibility = ["//visibility:default"],
   315  )
   316  `,
   317  		},
   318  	}
   319  
   320  	for _, c := range cases {
   321  		t.Run(c.cmd, func(t *testing.T) {
   322  			dir, err := createFiles(files)
   323  			if err != nil {
   324  				t.Fatal(err)
   325  			}
   326  
   327  			if err := runGazelle(dir, []string{c.cmd}); err != nil {
   328  				t.Fatal(err)
   329  			}
   330  			if got, err := ioutil.ReadFile(filepath.Join(dir, "BUILD")); err != nil {
   331  				t.Fatal(err)
   332  			} else if string(got) != c.want {
   333  				t.Fatalf("got %s ; want %s", string(got), c.want)
   334  			}
   335  		})
   336  	}
   337  }
   338  
   339  func TestFixUnlinkedCgoLibrary(t *testing.T) {
   340  	files := []fileSpec{
   341  		{path: "WORKSPACE"},
   342  		{
   343  			path: "BUILD",
   344  			content: `load("@io_bazel_rules_go//go:def.bzl", "cgo_library", "go_library")
   345  
   346  cgo_library(
   347      name = "cgo_default_library",
   348      srcs = ["cgo.go"],
   349  )
   350  
   351  go_library(
   352      name = "go_default_library",
   353      srcs = ["pure.go"],
   354      importpath = "example.com/foo",
   355      visibility = ["//visibility:public"],
   356  )
   357  `,
   358  		}, {
   359  			path:    "pure.go",
   360  			content: "package foo",
   361  		},
   362  	}
   363  
   364  	dir, err := createFiles(files)
   365  	if err != nil {
   366  		t.Fatal(err)
   367  	}
   368  
   369  	want := `load("@io_bazel_rules_go//go:def.bzl", "go_library")
   370  
   371  go_library(
   372      name = "go_default_library",
   373      srcs = ["pure.go"],
   374      importpath = "example.com/foo",
   375      visibility = ["//visibility:public"],
   376  )
   377  `
   378  	if err := runGazelle(dir, []string{"fix", "-go_prefix", "example.com/foo"}); err != nil {
   379  		t.Fatal(err)
   380  	}
   381  	if got, err := ioutil.ReadFile(filepath.Join(dir, "BUILD")); err != nil {
   382  		t.Fatal(err)
   383  	} else if string(got) != want {
   384  		t.Fatalf("got %s ; want %s", string(got), want)
   385  	}
   386  }
   387  
   388  // TestMultipleDirectories checks that all directories in a repository are
   389  // indexed but only directories listed on the command line are updated.
   390  func TestMultipleDirectories(t *testing.T) {
   391  	files := []fileSpec{
   392  		{path: "WORKSPACE"},
   393  		{
   394  			path: "a/BUILD.bazel",
   395  			content: `# This file shouldn't be modified.
   396  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   397  
   398  go_library(
   399      name = "go_default_library",
   400      srcs = ["a.go"],
   401      importpath = "example.com/foo/x",
   402  )
   403  `,
   404  		}, {
   405  			path:    "a/a.go",
   406  			content: "package a",
   407  		}, {
   408  			path: "b/b.go",
   409  			content: `
   410  package b
   411  
   412  import _ "example.com/foo/x"
   413  `,
   414  		},
   415  	}
   416  	dir, err := createFiles(files)
   417  	if err != nil {
   418  		t.Fatal(err)
   419  	}
   420  	defer os.RemoveAll(dir)
   421  
   422  	args := []string{"-go_prefix", "example.com/foo", "b"}
   423  	if err := runGazelle(dir, args); err != nil {
   424  		t.Fatal(err)
   425  	}
   426  
   427  	checkFiles(t, dir, []fileSpec{
   428  		files[1], // should not change
   429  		{
   430  			path: "b/BUILD.bazel",
   431  			content: `load("@io_bazel_rules_go//go:def.bzl", "go_library")
   432  
   433  go_library(
   434      name = "go_default_library",
   435      srcs = ["b.go"],
   436      importpath = "example.com/foo/b",
   437      visibility = ["//visibility:public"],
   438      deps = ["//a:go_default_library"],
   439  )
   440  `,
   441  		},
   442  	})
   443  }
   444  
   445  func TestErrorOutsideWorkspace(t *testing.T) {
   446  	files := []fileSpec{
   447  		{path: "a/"},
   448  		{path: "b/"},
   449  	}
   450  	dir, err := createFiles(files)
   451  	if err != nil {
   452  		t.Fatal(err)
   453  	}
   454  	defer os.RemoveAll(dir)
   455  	skipIfWorkspaceVisible(t, dir)
   456  
   457  	cases := []struct {
   458  		name, dir, want string
   459  		args            []string
   460  	}{
   461  		{
   462  			name: "outside workspace",
   463  			dir:  dir,
   464  			args: nil,
   465  			want: "WORKSPACE cannot be found",
   466  		}, {
   467  			name: "outside repo_root",
   468  			dir:  filepath.Join(dir, "a"),
   469  			args: []string{"-repo_root", filepath.Join(dir, "b")},
   470  			want: "not a subdirectory of repo root",
   471  		},
   472  	}
   473  	for _, c := range cases {
   474  		t.Run(c.name, func(t *testing.T) {
   475  			if err := runGazelle(c.dir, c.args); err == nil {
   476  				t.Fatal("got success; want %q", c.want)
   477  			} else if !strings.Contains(err.Error(), c.want) {
   478  				t.Fatal("got %q; want %q", err, c.want)
   479  			}
   480  		})
   481  	}
   482  }
   483  
   484  func TestBuildFileNameIgnoresBuild(t *testing.T) {
   485  	files := []fileSpec{
   486  		{path: "WORKSPACE"},
   487  		{path: "BUILD/"},
   488  		{
   489  			path:    "a/BUILD",
   490  			content: "!!! parse error",
   491  		}, {
   492  			path:    "a.go",
   493  			content: "package a",
   494  		},
   495  	}
   496  	dir, err := createFiles(files)
   497  	if err != nil {
   498  		t.Fatal(err)
   499  	}
   500  	defer os.Remove(dir)
   501  
   502  	args := []string{"-go_prefix", "example.com/foo", "-build_file_name", "BUILD.bazel"}
   503  	if err := runGazelle(dir, args); err != nil {
   504  		t.Fatal(err)
   505  	}
   506  	if _, err := os.Stat(filepath.Join(dir, "BUILD.bazel")); err != nil {
   507  		t.Errorf("BUILD.bazel not created: %v", err)
   508  	}
   509  }
   510  
   511  func TestExternalVendor(t *testing.T) {
   512  	files := []fileSpec{
   513  		{path: "WORKSPACE"},
   514  		{
   515  			path: "a.go",
   516  			content: `package foo
   517  
   518  import _ "golang.org/x/bar"
   519  `,
   520  		}, {
   521  			path: "vendor/golang.org/x/bar/bar.go",
   522  			content: `package bar
   523  
   524  import _ "golang.org/x/baz"
   525  `,
   526  		}, {
   527  			path:    "vendor/golang.org/x/baz/baz.go",
   528  			content: "package baz",
   529  		},
   530  	}
   531  	dir, err := createFiles(files)
   532  	if err != nil {
   533  		t.Fatal(err)
   534  	}
   535  	defer os.RemoveAll(dir)
   536  
   537  	args := []string{"-go_prefix", "example.com/foo", "-external", "vendored"}
   538  	if err := runGazelle(dir, args); err != nil {
   539  		t.Fatal(err)
   540  	}
   541  
   542  	checkFiles(t, dir, []fileSpec{
   543  		{
   544  			path: config.DefaultValidBuildFileNames[0],
   545  			content: `load("@io_bazel_rules_go//go:def.bzl", "go_library")
   546  
   547  go_library(
   548      name = "go_default_library",
   549      srcs = ["a.go"],
   550      importpath = "example.com/foo",
   551      visibility = ["//visibility:public"],
   552      deps = ["//vendor/golang.org/x/bar:go_default_library"],
   553  )
   554  `,
   555  		}, {
   556  			path: "vendor/golang.org/x/bar/" + config.DefaultValidBuildFileNames[0],
   557  			content: `load("@io_bazel_rules_go//go:def.bzl", "go_library")
   558  
   559  go_library(
   560      name = "go_default_library",
   561      srcs = ["bar.go"],
   562      importpath = "golang.org/x/bar",
   563      visibility = ["//visibility:public"],
   564      deps = ["//vendor/golang.org/x/baz:go_default_library"],
   565  )
   566  `,
   567  		},
   568  	})
   569  }
   570  
   571  func TestMigrateProtoRules(t *testing.T) {
   572  	files := []fileSpec{
   573  		{path: "WORKSPACE"},
   574  		{
   575  			path: config.DefaultValidBuildFileNames[0],
   576  			content: `
   577  load("@io_bazel_rules_go//proto:go_proto_library.bzl", "go_proto_library")
   578  
   579  filegroup(
   580      name = "go_default_library_protos",
   581      srcs = ["foo.proto"],
   582      visibility = ["//visibility:public"],
   583  )
   584  
   585  go_proto_library(
   586      name = "go_default_library",
   587      srcs = [":go_default_library_protos"],
   588  )
   589  `,
   590  		}, {
   591  			path: "foo.proto",
   592  			content: `syntax = "proto3";
   593  
   594  option go_package = "example.com/repo";
   595  `,
   596  		}, {
   597  			path:    "foo.pb.go",
   598  			content: `package repo`,
   599  		},
   600  	}
   601  
   602  	for _, tc := range []struct {
   603  		args []string
   604  		want string
   605  	}{
   606  		{
   607  			args: []string{"update", "-go_prefix", "example.com/repo"},
   608  			want: `
   609  load("@io_bazel_rules_go//proto:go_proto_library.bzl", "go_proto_library")
   610  
   611  filegroup(
   612      name = "go_default_library_protos",
   613      srcs = ["foo.proto"],
   614      visibility = ["//visibility:public"],
   615  )
   616  
   617  go_proto_library(
   618      name = "go_default_library",
   619      srcs = [":go_default_library_protos"],
   620  )
   621  `,
   622  		}, {
   623  			args: []string{"fix", "-go_prefix", "example.com/repo"},
   624  			want: `
   625  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   626  load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
   627  
   628  proto_library(
   629      name = "repo_proto",
   630      srcs = ["foo.proto"],
   631      visibility = ["//visibility:public"],
   632  )
   633  
   634  go_proto_library(
   635      name = "repo_go_proto",
   636      importpath = "example.com/repo",
   637      proto = ":repo_proto",
   638      visibility = ["//visibility:public"],
   639  )
   640  
   641  go_library(
   642      name = "go_default_library",
   643      embed = [":repo_go_proto"],
   644      importpath = "example.com/repo",
   645      visibility = ["//visibility:public"],
   646  )
   647  `,
   648  		},
   649  	} {
   650  		t.Run(tc.args[0], func(t *testing.T) {
   651  			dir, err := createFiles(files)
   652  			if err != nil {
   653  				t.Fatal(err)
   654  			}
   655  			defer os.RemoveAll(dir)
   656  
   657  			if err := runGazelle(dir, tc.args); err != nil {
   658  				t.Fatal(err)
   659  			}
   660  
   661  			checkFiles(t, dir, []fileSpec{{
   662  				path:    config.DefaultValidBuildFileNames[0],
   663  				content: tc.want,
   664  			}})
   665  		})
   666  	}
   667  }
   668  
   669  func TestRemoveProtoDeletesRules(t *testing.T) {
   670  	files := []fileSpec{
   671  		{path: "WORKSPACE"},
   672  		{
   673  			path: config.DefaultValidBuildFileNames[0],
   674  			content: `
   675  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   676  load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
   677  
   678  filegroup(
   679      name = "go_default_library_protos",
   680      srcs = ["foo.proto"],
   681      visibility = ["//visibility:public"],
   682  )
   683  
   684  proto_library(
   685      name = "repo_proto",
   686      srcs = ["foo.proto"],
   687      visibility = ["//visibility:public"],
   688  )
   689  
   690  go_proto_library(
   691      name = "repo_go_proto",
   692      importpath = "example.com/repo",
   693      proto = ":repo_proto",
   694      visibility = ["//visibility:public"],
   695  )
   696  
   697  go_library(
   698      name = "go_default_library",
   699      srcs = ["extra.go"],
   700      embed = [":repo_go_proto"],
   701      importpath = "example.com/repo",
   702      visibility = ["//visibility:public"],
   703  )
   704  `,
   705  		}, {
   706  			path:    "extra.go",
   707  			content: `package repo`,
   708  		},
   709  	}
   710  	dir, err := createFiles(files)
   711  	if err != nil {
   712  		t.Fatal(err)
   713  	}
   714  	defer os.RemoveAll(dir)
   715  
   716  	args := []string{"fix", "-go_prefix", "example.com/repo"}
   717  	if err := runGazelle(dir, args); err != nil {
   718  		t.Fatal(err)
   719  	}
   720  
   721  	checkFiles(t, dir, []fileSpec{{
   722  		path: config.DefaultValidBuildFileNames[0],
   723  		content: `
   724  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   725  
   726  go_library(
   727      name = "go_default_library",
   728      srcs = ["extra.go"],
   729      importpath = "example.com/repo",
   730      visibility = ["//visibility:public"],
   731  )
   732  `,
   733  	}})
   734  }
   735  
   736  func TestAddServiceConvertsToGrpc(t *testing.T) {
   737  	files := []fileSpec{
   738  		{path: "WORKSPACE"},
   739  		{
   740  			path: config.DefaultValidBuildFileNames[0],
   741  			content: `
   742  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   743  load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
   744  
   745  proto_library(
   746      name = "repo_proto",
   747      srcs = ["foo.proto"],
   748      visibility = ["//visibility:public"],
   749  )
   750  
   751  go_proto_library(
   752      name = "repo_go_proto",
   753      importpath = "example.com/repo",
   754      proto = ":repo_proto",
   755      visibility = ["//visibility:public"],
   756  )
   757  
   758  go_library(
   759      name = "go_default_library",
   760      embed = [":repo_go_proto"],
   761      importpath = "example.com/repo",
   762      visibility = ["//visibility:public"],
   763  )
   764  `,
   765  		}, {
   766  			path: "foo.proto",
   767  			content: `syntax = "proto3";
   768  
   769  option go_package = "example.com/repo";
   770  
   771  service {}
   772  `,
   773  		},
   774  	}
   775  
   776  	dir, err := createFiles(files)
   777  	if err != nil {
   778  		t.Fatal(err)
   779  	}
   780  	defer os.RemoveAll(dir)
   781  
   782  	args := []string{"-go_prefix", "example.com/repo"}
   783  	if err := runGazelle(dir, args); err != nil {
   784  		t.Fatal(err)
   785  	}
   786  
   787  	checkFiles(t, dir, []fileSpec{{
   788  		path: config.DefaultValidBuildFileNames[0],
   789  		content: `
   790  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   791  load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
   792  
   793  proto_library(
   794      name = "repo_proto",
   795      srcs = ["foo.proto"],
   796      visibility = ["//visibility:public"],
   797  )
   798  
   799  go_proto_library(
   800      name = "repo_go_proto",
   801      compilers = ["@io_bazel_rules_go//proto:go_grpc"],
   802      importpath = "example.com/repo",
   803      proto = ":repo_proto",
   804      visibility = ["//visibility:public"],
   805  )
   806  
   807  go_library(
   808      name = "go_default_library",
   809      embed = [":repo_go_proto"],
   810      importpath = "example.com/repo",
   811      visibility = ["//visibility:public"],
   812  )
   813  `,
   814  	}})
   815  }
   816  
   817  func TestEmptyGoPrefix(t *testing.T) {
   818  	files := []fileSpec{
   819  		{path: "WORKSPACE"},
   820  		{
   821  			path:    "foo/foo.go",
   822  			content: "package foo",
   823  		}, {
   824  			path: "bar/bar.go",
   825  			content: `
   826  package bar
   827  
   828  import (
   829  	_ "fmt"
   830  	_ "foo"
   831  )
   832  `,
   833  		},
   834  	}
   835  
   836  	dir, err := createFiles(files)
   837  	if err != nil {
   838  		t.Fatal(err)
   839  	}
   840  	defer os.RemoveAll(dir)
   841  
   842  	args := []string{"-go_prefix", ""}
   843  	if err := runGazelle(dir, args); err != nil {
   844  		t.Fatal(err)
   845  	}
   846  
   847  	checkFiles(t, dir, []fileSpec{{
   848  		path: filepath.Join("bar", config.DefaultValidBuildFileNames[0]),
   849  		content: `
   850  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   851  
   852  go_library(
   853      name = "go_default_library",
   854      srcs = ["bar.go"],
   855      importpath = "bar",
   856      visibility = ["//visibility:public"],
   857      deps = ["//foo:go_default_library"],
   858  )
   859  `,
   860  	}})
   861  }
   862  
   863  // TestResolveKeptImportpath checks that Gazelle can resolve dependencies
   864  // against a library with a '# keep' comment on its importpath attribute
   865  // when the importpath doesn't match what Gazelle would infer.
   866  func TestResolveKeptImportpath(t *testing.T) {
   867  	files := []fileSpec{
   868  		{path: "WORKSPACE"},
   869  		{
   870  			path: "foo/foo.go",
   871  			content: `
   872  package foo
   873  
   874  import _ "example.com/alt/baz"
   875  `,
   876  		}, {
   877  			path: "bar/BUILD.bazel",
   878  			content: `load("@io_bazel_rules_go//go:def.bzl", "go_library")
   879  
   880  go_library(
   881      name = "go_default_library",
   882      srcs = ["bar.go"],
   883      importpath = "example.com/alt/baz",  # keep
   884      visibility = ["//visibility:public"],
   885  )
   886  `,
   887  		}, {
   888  			path:    "bar/bar.go",
   889  			content: "package bar",
   890  		},
   891  	}
   892  
   893  	dir, err := createFiles(files)
   894  	if err != nil {
   895  		t.Fatal(err)
   896  	}
   897  	defer os.RemoveAll(dir)
   898  
   899  	args := []string{"-go_prefix", "example.com/repo"}
   900  	if err := runGazelle(dir, args); err != nil {
   901  		t.Fatal(err)
   902  	}
   903  
   904  	checkFiles(t, dir, []fileSpec{
   905  		{
   906  			path: "foo/BUILD.bazel",
   907  			content: `
   908  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   909  
   910  go_library(
   911      name = "go_default_library",
   912      srcs = ["foo.go"],
   913      importpath = "example.com/repo/foo",
   914      visibility = ["//visibility:public"],
   915      deps = ["//bar:go_default_library"],
   916  )
   917  `,
   918  		}, {
   919  			path: "bar/BUILD.bazel",
   920  			content: `load("@io_bazel_rules_go//go:def.bzl", "go_library")
   921  
   922  go_library(
   923      name = "go_default_library",
   924      srcs = ["bar.go"],
   925      importpath = "example.com/alt/baz",  # keep
   926      visibility = ["//visibility:public"],
   927  )
   928  `,
   929  		},
   930  	})
   931  }
   932  
   933  // TestResolveVendorSubdirectory checks that Gazelle can resolve libraries
   934  // in a vendor directory which is not at the repository root.
   935  func TestResolveVendorSubdirectory(t *testing.T) {
   936  	files := []fileSpec{
   937  		{path: "WORKSPACE"},
   938  		{
   939  			path:    "sub/vendor/example.com/foo/foo.go",
   940  			content: "package foo",
   941  		}, {
   942  			path: "sub/bar/bar.go",
   943  			content: `
   944  package bar
   945  
   946  import _ "example.com/foo"
   947  `,
   948  		},
   949  	}
   950  	dir, err := createFiles(files)
   951  	if err != nil {
   952  		t.Fatal(err)
   953  	}
   954  	defer os.RemoveAll(dir)
   955  
   956  	args := []string{"-go_prefix", "example.com/repo"}
   957  	if err := runGazelle(dir, args); err != nil {
   958  		t.Fatal(err)
   959  	}
   960  
   961  	checkFiles(t, dir, []fileSpec{
   962  		{
   963  			path: "sub/vendor/example.com/foo/BUILD.bazel",
   964  			content: `
   965  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   966  
   967  go_library(
   968      name = "go_default_library",
   969      srcs = ["foo.go"],
   970      importpath = "example.com/foo",
   971      visibility = ["//visibility:public"],
   972  )
   973  `,
   974  		}, {
   975  			path: "sub/bar/BUILD.bazel",
   976  			content: `
   977  load("@io_bazel_rules_go//go:def.bzl", "go_library")
   978  
   979  go_library(
   980      name = "go_default_library",
   981      srcs = ["bar.go"],
   982      importpath = "example.com/repo/sub/bar",
   983      visibility = ["//visibility:public"],
   984      deps = ["//sub/vendor/example.com/foo:go_default_library"],
   985  )
   986  `,
   987  		},
   988  	})
   989  }
   990  
   991  // TestDeleteProtoWithDeps checks that Gazelle will delete proto rules with
   992  // dependencies after the proto sources are removed.
   993  func TestDeleteProtoWithDeps(t *testing.T) {
   994  	files := []fileSpec{
   995  		{path: "WORKSPACE"},
   996  		{
   997  			path: "foo/BUILD.bazel",
   998  			content: `
   999  load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
  1000  load("@io_bazel_rules_go//go:def.bzl", "go_library")
  1001  
  1002  go_library(
  1003      name = "go_default_library",
  1004      srcs = ["extra.go"],
  1005      embed = [":scratch_go_proto"],
  1006      importpath = "example.com/repo/foo",
  1007      visibility = ["//visibility:public"],
  1008  )
  1009  
  1010  proto_library(
  1011      name = "foo_proto",
  1012      srcs = ["foo.proto"],
  1013      visibility = ["//visibility:public"],
  1014      deps = ["//foo/bar:bar_proto"],
  1015  )
  1016  
  1017  go_proto_library(
  1018      name = "foo_go_proto",
  1019      importpath = "example.com/repo/foo",
  1020      proto = ":foo_proto",
  1021      visibility = ["//visibility:public"],
  1022      deps = ["//foo/bar:go_default_library"],
  1023  )
  1024  `,
  1025  		}, {
  1026  			path:    "foo/extra.go",
  1027  			content: "package foo",
  1028  		}, {
  1029  			path: "foo/bar/bar.proto",
  1030  			content: `
  1031  syntax = "proto3";
  1032  
  1033  option go_package = "example.com/repo/foo/bar";
  1034  
  1035  message Bar {};
  1036  `,
  1037  		},
  1038  	}
  1039  	dir, err := createFiles(files)
  1040  	if err != nil {
  1041  		t.Fatal(err)
  1042  	}
  1043  	defer os.RemoveAll(dir)
  1044  
  1045  	args := []string{"-go_prefix", "example.com/repo"}
  1046  	if err := runGazelle(dir, args); err != nil {
  1047  		t.Fatal(err)
  1048  	}
  1049  
  1050  	checkFiles(t, dir, []fileSpec{
  1051  		{
  1052  			path: "foo/BUILD.bazel",
  1053  			content: `
  1054  load("@io_bazel_rules_go//go:def.bzl", "go_library")
  1055  
  1056  go_library(
  1057      name = "go_default_library",
  1058      srcs = ["extra.go"],
  1059      importpath = "example.com/repo/foo",
  1060      visibility = ["//visibility:public"],
  1061  )
  1062  `,
  1063  		},
  1064  	})
  1065  }
  1066  
  1067  func TestCustomRepoNames(t *testing.T) {
  1068  	files := []fileSpec{
  1069  		{
  1070  			path: "WORKSPACE",
  1071  			content: `
  1072  go_repository(
  1073      name = "custom_repo",
  1074      importpath = "example.com/bar",
  1075      commit = "123456",
  1076  )
  1077  `,
  1078  		}, {
  1079  			path: "foo.go",
  1080  			content: `
  1081  package foo
  1082  
  1083  import _ "example.com/bar"
  1084  `,
  1085  		},
  1086  	}
  1087  	dir, err := createFiles(files)
  1088  	if err != nil {
  1089  		t.Fatal(err)
  1090  	}
  1091  	defer os.RemoveAll(dir)
  1092  
  1093  	args := []string{"-go_prefix", "example.com/foo"}
  1094  	if err := runGazelle(dir, args); err != nil {
  1095  		t.Fatal(err)
  1096  	}
  1097  
  1098  	checkFiles(t, dir, []fileSpec{
  1099  		{
  1100  			path: "BUILD.bazel",
  1101  			content: `
  1102  load("@io_bazel_rules_go//go:def.bzl", "go_library")
  1103  
  1104  go_library(
  1105      name = "go_default_library",
  1106      srcs = ["foo.go"],
  1107      importpath = "example.com/foo",
  1108      visibility = ["//visibility:public"],
  1109      deps = ["@custom_repo//:go_default_library"],
  1110  )
  1111  `,
  1112  		},
  1113  	})
  1114  }
  1115  
  1116  func TestImportReposFromDep(t *testing.T) {
  1117  	files := []fileSpec{
  1118  		{
  1119  			path: "WORKSPACE",
  1120  			content: `workspace(name = "bazel_gazelle")
  1121  
  1122  http_archive(
  1123      name = "io_bazel_rules_go",
  1124      url = "https://github.com/bazelbuild/rules_go/releases/download/0.7.1/rules_go-0.7.1.tar.gz",
  1125      sha256 = "341d5eacef704415386974bc82a1783a8b7ffbff2ab6ba02375e1ca20d9b031c",
  1126  )
  1127  load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
  1128  go_rules_dependencies()
  1129  go_register_toolchains()
  1130  
  1131  load("//:deps.bzl", "gazelle_dependencies")
  1132  gazelle_dependencies()
  1133  
  1134  go_repository(
  1135      name = "org_golang_x_net",
  1136      importpath = "golang.org/x/net",
  1137      tag = "1.2",
  1138  )
  1139  
  1140  # keep
  1141  go_repository(
  1142      name = "org_golang_x_sys",
  1143      importpath = "golang.org/x/sys",
  1144      remote = "https://github.com/golang/sys",
  1145  )
  1146  
  1147  http_archive(
  1148      name = "com_github_go_yaml_yaml",
  1149      urls = ["https://example.com/yaml.tar.gz"],
  1150      sha256 = "1234",
  1151  )
  1152  `,
  1153  		}, {
  1154  			path: "Gopkg.lock",
  1155  			content: `# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
  1156  
  1157  
  1158  [[projects]]
  1159    name = "github.com/pkg/errors"
  1160    packages = ["."]
  1161    revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
  1162    version = "v0.8.0"
  1163  
  1164  [[projects]]
  1165    branch = "master"
  1166    name = "golang.org/x/net"
  1167    packages = ["context"]
  1168    revision = "66aacef3dd8a676686c7ae3716979581e8b03c47"
  1169  
  1170  [[projects]]
  1171    branch = "master"
  1172    name = "golang.org/x/sys"
  1173    packages = ["unix"]
  1174    revision = "bb24a47a89eac6c1227fbcb2ae37a8b9ed323366"
  1175  
  1176  [[projects]]
  1177    branch = "v2"
  1178    name = "github.com/go-yaml/yaml"
  1179    packages = ["."]
  1180    revision = "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b"
  1181  
  1182  [solve-meta]
  1183    analyzer-name = "dep"
  1184    analyzer-version = 1
  1185    inputs-digest = "05c1cd69be2c917c0cc4b32942830c2acfa044d8200fdc94716aae48a8083702"
  1186    solver-name = "gps-cdcl"
  1187    solver-version = 1
  1188  `,
  1189  		},
  1190  	}
  1191  	dir, err := createFiles(files)
  1192  	if err != nil {
  1193  		t.Fatal(err)
  1194  	}
  1195  	defer os.RemoveAll(dir)
  1196  
  1197  	args := []string{"update-repos", "-from_file", "Gopkg.lock"}
  1198  	if err := runGazelle(dir, args); err != nil {
  1199  		t.Fatal(err)
  1200  	}
  1201  
  1202  	checkFiles(t, dir, []fileSpec{
  1203  		{
  1204  			path: "WORKSPACE",
  1205  			content: `workspace(name = "bazel_gazelle")
  1206  
  1207  http_archive(
  1208      name = "io_bazel_rules_go",
  1209      url = "https://github.com/bazelbuild/rules_go/releases/download/0.7.1/rules_go-0.7.1.tar.gz",
  1210      sha256 = "341d5eacef704415386974bc82a1783a8b7ffbff2ab6ba02375e1ca20d9b031c",
  1211  )
  1212  
  1213  load("@io_bazel_rules_go//go:def.bzl", "go_register_toolchains", "go_repository", "go_rules_dependencies")
  1214  
  1215  go_rules_dependencies()
  1216  
  1217  go_register_toolchains()
  1218  
  1219  load("//:deps.bzl", "gazelle_dependencies")
  1220  
  1221  gazelle_dependencies()
  1222  
  1223  go_repository(
  1224      name = "org_golang_x_net",
  1225      importpath = "golang.org/x/net",
  1226      commit = "66aacef3dd8a676686c7ae3716979581e8b03c47",
  1227  )
  1228  
  1229  # keep
  1230  go_repository(
  1231      name = "org_golang_x_sys",
  1232      importpath = "golang.org/x/sys",
  1233      remote = "https://github.com/golang/sys",
  1234  )
  1235  
  1236  http_archive(
  1237      name = "com_github_go_yaml_yaml",
  1238      urls = ["https://example.com/yaml.tar.gz"],
  1239      sha256 = "1234",
  1240  )
  1241  
  1242  go_repository(
  1243      name = "com_github_pkg_errors",
  1244      commit = "645ef00459ed84a119197bfb8d8205042c6df63d",
  1245      importpath = "github.com/pkg/errors",
  1246  )
  1247  `,
  1248  		}})
  1249  }
  1250  
  1251  func TestDeleteRulesInEmptyDir(t *testing.T) {
  1252  	files := []fileSpec{
  1253  		{path: "WORKSPACE"},
  1254  		{
  1255  			path: "BUILD.bazel",
  1256  			content: `
  1257  load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_binary")
  1258  
  1259  go_library(
  1260      name = "go_default_library",
  1261      srcs = [
  1262          "bar.go",
  1263          "foo.go",
  1264      ],
  1265      importpath = "example.com/repo",
  1266      visibility = ["//visibility:private"],
  1267  )
  1268  
  1269  go_binary(
  1270      name = "cmd",
  1271      embed = [":go_default_library"],
  1272      visibility = ["//visibility:public"],
  1273  )
  1274  `,
  1275  		},
  1276  	}
  1277  	dir, err := createFiles(files)
  1278  	if err != nil {
  1279  		t.Fatal(err)
  1280  	}
  1281  	defer os.RemoveAll(dir)
  1282  
  1283  	args := []string{"-go_prefix=example.com/repo"}
  1284  	if err := runGazelle(dir, args); err != nil {
  1285  		t.Fatal(err)
  1286  	}
  1287  
  1288  	checkFiles(t, dir, []fileSpec{
  1289  		{
  1290  			path:    "BUILD.bazel",
  1291  			content: "",
  1292  		},
  1293  	})
  1294  }
  1295  
  1296  // TODO(jayconrod): more tests
  1297  //   run in fix mode in testdata directories to create new files
  1298  //   run in diff mode in testdata directories to update existing files (no change)