github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/language/go/fileinfo_go_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 package golang 17 18 import ( 19 "go/build/constraint" 20 "os" 21 "path/filepath" 22 "strings" 23 "testing" 24 25 "github.com/google/go-cmp/cmp" 26 ) 27 28 var ( 29 fileInfoCmpOption = cmp.AllowUnexported( 30 fileInfo{}, 31 fileEmbed{}, 32 buildTags{}, 33 cgoTagsAndOpts{}, 34 ) 35 ) 36 37 func TestGoFileInfo(t *testing.T) { 38 for _, tc := range []struct { 39 desc, name, source string 40 want fileInfo 41 }{ 42 { 43 "empty file", 44 "foo.go", 45 "package foo\n", 46 fileInfo{ 47 packageName: "foo", 48 }, 49 }, 50 { 51 "xtest file", 52 "foo_test.go", 53 "package foo_test\n", 54 fileInfo{ 55 packageName: "foo", 56 isTest: true, 57 }, 58 }, 59 { 60 "xtest suffix on non-test", 61 "foo_xtest.go", 62 "package foo_test\n", 63 fileInfo{ 64 packageName: "foo_test", 65 isTest: false, 66 }, 67 }, 68 { 69 "single import", 70 "foo.go", 71 `package foo 72 73 import "github.com/foo/bar" 74 `, 75 fileInfo{ 76 packageName: "foo", 77 imports: []string{"github.com/foo/bar"}, 78 }, 79 }, 80 { 81 "multiple imports", 82 "foo.go", 83 `package foo 84 85 import ( 86 "github.com/foo/bar" 87 x "github.com/local/project/y" 88 ) 89 `, 90 fileInfo{ 91 packageName: "foo", 92 imports: []string{"github.com/foo/bar", "github.com/local/project/y"}, 93 }, 94 }, 95 { 96 "standard imports included", 97 "foo.go", 98 `package foo 99 100 import "fmt" 101 `, 102 fileInfo{ 103 packageName: "foo", 104 imports: []string{"fmt"}, 105 }, 106 }, 107 { 108 "cgo", 109 "foo.go", 110 `package foo 111 112 import "C" 113 `, 114 fileInfo{ 115 packageName: "foo", 116 isCgo: true, 117 }, 118 }, 119 { 120 "build tags", 121 "foo.go", 122 `// +build linux darwin 123 124 // +build !ignore 125 126 package foo 127 `, 128 fileInfo{ 129 packageName: "foo", 130 tags: &buildTags{ 131 expr: mustParseBuildTag(t, "(linux || darwin) && !ignore"), 132 rawTags: []string{"linux", "darwin", "ignore"}, 133 }, 134 }, 135 }, 136 { 137 "build tags without blank line", 138 "route.go", 139 `// Copyright 2017 140 141 // +build darwin dragonfly freebsd netbsd openbsd 142 143 // Package route provides basic functions for the manipulation of 144 // packet routing facilities on BSD variants. 145 package route 146 `, 147 fileInfo{ 148 packageName: "route", 149 tags: &buildTags{ 150 expr: mustParseBuildTag(t, "darwin || dragonfly || freebsd || netbsd || openbsd"), 151 rawTags: []string{"darwin", "dragonfly", "freebsd", "netbsd", "openbsd"}, 152 }, 153 }, 154 }, 155 { 156 "embed", 157 "embed.go", 158 `package foo 159 160 import _ "embed" 161 162 //go:embed embed.go 163 var src string 164 `, 165 fileInfo{ 166 packageName: "foo", 167 imports: []string{"embed"}, 168 embeds: []fileEmbed{{path: "embed.go"}}, 169 }, 170 }, 171 } { 172 t.Run(tc.desc, func(t *testing.T) { 173 dir, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "TestGoFileInfo") 174 if err != nil { 175 t.Fatal(err) 176 } 177 defer os.RemoveAll(dir) 178 path := filepath.Join(dir, tc.name) 179 if err := os.WriteFile(path, []byte(tc.source), 0o600); err != nil { 180 t.Fatal(err) 181 } 182 183 got := goFileInfo(path, "") 184 // Clear fields we don't care about for testing. 185 got = fileInfo{ 186 packageName: got.packageName, 187 isTest: got.isTest, 188 imports: got.imports, 189 embeds: got.embeds, 190 isCgo: got.isCgo, 191 tags: got.tags, 192 } 193 for i := range got.embeds { 194 got.embeds[i] = fileEmbed{path: got.embeds[i].path} 195 } 196 197 if diff := cmp.Diff(tc.want, got, fileInfoCmpOption); diff != "" { 198 t.Errorf("(-want, +got): %s", diff) 199 } 200 201 }) 202 } 203 } 204 205 func TestGoFileInfoFailure(t *testing.T) { 206 dir, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "TestGoFileInfoFailure") 207 if err != nil { 208 t.Fatal(err) 209 } 210 defer os.RemoveAll(dir) 211 name := "foo_linux_amd64.go" 212 path := filepath.Join(dir, name) 213 if err := os.WriteFile(path, []byte("pakcage foo"), 0o600); err != nil { 214 t.Fatal(err) 215 } 216 217 got := goFileInfo(path, "") 218 want := fileInfo{ 219 path: path, 220 name: name, 221 ext: goExt, 222 goos: "linux", 223 goarch: "amd64", 224 } 225 if diff := cmp.Diff(want, got, fileInfoCmpOption); diff != "" { 226 t.Errorf("(-want, +got): %s", diff) 227 } 228 229 } 230 231 func TestCgo(t *testing.T) { 232 for _, tc := range []struct { 233 desc, source string 234 want fileInfo 235 }{ 236 { 237 "not cgo", 238 "package foo\n", 239 fileInfo{isCgo: false}, 240 }, 241 { 242 "empty cgo", 243 `package foo 244 245 import "C" 246 `, 247 fileInfo{isCgo: true}, 248 }, 249 { 250 "simple flags", 251 `package foo 252 253 /* 254 #cgo CFLAGS: -O0 255 #cgo CPPFLAGS: -O1 256 #cgo CXXFLAGS: -O2 257 #cgo LDFLAGS: -O3 -O4 258 */ 259 import "C" 260 `, 261 fileInfo{ 262 isCgo: true, 263 cppopts: []*cgoTagsAndOpts{ 264 {opts: "-O1"}, 265 }, 266 copts: []*cgoTagsAndOpts{ 267 {opts: "-O0"}, 268 }, 269 cxxopts: []*cgoTagsAndOpts{ 270 {opts: "-O2"}, 271 }, 272 clinkopts: []*cgoTagsAndOpts{ 273 {opts: strings.Join([]string{"-O3", "-O4"}, optSeparator)}, 274 }, 275 }, 276 }, 277 { 278 "cflags with conditions", 279 `package foo 280 281 /* 282 #cgo foo bar,!baz CFLAGS: -O0 283 */ 284 import "C" 285 `, 286 fileInfo{ 287 isCgo: true, 288 copts: []*cgoTagsAndOpts{ 289 { 290 buildTags: &buildTags{ 291 expr: mustParseBuildTag(t, "foo || (bar && !baz)"), 292 rawTags: []string{"foo", "bar", "baz"}, 293 }, 294 opts: "-O0", 295 }, 296 }, 297 }, 298 }, 299 { 300 "slashslash comments", 301 `package foo 302 303 // #cgo CFLAGS: -O0 304 // #cgo CFLAGS: -O1 305 import "C" 306 `, 307 fileInfo{ 308 isCgo: true, 309 copts: []*cgoTagsAndOpts{ 310 {opts: "-O0"}, 311 {opts: "-O1"}, 312 }, 313 }, 314 }, 315 { 316 "comment above single import group", 317 `package foo 318 319 /* 320 #cgo CFLAGS: -O0 321 */ 322 import ("C") 323 `, 324 fileInfo{ 325 isCgo: true, 326 copts: []*cgoTagsAndOpts{ 327 {opts: "-O0"}, 328 }, 329 }, 330 }, 331 } { 332 t.Run(tc.desc, func(t *testing.T) { 333 dir, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "TestCgo") 334 if err != nil { 335 t.Fatal(err) 336 } 337 defer os.RemoveAll(dir) 338 name := "TestCgo.go" 339 path := filepath.Join(dir, name) 340 if err := os.WriteFile(path, []byte(tc.source), 0o600); err != nil { 341 t.Fatal(err) 342 } 343 344 got := goFileInfo(path, "") 345 346 // Clear fields we don't care about for testing. 347 got = fileInfo{ 348 isCgo: got.isCgo, 349 copts: got.copts, 350 cppopts: got.cppopts, 351 cxxopts: got.cxxopts, 352 clinkopts: got.clinkopts, 353 } 354 355 if diff := cmp.Diff(tc.want, got, fileInfoCmpOption); diff != "" { 356 t.Errorf("(-want, +got): %s", diff) 357 } 358 359 }) 360 } 361 } 362 363 // Copied from go/build build_test.go 364 var ( 365 expandSrcDirPath = filepath.Join(string(filepath.Separator)+"projects", "src", "add") 366 ) 367 368 // Copied from go/build build_test.go 369 var expandSrcDirTests = []struct { 370 input, expected string 371 }{ 372 {"-L ${SRCDIR}/libs -ladd", "-L /projects/src/add/libs -ladd"}, 373 {"${SRCDIR}/add_linux_386.a -pthread -lstdc++", "/projects/src/add/add_linux_386.a -pthread -lstdc++"}, 374 {"Nothing to expand here!", "Nothing to expand here!"}, 375 {"$", "$"}, 376 {"$$", "$$"}, 377 {"${", "${"}, 378 {"$}", "$}"}, 379 {"$FOO ${BAR}", "$FOO ${BAR}"}, 380 {"Find me the $SRCDIRECTORY.", "Find me the $SRCDIRECTORY."}, 381 {"$SRCDIR is missing braces", "$SRCDIR is missing braces"}, 382 } 383 384 // Copied from go/build build_test.go 385 func TestExpandSrcDir(t *testing.T) { 386 for _, test := range expandSrcDirTests { 387 output, _ := expandSrcDir(test.input, expandSrcDirPath) 388 if output != test.expected { 389 t.Errorf("%q expands to %q with SRCDIR=%q when %q is expected", test.input, output, expandSrcDirPath, test.expected) 390 } else { 391 t.Logf("%q expands to %q with SRCDIR=%q", test.input, output, expandSrcDirPath) 392 } 393 } 394 } 395 396 var ( 397 goPackageCmpOption = cmp.AllowUnexported( 398 goPackage{}, 399 goTarget{}, 400 protoTarget{}, 401 platformStringsBuilder{}, 402 platformStringInfo{}, 403 ) 404 ) 405 406 func TestExpandSrcDirRepoRelative(t *testing.T) { 407 repo, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "repo") 408 if err != nil { 409 t.Fatal(err) 410 } 411 sub := filepath.Join(repo, "sub") 412 if err := os.Mkdir(sub, 0o755); err != nil { 413 t.Fatal(err) 414 } 415 goFile := filepath.Join(sub, "sub.go") 416 content := []byte(`package sub 417 418 /* 419 #cgo CFLAGS: -I${SRCDIR}/.. 420 */ 421 import "C" 422 `) 423 if err := os.WriteFile(goFile, content, 0o644); err != nil { 424 t.Fatal(err) 425 } 426 c, _, _ := testConfig( 427 t, 428 "-repo_root="+repo, 429 "-go_prefix=example.com/repo") 430 fi := goFileInfo(filepath.Join(sub, "sub.go"), "sub") 431 pkgs, _ := buildPackages(c, sub, "sub", false, nil, []fileInfo{fi}) 432 got, ok := pkgs["sub"] 433 if !ok { 434 t.Fatal("did not build package 'sub'") 435 } 436 want := &goPackage{ 437 name: "sub", 438 dir: sub, 439 rel: "sub", 440 library: goTarget{cgo: true}, 441 } 442 want.library.sources.addGenericString("sub.go") 443 want.library.copts.addGenericString("-Isub/..") 444 if diff := cmp.Diff(want, got, goPackageCmpOption); diff != "" { 445 t.Errorf("(-want, +got): %s", diff) 446 } 447 448 } 449 450 // Copied from go/build build_test.go 451 func TestShellSafety(t *testing.T) { 452 tests := []struct { 453 input, srcdir, expected string 454 result bool 455 }{ 456 {"-I${SRCDIR}/../include", "/projects/src/issue 11868", "-I/projects/src/issue 11868/../include", true}, 457 {"-I${SRCDIR}", "wtf$@%", "-Iwtf$@%", true}, 458 {"-X${SRCDIR}/1,${SRCDIR}/2", "/projects/src/issue 11868", "-X/projects/src/issue 11868/1,/projects/src/issue 11868/2", true}, 459 {"-I/tmp -I/tmp", "/tmp2", "-I/tmp -I/tmp", false}, 460 {"-I/tmp", "/tmp/[0]", "-I/tmp", true}, 461 {"-I${SRCDIR}/dir", "/tmp/[0]", "-I/tmp/[0]/dir", false}, 462 } 463 for _, test := range tests { 464 output, ok := expandSrcDir(test.input, test.srcdir) 465 if ok != test.result { 466 t.Errorf("Expected %t while %q expands to %q with SRCDIR=%q; got %t", test.result, test.input, output, test.srcdir, ok) 467 } 468 if output != test.expected { 469 t.Errorf("Expected %q while %q expands with SRCDIR=%q; got %q", test.expected, test.input, test.srcdir, output) 470 } 471 } 472 } 473 474 func mustParseBuildTag(t *testing.T, in string) constraint.Expr { 475 x, err := constraint.Parse("//go:build " + in) 476 if err != nil { 477 t.Fatalf("%s: %s", in, err) 478 } 479 480 return x 481 }