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 }