...

Source file src/github.com/bazelbuild/bazel-gazelle/cmd/gazelle/fix_test.go

Documentation: github.com/bazelbuild/bazel-gazelle/cmd/gazelle

     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 main
    17  
    18  import (
    19  	"flag"
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"runtime"
    24  	"strings"
    25  	"testing"
    26  
    27  	"github.com/bazelbuild/bazel-gazelle/testtools"
    28  	"github.com/bazelbuild/rules_go/go/tools/bazel"
    29  )
    30  
    31  var goSdk = flag.String("go_sdk", "", "name of the go_sdk repository when invoked by Bazel")
    32  
    33  func TestMain(m *testing.M) {
    34  	status := 1
    35  	defer func() {
    36  		os.Exit(status)
    37  	}()
    38  
    39  	flag.Parse()
    40  
    41  	var err error
    42  	tmpDir, err := os.MkdirTemp(os.Getenv("TEST_TMPDIR"), "gazelle_test")
    43  	if err != nil {
    44  		fmt.Fprintln(os.Stderr, err)
    45  		return
    46  	}
    47  	defer func() {
    48  		// Before deleting files in the temporary directory, add write permission
    49  		// to any files that don't have it. Files and directories in the module cache
    50  		// are read-only, and on Windows, the read-only bit prevents deletion and
    51  		// prevents Bazel from cleaning up the source tree.
    52  		_ = filepath.Walk(tmpDir, func(path string, info os.FileInfo, err error) error {
    53  			if err != nil {
    54  				return err
    55  			}
    56  			if mode := info.Mode(); mode&0o200 == 0 {
    57  				err = os.Chmod(path, mode|0o200)
    58  			}
    59  			return err
    60  		})
    61  		os.RemoveAll(tmpDir)
    62  	}()
    63  
    64  	if *goSdk != "" {
    65  		// This flag is only set when the test is run by Bazel. Figure out where
    66  		// the Go binary is and set GOROOT appropriately.
    67  		entries, err := bazel.ListRunfiles()
    68  		if err != nil {
    69  			fmt.Fprintln(os.Stderr, err)
    70  			return
    71  		}
    72  
    73  		var goToolPath string
    74  		ext := ""
    75  		if runtime.GOOS == "windows" {
    76  			ext = ".exe"
    77  		}
    78  		for _, entry := range entries {
    79  			if entry.Workspace == *goSdk && entry.ShortPath == "bin/go"+ext {
    80  				goToolPath = entry.Path
    81  				break
    82  			}
    83  		}
    84  		if goToolPath == "" {
    85  			fmt.Fprintln(os.Stderr, "could not locate go tool")
    86  			return
    87  		}
    88  		os.Setenv("GOROOT", filepath.Dir(filepath.Dir(goToolPath)))
    89  	}
    90  	os.Setenv("GOCACHE", filepath.Join(tmpDir, "gocache"))
    91  	os.Setenv("GOPATH", filepath.Join(tmpDir, "gopath"))
    92  
    93  	status = m.Run()
    94  }
    95  
    96  func defaultArgs(dir string) []string {
    97  	return []string{
    98  		"-repo_root", dir,
    99  		"-go_prefix", "example.com/repo",
   100  		dir,
   101  	}
   102  }
   103  
   104  func TestCreateFile(t *testing.T) {
   105  	// Create a directory with a simple .go file.
   106  	tmpdir := os.Getenv("TEST_TMPDIR")
   107  	dir, err := os.MkdirTemp(tmpdir, "")
   108  	if err != nil {
   109  		t.Fatalf("os.MkdirTemp(%q, %q) failed with %v; want success", tmpdir, "", err)
   110  	}
   111  	defer os.RemoveAll(dir)
   112  
   113  	goFile := filepath.Join(dir, "main.go")
   114  	if err = os.WriteFile(goFile, []byte("package main"), 0o600); err != nil {
   115  		t.Fatalf("error writing file %q: %v", goFile, err)
   116  	}
   117  
   118  	// Check that Gazelle creates a new file named "BUILD.bazel".
   119  	if err = run(dir, defaultArgs(dir)); err != nil {
   120  		t.Fatalf("run failed: %v", err)
   121  	}
   122  
   123  	buildFile := filepath.Join(dir, "BUILD.bazel")
   124  	if _, err = os.Stat(buildFile); err != nil {
   125  		t.Errorf("could not stat BUILD.bazel: %v", err)
   126  	}
   127  }
   128  
   129  func TestUpdateFile(t *testing.T) {
   130  	// Create a directory with a simple .go file and an empty BUILD file.
   131  	tmpdir := os.Getenv("TEST_TMPDIR")
   132  	dir, err := os.MkdirTemp(tmpdir, "")
   133  	if err != nil {
   134  		t.Fatalf("os.MkdirTemp(%q, %q) failed with %v; want success", tmpdir, "", err)
   135  	}
   136  	defer os.RemoveAll(dir)
   137  
   138  	goFile := filepath.Join(dir, "main.go")
   139  	if err = os.WriteFile(goFile, []byte("package main"), 0o600); err != nil {
   140  		t.Fatalf("error writing file %q: %v", goFile, err)
   141  	}
   142  
   143  	buildFile := filepath.Join(dir, "BUILD")
   144  	if err = os.WriteFile(buildFile, nil, 0o600); err != nil {
   145  		t.Fatalf("error writing file %q: %v", buildFile, err)
   146  	}
   147  
   148  	// Check that Gazelle updates the BUILD file in place.
   149  	if err = run(dir, defaultArgs(dir)); err != nil {
   150  		t.Fatalf("run failed: %v", err)
   151  	}
   152  
   153  	if st, err := os.Stat(buildFile); err != nil {
   154  		t.Errorf("could not stat BUILD: %v", err)
   155  	} else if st.Size() == 0 {
   156  		t.Errorf("BUILD was not updated")
   157  	}
   158  
   159  	if _, err = os.Stat(filepath.Join(dir, "BUILD.bazel")); err == nil {
   160  		t.Errorf("BUILD.bazel should not exist")
   161  	}
   162  }
   163  
   164  func TestNoChanges(t *testing.T) {
   165  	// Create a directory with a BUILD file that doesn't need any changes.
   166  	tmpdir := os.Getenv("TEST_TMPDIR")
   167  	dir, err := os.MkdirTemp(tmpdir, "")
   168  	if err != nil {
   169  		t.Fatalf("os.MkdirTemp(%q, %q) failed with %v; want success", tmpdir, "", err)
   170  	}
   171  	defer os.RemoveAll(dir)
   172  
   173  	goFile := filepath.Join(dir, "main.go")
   174  	if err = os.WriteFile(goFile, []byte("package main\n\nfunc main() {}"), 0o600); err != nil {
   175  		t.Fatalf("error writing file %q: %v", goFile, err)
   176  	}
   177  
   178  	buildFile := filepath.Join(dir, "BUILD")
   179  	if err = os.WriteFile(buildFile, []byte(`load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
   180  
   181  go_library(
   182      name = "go_default_library",
   183      srcs = ["main.go"],
   184      importpath = "example.com/repo",
   185      visibility = ["//visibility:private"],
   186  )
   187  
   188  go_binary(
   189      name = "hello",
   190      embed = [":go_default_library"],
   191      visibility = ["//visibility:public"],
   192  )
   193  `), 0o600); err != nil {
   194  		t.Fatalf("error writing file %q: %v", buildFile, err)
   195  	}
   196  	st, err := os.Stat(buildFile)
   197  	if err != nil {
   198  		t.Errorf("could not stat BUILD: %v", err)
   199  	}
   200  	modTime := st.ModTime()
   201  
   202  	// Ensure that Gazelle does not write to the BUILD file.
   203  	if err = run(dir, defaultArgs(dir)); err != nil {
   204  		t.Fatalf("run failed: %v", err)
   205  	}
   206  
   207  	if st, err := os.Stat(buildFile); err != nil {
   208  		t.Errorf("could not stat BUILD: %v", err)
   209  	} else if !modTime.Equal(st.ModTime()) {
   210  		t.Errorf("unexpected modificaiton to BUILD")
   211  	}
   212  }
   213  
   214  func TestFixReadWriteDir(t *testing.T) {
   215  	buildInFile := testtools.FileSpec{
   216  		Path: "in/BUILD.in",
   217  		Content: `
   218  go_binary(
   219      name = "hello",
   220      pure = "on",
   221  )
   222  `,
   223  	}
   224  	buildSrcFile := testtools.FileSpec{
   225  		Path:    "src/BUILD.bazel",
   226  		Content: `# src build file`,
   227  	}
   228  	oldFiles := []testtools.FileSpec{
   229  		buildInFile,
   230  		buildSrcFile,
   231  		{
   232  			Path: "src/hello.go",
   233  			Content: `
   234  package main
   235  
   236  func main() {}
   237  `,
   238  		},
   239  		{
   240  			Path:    "out/BUILD",
   241  			Content: `this should get replaced`,
   242  		},
   243  	}
   244  
   245  	for _, tc := range []struct {
   246  		desc string
   247  		args []string
   248  		want []testtools.FileSpec
   249  	}{
   250  		{
   251  			desc: "read",
   252  			args: []string{
   253  				"-repo_root={{dir}}/src",
   254  				"-experimental_read_build_files_dir={{dir}}/in",
   255  				"-build_file_name=BUILD.bazel,BUILD,BUILD.in",
   256  				"-go_prefix=example.com/repo",
   257  				"{{dir}}/src",
   258  			},
   259  			want: []testtools.FileSpec{
   260  				buildInFile,
   261  				{
   262  					Path: "src/BUILD.bazel",
   263  					Content: `
   264  load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
   265  
   266  go_binary(
   267      name = "hello",
   268      embed = [":repo_lib"],
   269      pure = "on",
   270      visibility = ["//visibility:public"],
   271  )
   272  
   273  go_library(
   274      name = "repo_lib",
   275      srcs = ["hello.go"],
   276      importpath = "example.com/repo",
   277      visibility = ["//visibility:private"],
   278  )
   279  `,
   280  				},
   281  			},
   282  		}, {
   283  			desc: "write",
   284  			args: []string{
   285  				"-repo_root={{dir}}/src",
   286  				"-experimental_write_build_files_dir={{dir}}/out",
   287  				"-build_file_name=BUILD.bazel,BUILD,BUILD.in",
   288  				"-go_prefix=example.com/repo",
   289  				"{{dir}}/src",
   290  			},
   291  			want: []testtools.FileSpec{
   292  				buildInFile,
   293  				buildSrcFile,
   294  				{
   295  					Path: "out/BUILD",
   296  					Content: `
   297  load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
   298  
   299  # src build file
   300  
   301  go_library(
   302      name = "repo_lib",
   303      srcs = ["hello.go"],
   304      importpath = "example.com/repo",
   305      visibility = ["//visibility:private"],
   306  )
   307  
   308  go_binary(
   309      name = "repo",
   310      embed = [":repo_lib"],
   311      visibility = ["//visibility:public"],
   312  )
   313  `,
   314  				},
   315  			},
   316  		}, {
   317  			desc: "read_and_write",
   318  			args: []string{
   319  				"-repo_root={{dir}}/src",
   320  				"-experimental_read_build_files_dir={{dir}}/in",
   321  				"-experimental_write_build_files_dir={{dir}}/out",
   322  				"-build_file_name=BUILD.bazel,BUILD,BUILD.in",
   323  				"-go_prefix=example.com/repo",
   324  				"{{dir}}/src",
   325  			},
   326  			want: []testtools.FileSpec{
   327  				buildInFile,
   328  				{
   329  					Path: "out/BUILD",
   330  					Content: `
   331  load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
   332  
   333  go_binary(
   334      name = "hello",
   335      embed = [":repo_lib"],
   336      pure = "on",
   337      visibility = ["//visibility:public"],
   338  )
   339  
   340  go_library(
   341      name = "repo_lib",
   342      srcs = ["hello.go"],
   343      importpath = "example.com/repo",
   344      visibility = ["//visibility:private"],
   345  )
   346  `,
   347  				},
   348  			},
   349  		},
   350  	} {
   351  		t.Run(tc.desc, func(t *testing.T) {
   352  			dir, cleanup := testtools.CreateFiles(t, oldFiles)
   353  			defer cleanup()
   354  			replacer := strings.NewReplacer("{{dir}}", dir, "/", string(os.PathSeparator))
   355  			for i := range tc.args {
   356  				if strings.HasPrefix(tc.args[i], "-go_prefix=") {
   357  					continue // don't put backslashes in prefix on windows
   358  				}
   359  				tc.args[i] = replacer.Replace(tc.args[i])
   360  			}
   361  			if err := run(dir, tc.args); err != nil {
   362  				t.Error(err)
   363  			}
   364  			testtools.CheckFiles(t, dir, tc.want)
   365  		})
   366  	}
   367  }
   368  
   369  func TestFix_LangFilter(t *testing.T) {
   370  	fixture := []testtools.FileSpec{
   371  		{
   372  			Path: "BUILD.bazel",
   373  			Content: `
   374  load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
   375  
   376  go_binary(
   377      name = "nofix",
   378      library = ":go_default_library",
   379      visibility = ["//visibility:public"],
   380  )
   381  
   382  go_library(
   383      name = "go_default_library",
   384      srcs = ["main.go"],
   385      importpath = "example.com/repo",
   386      visibility = ["//visibility:public"],
   387  )`,
   388  		},
   389  		{
   390  			Path:    "main.go",
   391  			Content: `package main`,
   392  		},
   393  	}
   394  
   395  	dir, cleanup := testtools.CreateFiles(t, fixture)
   396  	defer cleanup()
   397  
   398  	// Check that Gazelle does not update the BUILD file, due to lang filter.
   399  	if err := run(dir, []string{
   400  		"-repo_root", dir,
   401  		"-go_prefix", "example.com/repo",
   402  		"-lang=proto",
   403  		dir,
   404  	}); err != nil {
   405  		t.Fatalf("run failed: %v", err)
   406  	}
   407  
   408  	testtools.CheckFiles(t, dir, fixture)
   409  }
   410  

View as plain text