github.com/afking/bazel-gazelle@v0.0.0-20180301150245-c02bc0f529e8/internal/packages/walk_test.go (about)

     1  /* Copyright 2016 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 packages_test
    17  
    18  import (
    19  	"io/ioutil"
    20  	"os"
    21  	"path"
    22  	"path/filepath"
    23  	"reflect"
    24  	"strings"
    25  	"testing"
    26  
    27  	"github.com/bazelbuild/bazel-gazelle/internal/config"
    28  	"github.com/bazelbuild/bazel-gazelle/internal/packages"
    29  	bf "github.com/bazelbuild/buildtools/build"
    30  )
    31  
    32  func tempDir() (string, error) {
    33  	return ioutil.TempDir(os.Getenv("TEST_TMPDIR"), "walk_test")
    34  }
    35  
    36  type fileSpec struct {
    37  	path, content, symlink string
    38  }
    39  
    40  func checkFiles(t *testing.T, files []fileSpec, goPrefix string, want []*packages.Package) {
    41  	dir, err := createFiles(files)
    42  	if err != nil {
    43  		t.Fatalf("createFiles() failed with %v; want success", err)
    44  	}
    45  	defer os.RemoveAll(dir)
    46  
    47  	for _, p := range want {
    48  		p.Dir = filepath.Join(dir, filepath.FromSlash(p.Rel))
    49  	}
    50  
    51  	c := &config.Config{
    52  		RepoRoot:            dir,
    53  		GoPrefix:            goPrefix,
    54  		Dirs:                []string{dir},
    55  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
    56  	}
    57  	got := walkPackages(c)
    58  	checkPackages(t, got, want)
    59  }
    60  
    61  func createFiles(files []fileSpec) (string, error) {
    62  	dir, err := tempDir()
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  	for _, f := range files {
    67  		path := filepath.Join(dir, f.path)
    68  		if strings.HasSuffix(f.path, "/") {
    69  			if err := os.MkdirAll(path, 0700); err != nil {
    70  				return dir, err
    71  			}
    72  			continue
    73  		}
    74  		if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
    75  			return "", err
    76  		}
    77  		if f.symlink != "" {
    78  			if err := os.Symlink(f.symlink, path); err != nil {
    79  				return "", err
    80  			}
    81  			continue
    82  		}
    83  		if err := ioutil.WriteFile(path, []byte(f.content), 0600); err != nil {
    84  			return "", err
    85  		}
    86  	}
    87  	return dir, nil
    88  }
    89  
    90  func walkPackages(c *config.Config) []*packages.Package {
    91  	var pkgs []*packages.Package
    92  	packages.Walk(c, c.RepoRoot, func(_, _ string, _ *config.Config, pkg *packages.Package, _ *bf.File, _ bool) {
    93  		if pkg != nil {
    94  			pkgs = append(pkgs, pkg)
    95  		}
    96  	})
    97  	return pkgs
    98  }
    99  
   100  func checkPackages(t *testing.T, got []*packages.Package, want []*packages.Package) {
   101  	if len(got) != len(want) {
   102  		names := []string{}
   103  		for _, p := range got {
   104  			names = append(names, p.Name)
   105  		}
   106  		t.Fatalf("got %d packages %v; want %d", len(got), names, len(want))
   107  	}
   108  	for i := 0; i < len(got); i++ {
   109  		checkPackage(t, got[i], want[i])
   110  	}
   111  }
   112  
   113  func checkPackage(t *testing.T, got, want *packages.Package) {
   114  	// TODO: Implement Stringer or Formatter to get more readable output.
   115  	if !reflect.DeepEqual(got, want) {
   116  		t.Errorf("for package %q, got %#v; want %#v", want.Name, got, want)
   117  	}
   118  }
   119  
   120  func TestWalkEmpty(t *testing.T) {
   121  	files := []fileSpec{
   122  		{path: "a/foo.c"},
   123  		{path: "b/BUILD"},
   124  		{path: "c/"},
   125  	}
   126  	want := []*packages.Package{}
   127  	checkFiles(t, files, "", want)
   128  }
   129  
   130  func TestWalkSimple(t *testing.T) {
   131  	files := []fileSpec{{path: "lib.go", content: "package lib"}}
   132  	want := []*packages.Package{
   133  		{
   134  			Name:       "lib",
   135  			ImportPath: "example.com/repo",
   136  			Library: packages.GoTarget{
   137  				Sources: packages.PlatformStrings{
   138  					Generic: []string{"lib.go"},
   139  				},
   140  			},
   141  		},
   142  	}
   143  	checkFiles(t, files, "example.com/repo", want)
   144  }
   145  
   146  func TestWalkNested(t *testing.T) {
   147  	files := []fileSpec{
   148  		{path: "a/foo.go", content: "package a"},
   149  		{path: "b/c/bar.go", content: "package c"},
   150  		{path: "b/d/baz.go", content: "package main"},
   151  	}
   152  	want := []*packages.Package{
   153  		{
   154  			Name:       "a",
   155  			Rel:        "a",
   156  			ImportPath: "example.com/repo/a",
   157  			Library: packages.GoTarget{
   158  				Sources: packages.PlatformStrings{
   159  					Generic: []string{"foo.go"},
   160  				},
   161  			},
   162  		},
   163  		{
   164  			Name:       "c",
   165  			Rel:        "b/c",
   166  			ImportPath: "example.com/repo/b/c",
   167  			Library: packages.GoTarget{
   168  				Sources: packages.PlatformStrings{
   169  					Generic: []string{"bar.go"},
   170  				},
   171  			},
   172  		},
   173  		{
   174  			Name:       "main",
   175  			Rel:        "b/d",
   176  			ImportPath: "example.com/repo/b/d",
   177  			Library: packages.GoTarget{
   178  				Sources: packages.PlatformStrings{
   179  					Generic: []string{"baz.go"},
   180  				},
   181  			},
   182  		},
   183  	}
   184  	checkFiles(t, files, "example.com/repo", want)
   185  }
   186  
   187  func TestProtoOnly(t *testing.T) {
   188  	files := []fileSpec{
   189  		{path: "a/a.proto"},
   190  	}
   191  	want := []*packages.Package{
   192  		{
   193  			Name:       "a",
   194  			Rel:        "a",
   195  			ImportPath: "example.com/repo/a",
   196  			Proto: packages.ProtoTarget{
   197  				Sources: packages.PlatformStrings{
   198  					Generic: []string{"a.proto"},
   199  				},
   200  			},
   201  		},
   202  	}
   203  	checkFiles(t, files, "example.com/repo", want)
   204  }
   205  
   206  func TestMultiplePackagesWithDefault(t *testing.T) {
   207  	files := []fileSpec{
   208  		{path: "a/a.go", content: "package a"},
   209  		{path: "a/b.go", content: "package b"},
   210  	}
   211  	want := []*packages.Package{
   212  		{
   213  			Name:       "a",
   214  			Rel:        "a",
   215  			ImportPath: "example.com/repo/a",
   216  			Library: packages.GoTarget{
   217  				Sources: packages.PlatformStrings{
   218  					Generic: []string{"a.go"},
   219  				},
   220  			},
   221  		},
   222  	}
   223  	checkFiles(t, files, "example.com/repo", want)
   224  }
   225  
   226  func TestMultiplePackagesWithoutDefault(t *testing.T) {
   227  	files := []fileSpec{
   228  		{path: "a/b.go", content: "package b"},
   229  		{path: "a/c.go", content: "package c"},
   230  	}
   231  	dir, err := createFiles(files)
   232  	if err != nil {
   233  		t.Fatalf("createFiles() failed with %v; want success", err)
   234  	}
   235  	defer os.RemoveAll(dir)
   236  
   237  	c := &config.Config{
   238  		RepoRoot:            dir,
   239  		Dirs:                []string{dir},
   240  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   241  	}
   242  	got := walkPackages(c)
   243  	if len(got) > 0 {
   244  		t.Errorf("got %v; want empty slice", got)
   245  	}
   246  }
   247  
   248  func TestMultiplePackagesWithProtoDefault(t *testing.T) {
   249  	files := []fileSpec{
   250  		{path: "a/a.proto", content: `syntax = "proto2";
   251  package a;
   252  `},
   253  		{path: "a/b.go", content: "package b"},
   254  	}
   255  	want := []*packages.Package{
   256  		{
   257  			Name:       "a",
   258  			Rel:        "a",
   259  			ImportPath: "example.com/repo/a",
   260  			Proto: packages.ProtoTarget{
   261  				Sources: packages.PlatformStrings{
   262  					Generic: []string{"a.proto"},
   263  				},
   264  			},
   265  		},
   266  	}
   267  	checkFiles(t, files, "example.com/repo", want)
   268  }
   269  
   270  func TestRootWithPrefix(t *testing.T) {
   271  	files := []fileSpec{
   272  		{path: "a.go", content: "package a"},
   273  		{path: "b.go", content: "package b"},
   274  	}
   275  	want := []*packages.Package{
   276  		{
   277  			Name:       "a",
   278  			ImportPath: "example.com/a",
   279  			Library: packages.GoTarget{
   280  				Sources: packages.PlatformStrings{
   281  					Generic: []string{"a.go"},
   282  				},
   283  			},
   284  		},
   285  	}
   286  	checkFiles(t, files, "example.com/a", want)
   287  }
   288  
   289  func TestRootWithoutPrefix(t *testing.T) {
   290  	files := []fileSpec{
   291  		{path: "a.go", content: "package a"},
   292  		{path: "b.go", content: "package b"},
   293  	}
   294  	dir, err := createFiles(files)
   295  	if err != nil {
   296  		t.Fatalf("createFiles() failed with %v; want success", err)
   297  	}
   298  	defer os.RemoveAll(dir)
   299  
   300  	c := &config.Config{
   301  		RepoRoot:            dir,
   302  		Dirs:                []string{dir},
   303  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   304  	}
   305  	got := walkPackages(c)
   306  	if len(got) > 0 {
   307  		t.Errorf("got %v; want empty slice", got)
   308  	}
   309  }
   310  
   311  func TestVendorResetsPrefix(t *testing.T) {
   312  	files := []fileSpec{
   313  		{path: "vendor/"},
   314  		{path: "sub/vendor/"},
   315  	}
   316  	dir, err := createFiles(files)
   317  	if err != nil {
   318  		t.Fatal(err)
   319  	}
   320  	defer os.RemoveAll(dir)
   321  
   322  	basePrefix := "example.com/repo"
   323  	c := &config.Config{
   324  		RepoRoot:            dir,
   325  		Dirs:                []string{dir},
   326  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   327  		GoPrefix:            basePrefix,
   328  	}
   329  	packages.Walk(c, c.RepoRoot, func(_, rel string, c *config.Config, _ *packages.Package, _ *bf.File, _ bool) {
   330  		if path.Base(rel) != "vendor" {
   331  			return
   332  		}
   333  		if c.GoPrefix != "" {
   334  			t.Errorf("in %q, GoPrefix not reset", rel)
   335  		}
   336  		if c.GoPrefixRel != rel {
   337  			t.Errorf("in %q, GoPrefixRel not set", rel)
   338  		}
   339  	})
   340  	if c.GoPrefix != basePrefix {
   341  		t.Errorf("prefix in base configuration was modified: %q", c.GoPrefix)
   342  	}
   343  }
   344  
   345  func TestPrefixEmpty(t *testing.T) {
   346  	files := []fileSpec{
   347  		{path: "a.go", content: "package foo"},
   348  	}
   349  	want := []*packages.Package{}
   350  	checkFiles(t, files, "", want)
   351  }
   352  
   353  func TestProtoImportPath(t *testing.T) {
   354  	files := []fileSpec{{
   355  		path: "foo.proto",
   356  		content: `syntax = "proto3";
   357  option go_package = "example.com/repo/foo";
   358  `,
   359  	}}
   360  	want := []*packages.Package{{
   361  		Name:       "foo",
   362  		ImportPath: "example.com/repo/foo",
   363  		Proto: packages.ProtoTarget{
   364  			Sources: packages.PlatformStrings{
   365  				Generic: []string{"foo.proto"},
   366  			},
   367  		},
   368  	}}
   369  	checkFiles(t, files, "", want)
   370  }
   371  
   372  func TestTestdata(t *testing.T) {
   373  	files := []fileSpec{
   374  		{path: "raw/testdata/"},
   375  		{path: "raw/a.go", content: "package raw"},
   376  		{path: "with_build/testdata/BUILD"},
   377  		{path: "with_build/a.go", content: "package with_build"},
   378  		{path: "with_build_bazel/testdata/BUILD.bazel"},
   379  		{path: "with_build_bazel/a.go", content: "package with_build_bazel"},
   380  		{path: "with_build_nested/testdata/x/BUILD"},
   381  		{path: "with_build_nested/a.go", content: "package with_build_nested"},
   382  		{path: "with_go/testdata/a.go", content: "package testdata"},
   383  		{path: "with_go/a.go", content: "package with_go"},
   384  	}
   385  	want := []*packages.Package{
   386  		{
   387  			Name:       "raw",
   388  			Rel:        "raw",
   389  			ImportPath: "example.com/repo/raw",
   390  			Library: packages.GoTarget{
   391  				Sources: packages.PlatformStrings{
   392  					Generic: []string{"a.go"},
   393  				},
   394  			},
   395  			HasTestdata: true,
   396  		},
   397  		{
   398  			Name:       "with_build",
   399  			Rel:        "with_build",
   400  			ImportPath: "example.com/repo/with_build",
   401  			Library: packages.GoTarget{
   402  				Sources: packages.PlatformStrings{
   403  					Generic: []string{"a.go"},
   404  				},
   405  			},
   406  			HasTestdata: false,
   407  		},
   408  		{
   409  			Name:       "with_build_bazel",
   410  			Rel:        "with_build_bazel",
   411  			ImportPath: "example.com/repo/with_build_bazel",
   412  			Library: packages.GoTarget{
   413  				Sources: packages.PlatformStrings{
   414  					Generic: []string{"a.go"},
   415  				},
   416  			},
   417  			HasTestdata: false,
   418  		},
   419  		{
   420  			Name:       "with_build_nested",
   421  			Rel:        "with_build_nested",
   422  			ImportPath: "example.com/repo/with_build_nested",
   423  			Library: packages.GoTarget{
   424  				Sources: packages.PlatformStrings{
   425  					Generic: []string{"a.go"},
   426  				},
   427  			},
   428  			HasTestdata: false,
   429  		},
   430  		{
   431  			Name:       "testdata",
   432  			Rel:        "with_go/testdata",
   433  			ImportPath: "example.com/repo/with_go/testdata",
   434  			Library: packages.GoTarget{
   435  				Sources: packages.PlatformStrings{
   436  					Generic: []string{"a.go"},
   437  				},
   438  			},
   439  		},
   440  		{
   441  			Name:       "with_go",
   442  			Rel:        "with_go",
   443  			ImportPath: "example.com/repo/with_go",
   444  			Library: packages.GoTarget{
   445  				Sources: packages.PlatformStrings{
   446  					Generic: []string{"a.go"},
   447  				},
   448  			},
   449  			HasTestdata: false,
   450  		},
   451  	}
   452  	checkFiles(t, files, "example.com/repo", want)
   453  }
   454  
   455  func TestGenerated(t *testing.T) {
   456  	files := []fileSpec{
   457  		{
   458  			path: "gen/BUILD",
   459  			content: `
   460  genrule(
   461      name = "from_genrule",
   462      outs = ["foo.go", "bar.go", "w.txt", "x.c", "y.s", "z.S"],
   463  )
   464  
   465  gen_other(
   466      name = "from_gen_other",
   467      out = "baz.go",
   468  )
   469  `,
   470  		},
   471  		{
   472  			path: "gen/foo.go",
   473  			content: `package foo
   474  
   475  import "github.com/jr_hacker/stuff"
   476  `,
   477  		},
   478  	}
   479  	want := []*packages.Package{
   480  		{
   481  			Name:       "foo",
   482  			Rel:        "gen",
   483  			ImportPath: "example.com/repo/gen",
   484  			Library: packages.GoTarget{
   485  				Sources: packages.PlatformStrings{
   486  					Generic: []string{"bar.go", "baz.go", "foo.go", "y.s"},
   487  				},
   488  				Imports: packages.PlatformStrings{
   489  					Generic: []string{"github.com/jr_hacker/stuff"},
   490  				},
   491  			},
   492  		},
   493  	}
   494  	checkFiles(t, files, "example.com/repo", want)
   495  }
   496  
   497  func TestGeneratedCgo(t *testing.T) {
   498  	files := []fileSpec{
   499  		{
   500  			path: "gen/BUILD",
   501  			content: `
   502  genrule(
   503      name = "from_genrule",
   504      outs = ["foo.go", "bar.go", "w.txt", "x.c", "y.s", "z.S"],
   505  )
   506  
   507  gen_other(
   508      name = "from_gen_other",
   509      out = "baz.go",
   510  )
   511  `,
   512  		},
   513  		{
   514  			path: "gen/foo.go",
   515  			content: `package foo
   516  
   517  import "C"
   518  
   519  import "github.com/jr_hacker/stuff"
   520  `,
   521  		},
   522  	}
   523  	want := []*packages.Package{
   524  		{
   525  			Name:       "foo",
   526  			Rel:        "gen",
   527  			ImportPath: "example.com/repo/gen",
   528  			Library: packages.GoTarget{
   529  				Sources: packages.PlatformStrings{
   530  					Generic: []string{"bar.go", "baz.go", "foo.go", "x.c", "y.s", "z.S"},
   531  				},
   532  				Imports: packages.PlatformStrings{
   533  					Generic: []string{"github.com/jr_hacker/stuff"},
   534  				},
   535  				Cgo: true,
   536  			},
   537  		},
   538  	}
   539  	checkFiles(t, files, "example.com/repo", want)
   540  }
   541  
   542  func TestIgnore(t *testing.T) {
   543  	files := []fileSpec{
   544  		{
   545  			path:    "BUILD",
   546  			content: "# gazelle:ignore",
   547  		}, {
   548  			path:    "foo.go",
   549  			content: "package foo",
   550  		}, {
   551  			path:    "bar/bar.go",
   552  			content: "package bar",
   553  		},
   554  	}
   555  	want := []*packages.Package{
   556  		{
   557  			Name:       "bar",
   558  			Rel:        "bar",
   559  			ImportPath: "example.com/repo/bar",
   560  			Library: packages.GoTarget{
   561  				Sources: packages.PlatformStrings{
   562  					Generic: []string{"bar.go"},
   563  				},
   564  			},
   565  		},
   566  	}
   567  	checkFiles(t, files, "example.com/repo", want)
   568  }
   569  
   570  func TestExcluded(t *testing.T) {
   571  	files := []fileSpec{
   572  		{
   573  			path:    "BUILD",
   574  			content: "# gazelle:exclude exclude/do.go",
   575  		}, {
   576  			path: "exclude/BUILD",
   577  			content: `
   578  # gazelle:exclude not.go
   579  
   580  # gazelle:exclude build.go
   581  
   582  genrule(
   583      name = "gen_build",
   584      outs = ["build.go"],
   585  )
   586  `,
   587  		},
   588  		{
   589  			path:    "exclude/do.go",
   590  			content: "",
   591  		},
   592  		{
   593  			path:    "exclude/not.go",
   594  			content: "",
   595  		},
   596  		{
   597  			path:    "exclude/build.go",
   598  			content: "",
   599  		},
   600  		{
   601  			path:    "exclude/real.go",
   602  			content: "package exclude",
   603  		},
   604  	}
   605  	want := []*packages.Package{
   606  		{
   607  			Name:       "exclude",
   608  			Rel:        "exclude",
   609  			ImportPath: "example.com/repo/exclude",
   610  			Library: packages.GoTarget{
   611  				Sources: packages.PlatformStrings{
   612  					Generic: []string{"real.go"},
   613  				},
   614  			},
   615  		},
   616  	}
   617  	checkFiles(t, files, "example.com/repo", want)
   618  }
   619  
   620  func TestExcludedPbGo(t *testing.T) {
   621  	files := []fileSpec{
   622  		{
   623  			path: "exclude/BUILD",
   624  			content: `
   625  # gazelle:exclude a.proto
   626  `,
   627  		},
   628  		{
   629  			path: "exclude/a.proto",
   630  			content: `syntax = "proto2";
   631  package exclude;`,
   632  		},
   633  		{
   634  			path:    "exclude/a.pb.go",
   635  			content: `package exclude`,
   636  		},
   637  		{
   638  			path: "exclude/b.proto",
   639  			content: `syntax = "proto2";
   640  package exclude;
   641  `,
   642  		},
   643  		{
   644  			path:    "exclude/b.pb.go",
   645  			content: `package exclude`,
   646  		},
   647  	}
   648  	want := []*packages.Package{
   649  		{
   650  			Name:       "exclude",
   651  			Rel:        "exclude",
   652  			ImportPath: "example.com/repo/exclude",
   653  			Library: packages.GoTarget{
   654  				Sources: packages.PlatformStrings{
   655  					Generic: []string{"a.pb.go"},
   656  				},
   657  			},
   658  			Proto: packages.ProtoTarget{
   659  				Sources: packages.PlatformStrings{
   660  					Generic: []string{"b.proto"},
   661  				},
   662  				HasPbGo: true,
   663  			},
   664  		},
   665  	}
   666  	checkFiles(t, files, "example.com/repo", want)
   667  }
   668  
   669  func TestLegacyProtos(t *testing.T) {
   670  	files := []fileSpec{
   671  		{
   672  			path:    "BUILD",
   673  			content: `# gazelle:proto legacy`,
   674  		}, {
   675  			path: "have_pbgo/a.proto",
   676  			content: `syntax = "proto2";
   677  package have_pbgo;`,
   678  		}, {
   679  			path:    "have_pbgo/a.pb.go",
   680  			content: `package have_pbgo`,
   681  		}, {
   682  			path: "no_pbgo/b.proto",
   683  			content: `syntax = "proto2";
   684  package no_pbgo;`,
   685  		}, {
   686  			path:    "no_pbgo/other.go",
   687  			content: `package no_pbgo`,
   688  		}, {
   689  			path: "proto_only/c.proto",
   690  			content: `syntax = "proto2";
   691  package proto_only;`,
   692  		},
   693  	}
   694  	want := []*packages.Package{
   695  		{
   696  			Name:       "have_pbgo",
   697  			Rel:        "have_pbgo",
   698  			ImportPath: "example.com/repo/have_pbgo",
   699  			Library: packages.GoTarget{
   700  				Sources: packages.PlatformStrings{
   701  					Generic: []string{"a.pb.go"},
   702  				},
   703  			},
   704  			Proto: packages.ProtoTarget{
   705  				Sources: packages.PlatformStrings{
   706  					Generic: []string{"a.proto"},
   707  				},
   708  				HasPbGo: true,
   709  			},
   710  		}, {
   711  			Name:       "no_pbgo",
   712  			Rel:        "no_pbgo",
   713  			ImportPath: "example.com/repo/no_pbgo",
   714  			Library: packages.GoTarget{
   715  				Sources: packages.PlatformStrings{
   716  					Generic: []string{"other.go"},
   717  				},
   718  			},
   719  			Proto: packages.ProtoTarget{
   720  				Sources: packages.PlatformStrings{
   721  					Generic: []string{"b.proto"},
   722  				},
   723  				HasPbGo: false,
   724  			},
   725  		},
   726  	}
   727  	checkFiles(t, files, "example.com/repo", want)
   728  }
   729  
   730  func TestMalformedBuildFile(t *testing.T) {
   731  	files := []fileSpec{
   732  		{path: "BUILD", content: "????"},
   733  		{path: "foo.go", content: "package foo"},
   734  	}
   735  	want := []*packages.Package{}
   736  	checkFiles(t, files, "example.com/repo", want)
   737  }
   738  
   739  func TestMultipleBuildFiles(t *testing.T) {
   740  	files := []fileSpec{
   741  		{path: "BUILD"},
   742  		{path: "BUILD.bazel"},
   743  		{path: "foo.go", content: "package foo"},
   744  	}
   745  	want := []*packages.Package{}
   746  	checkFiles(t, files, "example.com/repo", want)
   747  }
   748  
   749  func TestMalformedGoFile(t *testing.T) {
   750  	files := []fileSpec{
   751  		{path: "a.go", content: "pakcage foo"},
   752  		{path: "b.go", content: "package foo"},
   753  	}
   754  	want := []*packages.Package{
   755  		{
   756  			Name:       "foo",
   757  			ImportPath: "example.com/repo",
   758  			Library: packages.GoTarget{
   759  				Sources: packages.PlatformStrings{
   760  					Generic: []string{"a.go", "b.go"},
   761  				},
   762  			},
   763  		},
   764  	}
   765  	checkFiles(t, files, "example.com/repo", want)
   766  }
   767  
   768  func TestSymlinksBasic(t *testing.T) {
   769  	files := []fileSpec{
   770  		{path: "root/a.go", content: "package a"},
   771  		{path: "root/b", symlink: "../b"},   // symlink outside repo is followed
   772  		{path: "root/c", symlink: "c"},      // symlink inside repo is not followed.
   773  		{path: "root/d", symlink: "../b/d"}, // symlink under root/b not followed
   774  		{path: "root/e", symlink: "../e"},
   775  		{path: "c/c.go", symlink: "package c"},
   776  		{path: "b/b.go", content: "package b"},
   777  		{path: "b/d/d.go", content: "package d"},
   778  		{path: "e/loop", symlink: "loop2"}, // symlink loop
   779  		{path: "e/loop2", symlink: "loop"},
   780  	}
   781  	dir, err := createFiles(files)
   782  	if err != nil {
   783  		t.Fatalf("createFiles() failed with %v; want success", err)
   784  	}
   785  	want := []*packages.Package{
   786  		{
   787  			Name:       "d",
   788  			Dir:        dir + "/root/b/d",
   789  			Rel:        "b/d",
   790  			ImportPath: "example.com/repo/b/d",
   791  			Library: packages.GoTarget{
   792  				Sources: packages.PlatformStrings{
   793  					Generic: []string{"d.go"},
   794  				},
   795  			},
   796  		},
   797  		{
   798  			Name:       "b",
   799  			Dir:        dir + "/root/b",
   800  			Rel:        "b",
   801  			ImportPath: "example.com/repo/b",
   802  			Library: packages.GoTarget{
   803  				Sources: packages.PlatformStrings{
   804  					Generic: []string{"b.go"},
   805  				},
   806  			},
   807  		},
   808  		{
   809  			Name:       "a",
   810  			Dir:        dir + "/root",
   811  			ImportPath: "example.com/repo",
   812  			Library: packages.GoTarget{
   813  				Sources: packages.PlatformStrings{
   814  					Generic: []string{"a.go"},
   815  				},
   816  			},
   817  		},
   818  	}
   819  	c := &config.Config{
   820  		RepoRoot:            dir + "/root",
   821  		GoPrefix:            "example.com/repo",
   822  		Dirs:                []string{dir + "/root"},
   823  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   824  	}
   825  	got := walkPackages(c)
   826  	checkPackages(t, got, want)
   827  }
   828  
   829  func TestSymlinksIgnore(t *testing.T) {
   830  	files := []fileSpec{
   831  		{
   832  			path:    "root/BUILD",
   833  			content: "# gazelle:exclude b",
   834  		},
   835  		{path: "root/b", symlink: "../b"},
   836  		{path: "b/b.go", content: "package b"},
   837  	}
   838  	dir, err := createFiles(files)
   839  	if err != nil {
   840  		t.Fatalf("createFiles() failed with %v; want success", err)
   841  	}
   842  	want := []*packages.Package{}
   843  	c := &config.Config{
   844  		RepoRoot:            dir + "/root",
   845  		GoPrefix:            "example.com/repo",
   846  		Dirs:                []string{dir + "/root"},
   847  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   848  	}
   849  	got := walkPackages(c)
   850  	checkPackages(t, got, want)
   851  }
   852  
   853  func TestSymlinksMixIgnoredAndNonIgnored(t *testing.T) {
   854  	files := []fileSpec{
   855  		{
   856  			path:    "root/BUILD",
   857  			content: "# gazelle:exclude b",
   858  		},
   859  		{path: "root/b", symlink: "../b"},  // ignored
   860  		{path: "root/b2", symlink: "../b"}, // not ignored
   861  		{path: "b/b.go", content: "package b"},
   862  	}
   863  	dir, err := createFiles(files)
   864  	if err != nil {
   865  		t.Fatalf("createFiles() failed with %v; want success", err)
   866  	}
   867  	want := []*packages.Package{
   868  		{
   869  			Name:       "b",
   870  			Dir:        dir + "/root/b2",
   871  			Rel:        "b2",
   872  			ImportPath: "example.com/repo/b2",
   873  			Library: packages.GoTarget{
   874  				Sources: packages.PlatformStrings{
   875  					Generic: []string{"b.go"},
   876  				},
   877  			},
   878  		},
   879  	}
   880  	c := &config.Config{
   881  		RepoRoot:            dir + "/root",
   882  		GoPrefix:            "example.com/repo",
   883  		Dirs:                []string{dir + "/root"},
   884  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   885  	}
   886  	got := walkPackages(c)
   887  	checkPackages(t, got, want)
   888  }
   889  
   890  func TestSymlinksChained(t *testing.T) {
   891  	files := []fileSpec{
   892  		{path: "root/b", symlink: "../link0"},
   893  		{path: "link0", symlink: "b"},
   894  		{path: "root/b2", symlink: "../b"},
   895  		{path: "b/b.go", content: "package b"},
   896  	}
   897  	dir, err := createFiles(files)
   898  	if err != nil {
   899  		t.Fatalf("createFiles() failed with %v; want success", err)
   900  	}
   901  	want := []*packages.Package{
   902  		{
   903  			Name:       "b",
   904  			Dir:        dir + "/root/b",
   905  			Rel:        "b",
   906  			ImportPath: "example.com/repo/b",
   907  			Library: packages.GoTarget{
   908  				Sources: packages.PlatformStrings{
   909  					Generic: []string{"b.go"},
   910  				},
   911  			},
   912  		},
   913  	}
   914  	c := &config.Config{
   915  		RepoRoot:            dir + "/root",
   916  		GoPrefix:            "example.com/repo",
   917  		Dirs:                []string{dir + "/root"},
   918  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   919  	}
   920  	got := walkPackages(c)
   921  	checkPackages(t, got, want)
   922  }
   923  
   924  func TestSymlinksDangling(t *testing.T) {
   925  	files := []fileSpec{
   926  		{path: "root/b", symlink: "../b"},
   927  	}
   928  	dir, err := createFiles(files)
   929  	if err != nil {
   930  		t.Fatalf("createFiles() failed with %v; want success", err)
   931  	}
   932  	want := []*packages.Package{}
   933  	c := &config.Config{
   934  		RepoRoot:            dir + "/root",
   935  		GoPrefix:            "example.com/repo",
   936  		Dirs:                []string{dir + "/root"},
   937  		ValidBuildFileNames: config.DefaultValidBuildFileNames,
   938  	}
   939  	got := walkPackages(c)
   940  	checkPackages(t, got, want)
   941  }