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