golang.org/x/tools@v0.21.0/go/packages/overlay_test.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package packages_test 6 7 import ( 8 "fmt" 9 "log" 10 "os" 11 "path/filepath" 12 "reflect" 13 "sort" 14 "testing" 15 16 "golang.org/x/tools/go/packages" 17 "golang.org/x/tools/go/packages/packagestest" 18 "golang.org/x/tools/internal/testenv" 19 ) 20 21 const ( 22 commonMode = packages.NeedName | packages.NeedFiles | 23 packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedSyntax 24 everythingMode = commonMode | packages.NeedDeps | packages.NeedTypes | 25 packages.NeedTypesSizes 26 ) 27 28 func TestOverlayChangesPackageName(t *testing.T) { 29 testAllOrModulesParallel(t, testOverlayChangesPackageName) 30 } 31 func testOverlayChangesPackageName(t *testing.T, exporter packagestest.Exporter) { 32 log.SetFlags(log.Lshortfile) 33 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 34 Name: "fake", 35 Files: map[string]interface{}{ 36 "a.go": "package foo\nfunc f(){}\n", 37 }, 38 Overlay: map[string][]byte{ 39 "a.go": []byte("package foox\nfunc f(){}\n"), 40 }, 41 }}) 42 defer exported.Cleanup() 43 exported.Config.Mode = packages.NeedName 44 45 initial, err := packages.Load(exported.Config, 46 filepath.Dir(exported.File("fake", "a.go"))) 47 if err != nil { 48 t.Fatalf("failed to load: %v", err) 49 } 50 if len(initial) != 1 || initial[0].ID != "fake" || initial[0].Name != "foox" { 51 t.Fatalf("got %v, expected [fake]", initial) 52 } 53 if len(initial[0].Errors) != 0 { 54 t.Fatalf("got %v, expected no errors", initial[0].Errors) 55 } 56 log.SetFlags(0) 57 } 58 func TestOverlayChangesBothPackageNames(t *testing.T) { 59 testAllOrModulesParallel(t, testOverlayChangesBothPackageNames) 60 } 61 func testOverlayChangesBothPackageNames(t *testing.T, exporter packagestest.Exporter) { 62 log.SetFlags(log.Lshortfile) 63 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 64 Name: "fake", 65 Files: map[string]interface{}{ 66 "a.go": "package foo\nfunc g(){}\n", 67 "a_test.go": "package foo\nfunc f(){}\n", 68 }, 69 Overlay: map[string][]byte{ 70 "a.go": []byte("package foox\nfunc g(){}\n"), 71 "a_test.go": []byte("package foox\nfunc f(){}\n"), 72 }, 73 }}) 74 defer exported.Cleanup() 75 exported.Config.Mode = commonMode 76 77 initial, err := packages.Load(exported.Config, 78 filepath.Dir(exported.File("fake", "a.go"))) 79 if err != nil { 80 t.Fatalf("failed to load: %v", err) 81 } 82 if len(initial) != 3 { 83 t.Errorf("got %d packges, expected 3", len(initial)) 84 } 85 want := []struct { 86 id, name string 87 count int 88 }{ 89 {"fake", "foox", 1}, 90 {"fake [fake.test]", "foox", 2}, 91 {"fake.test", "main", 1}, 92 } 93 if len(initial) != 3 { 94 t.Fatalf("expected 3 packages, got %v", len(initial)) 95 } 96 for i := 0; i < 3; i++ { 97 if ok := checkPkg(t, initial[i], want[i].id, want[i].name, want[i].count); !ok { 98 t.Errorf("%d: got {%s %s %d}, expected %v", i, initial[i].ID, 99 initial[i].Name, len(initial[i].Syntax), want[i]) 100 } 101 if len(initial[i].Errors) != 0 { 102 t.Errorf("%d: got %v, expected no errors", i, initial[i].Errors) 103 } 104 } 105 log.SetFlags(0) 106 } 107 func TestOverlayChangesTestPackageName(t *testing.T) { 108 testAllOrModulesParallel(t, testOverlayChangesTestPackageName) 109 } 110 func testOverlayChangesTestPackageName(t *testing.T, exporter packagestest.Exporter) { 111 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 112 Name: "fake", 113 Files: map[string]interface{}{ 114 "a_test.go": "package foo\nfunc f(){}\n", 115 }, 116 Overlay: map[string][]byte{ 117 "a_test.go": []byte("package foox\nfunc f(){}\n"), 118 }, 119 }}) 120 defer exported.Cleanup() 121 exported.Config.Mode = commonMode 122 123 initial, err := packages.Load(exported.Config, 124 filepath.Dir(exported.File("fake", "a_test.go"))) 125 if err != nil { 126 t.Fatalf("failed to load: %v", err) 127 } 128 if len(initial) != 3 { 129 t.Errorf("got %d packges, expected 3", len(initial)) 130 } 131 want := []struct { 132 id, name string 133 count int 134 }{ 135 {"fake", "foox", 0}, 136 {"fake [fake.test]", "foox", 1}, 137 {"fake.test", "main", 1}, 138 } 139 if len(initial) != 3 { 140 t.Fatalf("expected 3 packages, got %v", len(initial)) 141 } 142 for i := 0; i < 3; i++ { 143 if ok := checkPkg(t, initial[i], want[i].id, want[i].name, want[i].count); !ok { 144 t.Errorf("got {%s %s %d}, expected %v", initial[i].ID, 145 initial[i].Name, len(initial[i].Syntax), want[i]) 146 } 147 } 148 if len(initial[0].Errors) != 0 { 149 t.Fatalf("got %v, expected no errors", initial[0].Errors) 150 } 151 log.SetFlags(0) 152 } 153 154 func checkPkg(t *testing.T, p *packages.Package, id, name string, syntax int) bool { 155 t.Helper() 156 if p.ID == id && p.Name == name && len(p.Syntax) == syntax { 157 return true 158 } 159 return false 160 } 161 162 func TestOverlayXTests(t *testing.T) { 163 testAllOrModulesParallel(t, testOverlayXTests) 164 } 165 166 // This test checks the behavior of go/packages.Load with an overlaid 167 // x test. The source of truth is the go/packages.Load results for the 168 // exact same package, just on-disk. 169 func testOverlayXTests(t *testing.T, exporter packagestest.Exporter) { 170 const aFile = `package a; const C = "C"; func Hello() {}` 171 const aTestVariant = `package a 172 173 import "testing" 174 175 const TestC = "test" + C 176 177 func TestHello(){ 178 Hello() 179 }` 180 const aXTest = `package a_test 181 182 import ( 183 "testing" 184 185 "golang.org/fake/a" 186 ) 187 188 const xTestC = "x" + a.C 189 190 func TestHello(t *testing.T) { 191 a.Hello() 192 }` 193 194 // First, get the source of truth by loading the package, all on disk. 195 onDisk := packagestest.Export(t, exporter, []packagestest.Module{{ 196 Name: "golang.org/fake", 197 Files: map[string]interface{}{ 198 "a/a.go": aFile, 199 "a/a_test.go": aTestVariant, 200 "a/a_x_test.go": aXTest, 201 }, 202 }}) 203 defer onDisk.Cleanup() 204 205 onDisk.Config.Mode = commonMode 206 onDisk.Config.Tests = true 207 onDisk.Config.Mode = packages.LoadTypes 208 initial, err := packages.Load(onDisk.Config, fmt.Sprintf("file=%s", onDisk.File("golang.org/fake", "a/a_x_test.go"))) 209 if err != nil { 210 t.Fatal(err) 211 } 212 wantPkg := initial[0] 213 214 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 215 Name: "golang.org/fake", 216 Files: map[string]interface{}{ 217 "a/a.go": aFile, 218 "a/a_test.go": aTestVariant, 219 "a/a_x_test.go": ``, // empty x test on disk 220 }, 221 Overlay: map[string][]byte{ 222 "a/a_x_test.go": []byte(aXTest), 223 }, 224 }}) 225 defer exported.Cleanup() 226 227 if len(initial) != 1 { 228 t.Fatalf("expected 1 package, got %d", len(initial)) 229 } 230 // Confirm that the overlaid package is identical to the on-disk version. 231 pkg := initial[0] 232 if !reflect.DeepEqual(wantPkg, pkg) { 233 t.Fatalf("mismatched packages: want %#v, got %#v", wantPkg, pkg) 234 } 235 xTestC := constant(pkg, "xTestC") 236 if xTestC == nil { 237 t.Fatalf("no value for xTestC") 238 } 239 got := xTestC.Val().String() 240 // TODO(rstambler): Ideally, this test would check that the test variant 241 // was imported, but that's pretty complicated. 242 if want := `"xC"`; got != want { 243 t.Errorf("got: %q, want %q", got, want) 244 } 245 } 246 247 func TestOverlay(t *testing.T) { testAllOrModulesParallel(t, testOverlay) } 248 func testOverlay(t *testing.T, exporter packagestest.Exporter) { 249 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 250 Name: "golang.org/fake", 251 Files: map[string]interface{}{ 252 "a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`, 253 "b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`, 254 "c/c.go": `package c; const C = "c"`, 255 "c/c_test.go": `package c; import "testing"; func TestC(t *testing.T) {}`, 256 "d/d.go": `package d; const D = "d"`, 257 }}}) 258 defer exported.Cleanup() 259 260 for i, test := range []struct { 261 overlay map[string][]byte 262 want string // expected value of a.A 263 wantErrs []string 264 }{ 265 {nil, `"abc"`, nil}, // default 266 {map[string][]byte{}, `"abc"`, nil}, // empty overlay 267 {map[string][]byte{exported.File("golang.org/fake", "c/c.go"): []byte(`package c; const C = "C"`)}, `"abC"`, nil}, 268 {map[string][]byte{exported.File("golang.org/fake", "b/b.go"): []byte(`package b; import "golang.org/fake/c"; const B = "B" + c.C`)}, `"aBc"`, nil}, 269 // Overlay with an existing file in an existing package adding a new import. 270 {map[string][]byte{exported.File("golang.org/fake", "b/b.go"): []byte(`package b; import "golang.org/fake/d"; const B = "B" + d.D`)}, `"aBd"`, nil}, 271 // Overlay with an existing file in an existing package. 272 {map[string][]byte{exported.File("golang.org/fake", "c/c.go"): []byte(`package c; import "net/http"; const C = http.MethodGet`)}, `"abGET"`, nil}, 273 // Overlay with a new file in an existing package. 274 {map[string][]byte{ 275 exported.File("golang.org/fake", "c/c.go"): []byte(`package c;`), 276 filepath.Join(filepath.Dir(exported.File("golang.org/fake", "c/c.go")), "c_new_file.go"): []byte(`package c; const C = "Ç"`)}, 277 `"abÇ"`, nil}, 278 // Overlay with a new file in an existing package, adding a new dependency to that package. 279 {map[string][]byte{ 280 exported.File("golang.org/fake", "c/c.go"): []byte(`package c;`), 281 filepath.Join(filepath.Dir(exported.File("golang.org/fake", "c/c.go")), "c_new_file.go"): []byte(`package c; import "golang.org/fake/d"; const C = "c" + d.D`)}, 282 `"abcd"`, nil}, 283 } { 284 exported.Config.Overlay = test.overlay 285 exported.Config.Mode = packages.LoadAllSyntax 286 initial, err := packages.Load(exported.Config, "golang.org/fake/a") 287 if err != nil { 288 t.Error(err) 289 continue 290 } 291 292 // Check value of a.A. 293 a := initial[0] 294 aA := constant(a, "A") 295 if aA == nil { 296 t.Errorf("%d. a.A: got nil", i) 297 continue 298 } 299 got := aA.Val().String() 300 if got != test.want { 301 t.Errorf("%d. a.A: got %s, want %s", i, got, test.want) 302 } 303 304 // Check errors. 305 var errors []packages.Error 306 packages.Visit(initial, nil, func(pkg *packages.Package) { 307 errors = append(errors, pkg.Errors...) 308 }) 309 if errs := errorMessages(errors); !reflect.DeepEqual(errs, test.wantErrs) { 310 t.Errorf("%d. got errors %s, want %s", i, errs, test.wantErrs) 311 } 312 } 313 } 314 315 func TestOverlayDeps(t *testing.T) { testAllOrModulesParallel(t, testOverlayDeps) } 316 func testOverlayDeps(t *testing.T, exporter packagestest.Exporter) { 317 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 318 Name: "golang.org/fake", 319 Files: map[string]interface{}{ 320 "c/c.go": `package c; const C = "c"`, 321 "c/c_test.go": `package c; import "testing"; func TestC(t *testing.T) {}`, 322 }, 323 }}) 324 defer exported.Cleanup() 325 326 exported.Config.Overlay = map[string][]byte{exported.File("golang.org/fake", "c/c.go"): []byte(`package c; import "net/http"; const C = http.MethodGet`)} 327 exported.Config.Mode = packages.NeedName | 328 packages.NeedFiles | 329 packages.NeedCompiledGoFiles | 330 packages.NeedImports | 331 packages.NeedDeps | 332 packages.NeedTypesSizes 333 pkgs, err := packages.Load(exported.Config, fmt.Sprintf("file=%s", exported.File("golang.org/fake", "c/c.go"))) 334 if err != nil { 335 t.Error(err) 336 } 337 338 // Find package golang.org/fake/c 339 sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].ID < pkgs[j].ID }) 340 if len(pkgs) != 2 { 341 t.Fatalf("expected 2 packages, got %v", len(pkgs)) 342 } 343 pkgc := pkgs[0] 344 if pkgc.ID != "golang.org/fake/c" { 345 t.Errorf("expected first package in sorted list to be \"golang.org/fake/c\", got %v", pkgc.ID) 346 } 347 348 // Make sure golang.org/fake/c imports net/http, as per the overlay. 349 contains := func(imports map[string]*packages.Package, wantImport string) bool { 350 for imp := range imports { 351 if imp == wantImport { 352 return true 353 } 354 } 355 return false 356 } 357 if !contains(pkgc.Imports, "net/http") { 358 t.Errorf("expected import of %s in package %s, got the following imports: %v", 359 "net/http", pkgc.ID, pkgc.Imports) 360 } 361 362 } 363 364 func TestNewPackagesInOverlay(t *testing.T) { testAllOrModulesParallel(t, testNewPackagesInOverlay) } 365 func testNewPackagesInOverlay(t *testing.T, exporter packagestest.Exporter) { 366 exported := packagestest.Export(t, exporter, []packagestest.Module{ 367 { 368 Name: "golang.org/fake", 369 Files: map[string]interface{}{ 370 "a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`, 371 "b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`, 372 "c/c.go": `package c; const C = "c"`, 373 "d/d.go": `package d; const D = "d"`, 374 }, 375 }, 376 { 377 Name: "example.com/extramodule", 378 Files: map[string]interface{}{ 379 "pkg/x.go": "package pkg\n", 380 }, 381 }, 382 }) 383 defer exported.Cleanup() 384 385 dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go"))) 386 387 for _, test := range []struct { 388 name string 389 overlay map[string][]byte 390 want string // expected value of e.E 391 }{ 392 {"one_file", 393 map[string][]byte{ 394 filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A`)}, 395 `"eabc"`}, 396 {"multiple_files_same_package", 397 map[string][]byte{ 398 filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A + underscore`), 399 filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), 400 }, 401 `"eabc_"`}, 402 {"multiple_files_two_packages", 403 map[string][]byte{ 404 filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), 405 filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), 406 filepath.Join(dir, "f", "f.go"): []byte(`package f; const F = "f"`), 407 }, 408 `"ef_"`}, 409 {"multiple_files_three_packages", 410 map[string][]byte{ 411 filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), 412 filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), 413 filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), 414 filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), 415 }, 416 `"efg_"`}, 417 {"multiple_files_four_packages", 418 map[string][]byte{ 419 filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; import "golang.org/fake/h"; const E = "e" + f.F + h.H + underscore`), 420 filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), 421 filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), 422 filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), 423 filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), 424 }, 425 `"efgh_"`}, 426 {"multiple_files_four_packages_again", 427 map[string][]byte{ 428 filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), 429 filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), 430 filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), 431 filepath.Join(dir, "g", "g.go"): []byte(`package g; import "golang.org/fake/h"; const G = "g" + h.H`), 432 filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), 433 }, 434 `"efgh_"`}, 435 {"main_overlay", 436 map[string][]byte{ 437 filepath.Join(dir, "e", "main.go"): []byte(`package main; import "golang.org/fake/a"; const E = "e" + a.A; func main(){}`)}, 438 `"eabc"`}, 439 } { 440 t.Run(test.name, func(t *testing.T) { 441 exported.Config.Overlay = test.overlay 442 exported.Config.Mode = packages.LoadAllSyntax 443 exported.Config.Logf = t.Logf 444 445 // With an overlay, we don't know the expected import path, 446 // so load with the absolute path of the directory. 447 initial, err := packages.Load(exported.Config, filepath.Join(dir, "e")) 448 if err != nil { 449 t.Fatal(err) 450 } 451 452 // Check value of e.E. 453 e := initial[0] 454 eE := constant(e, "E") 455 if eE == nil { 456 t.Fatalf("e.E: was nil in %#v", e) 457 } 458 got := eE.Val().String() 459 if got != test.want { 460 t.Fatalf("e.E: got %s, want %s", got, test.want) 461 } 462 }) 463 } 464 } 465 466 // Test that we can create a package and its test package in an overlay. 467 func TestOverlayNewPackageAndTest(t *testing.T) { 468 testAllOrModulesParallel(t, testOverlayNewPackageAndTest) 469 } 470 func testOverlayNewPackageAndTest(t *testing.T, exporter packagestest.Exporter) { 471 exported := packagestest.Export(t, exporter, []packagestest.Module{ 472 { 473 Name: "golang.org/fake", 474 Files: map[string]interface{}{ 475 "foo.txt": "placeholder", 476 }, 477 }, 478 }) 479 defer exported.Cleanup() 480 481 dir := filepath.Dir(exported.File("golang.org/fake", "foo.txt")) 482 exported.Config.Overlay = map[string][]byte{ 483 filepath.Join(dir, "a.go"): []byte(`package a;`), 484 filepath.Join(dir, "a_test.go"): []byte(`package a; import "testing";`), 485 } 486 initial, err := packages.Load(exported.Config, "file="+filepath.Join(dir, "a.go"), "file="+filepath.Join(dir, "a_test.go")) 487 if err != nil { 488 t.Fatal(err) 489 } 490 if len(initial) != 2 { 491 t.Errorf("got %v packages, wanted %v", len(initial), 2) 492 } 493 } 494 495 func TestAdHocOverlays(t *testing.T) { 496 t.Parallel() 497 testenv.NeedsTool(t, "go") 498 499 // This test doesn't use packagestest because we are testing ad-hoc packages, 500 // which are outside of $GOPATH and outside of a module. 501 tmp, err := os.MkdirTemp("", "testAdHocOverlays") 502 if err != nil { 503 t.Fatal(err) 504 } 505 defer os.RemoveAll(tmp) 506 507 filename := filepath.Join(tmp, "a.go") 508 content := []byte(`package a 509 const A = 1 510 `) 511 512 // Make sure that the user's value of GO111MODULE does not affect test results. 513 for _, go111module := range []string{"off", "auto", "on"} { 514 t.Run("GO111MODULE="+go111module, func(t *testing.T) { 515 config := &packages.Config{ 516 Dir: tmp, 517 Env: append(os.Environ(), "GOPACKAGESDRIVER=off", fmt.Sprintf("GO111MODULE=%s", go111module)), 518 Mode: packages.LoadAllSyntax, 519 Overlay: map[string][]byte{ 520 filename: content, 521 }, 522 Logf: t.Logf, 523 } 524 initial, err := packages.Load(config, fmt.Sprintf("file=%s", filename)) 525 if err != nil { 526 t.Fatal(err) 527 } 528 if len(initial) == 0 { 529 t.Fatalf("no packages for %s", filename) 530 } 531 // Check value of a.A. 532 a := initial[0] 533 if a.Errors != nil { 534 t.Fatalf("a: got errors %+v, want no error", err) 535 } 536 aA := constant(a, "A") 537 if aA == nil { 538 t.Errorf("a.A: got nil") 539 return 540 } 541 got := aA.Val().String() 542 if want := "1"; got != want { 543 t.Errorf("a.A: got %s, want %s", got, want) 544 } 545 }) 546 } 547 } 548 549 // TestOverlayModFileChanges tests the behavior resulting from having files 550 // from multiple modules in overlays. 551 func TestOverlayModFileChanges(t *testing.T) { 552 t.Parallel() 553 testenv.NeedsTool(t, "go") 554 555 // Create two unrelated modules in a temporary directory. 556 tmp, err := os.MkdirTemp("", "tmp") 557 if err != nil { 558 t.Fatal(err) 559 } 560 defer os.RemoveAll(tmp) 561 562 // mod1 has a dependency on golang.org/x/xerrors. 563 mod1, err := os.MkdirTemp(tmp, "mod1") 564 if err != nil { 565 t.Fatal(err) 566 } 567 if err := os.WriteFile(filepath.Join(mod1, "go.mod"), []byte(`module mod1 568 569 require ( 570 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 571 ) 572 `), 0775); err != nil { 573 t.Fatal(err) 574 } 575 576 // mod2 does not have any dependencies. 577 mod2, err := os.MkdirTemp(tmp, "mod2") 578 if err != nil { 579 t.Fatal(err) 580 } 581 582 want := `module mod2 583 584 go 1.11 585 ` 586 if err := os.WriteFile(filepath.Join(mod2, "go.mod"), []byte(want), 0775); err != nil { 587 t.Fatal(err) 588 } 589 590 // Run packages.Load on mod2, while passing the contents over mod1/main.go in the overlay. 591 config := &packages.Config{ 592 Dir: mod2, 593 Env: append(os.Environ(), "GOPACKAGESDRIVER=off"), 594 Mode: packages.LoadImports, 595 Overlay: map[string][]byte{ 596 filepath.Join(mod1, "main.go"): []byte(`package main 597 import "golang.org/x/xerrors" 598 func main() { 599 _ = errors.New("") 600 } 601 `), 602 filepath.Join(mod2, "main.go"): []byte(`package main 603 func main() {} 604 `), 605 }, 606 } 607 if _, err := packages.Load(config, fmt.Sprintf("file=%s", filepath.Join(mod2, "main.go"))); err != nil { 608 t.Fatal(err) 609 } 610 611 // Check that mod2/go.mod has not been modified. 612 got, err := os.ReadFile(filepath.Join(mod2, "go.mod")) 613 if err != nil { 614 t.Fatal(err) 615 } 616 if string(got) != want { 617 t.Errorf("expected %s, got %s", want, string(got)) 618 } 619 } 620 621 func TestOverlayGOPATHVendoring(t *testing.T) { 622 t.Parallel() 623 624 exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{{ 625 Name: "golang.org/fake", 626 Files: map[string]interface{}{ 627 "vendor/vendor.com/foo/foo.go": `package foo; const X = "hi"`, 628 "user/user.go": `package user`, 629 }, 630 }}) 631 defer exported.Cleanup() 632 633 exported.Config.Mode = packages.LoadAllSyntax 634 exported.Config.Logf = t.Logf 635 exported.Config.Overlay = map[string][]byte{ 636 exported.File("golang.org/fake", "user/user.go"): []byte(`package user; import "vendor.com/foo"; var x = foo.X`), 637 } 638 initial, err := packages.Load(exported.Config, "golang.org/fake/user") 639 if err != nil { 640 t.Fatal(err) 641 } 642 user := initial[0] 643 if len(user.Imports) != 1 { 644 t.Fatal("no imports for user") 645 } 646 if user.Imports["vendor.com/foo"].Name != "foo" { 647 t.Errorf("failed to load vendored package foo, imports: %#v", user.Imports["vendor.com/foo"]) 648 } 649 } 650 651 func TestContainsOverlay(t *testing.T) { testAllOrModulesParallel(t, testContainsOverlay) } 652 func testContainsOverlay(t *testing.T, exporter packagestest.Exporter) { 653 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 654 Name: "golang.org/fake", 655 Files: map[string]interface{}{ 656 "a/a.go": `package a; import "golang.org/fake/b"`, 657 "b/b.go": `package b; import "golang.org/fake/c"`, 658 "c/c.go": `package c`, 659 }}}) 660 defer exported.Cleanup() 661 bOverlayFile := filepath.Join(filepath.Dir(exported.File("golang.org/fake", "b/b.go")), "b_overlay.go") 662 exported.Config.Mode = packages.LoadImports 663 exported.Config.Overlay = map[string][]byte{bOverlayFile: []byte(`package b;`)} 664 initial, err := packages.Load(exported.Config, "file="+bOverlayFile) 665 if err != nil { 666 t.Fatal(err) 667 } 668 669 graph, _ := importGraph(initial) 670 wantGraph := ` 671 * golang.org/fake/b 672 golang.org/fake/c 673 golang.org/fake/b -> golang.org/fake/c 674 `[1:] 675 if graph != wantGraph { 676 t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) 677 } 678 } 679 680 func TestContainsOverlayXTest(t *testing.T) { testAllOrModulesParallel(t, testContainsOverlayXTest) } 681 func testContainsOverlayXTest(t *testing.T, exporter packagestest.Exporter) { 682 exported := packagestest.Export(t, exporter, []packagestest.Module{{ 683 Name: "golang.org/fake", 684 Files: map[string]interface{}{ 685 "a/a.go": `package a; import "golang.org/fake/b"`, 686 "b/b.go": `package b; import "golang.org/fake/c"`, 687 "c/c.go": `package c`, 688 }}}) 689 defer exported.Cleanup() 690 691 bOverlayXTestFile := filepath.Join(filepath.Dir(exported.File("golang.org/fake", "b/b.go")), "b_overlay_x_test.go") 692 exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedImports 693 exported.Config.Overlay = map[string][]byte{bOverlayXTestFile: []byte(`package b_test; import "golang.org/fake/b"`)} 694 initial, err := packages.Load(exported.Config, "file="+bOverlayXTestFile) 695 if err != nil { 696 t.Fatal(err) 697 } 698 699 graph, _ := importGraph(initial) 700 wantGraph := ` 701 golang.org/fake/b 702 * golang.org/fake/b_test [golang.org/fake/b.test] 703 golang.org/fake/c 704 golang.org/fake/b -> golang.org/fake/c 705 golang.org/fake/b_test [golang.org/fake/b.test] -> golang.org/fake/b 706 `[1:] 707 if graph != wantGraph { 708 t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) 709 } 710 } 711 712 func TestInvalidFilesBeforeOverlay(t *testing.T) { 713 testAllOrModulesParallel(t, testInvalidFilesBeforeOverlay) 714 } 715 716 func testInvalidFilesBeforeOverlay(t *testing.T, exporter packagestest.Exporter) { 717 exported := packagestest.Export(t, exporter, []packagestest.Module{ 718 { 719 Name: "golang.org/fake", 720 Files: map[string]interface{}{ 721 "d/d.go": ``, 722 "main.go": ``, 723 }, 724 }, 725 }) 726 defer exported.Cleanup() 727 728 dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "d/d.go"))) 729 730 exported.Config.Mode = everythingMode 731 exported.Config.Tests = true 732 733 // First, check that all packages returned have files associated with them. 734 // Tests the work-around for golang/go#39986. 735 t.Run("no overlay", func(t *testing.T) { 736 initial, err := packages.Load(exported.Config, fmt.Sprintf("%s/...", dir)) 737 if err != nil { 738 t.Fatal(err) 739 } 740 for _, pkg := range initial { 741 if len(pkg.CompiledGoFiles) == 0 { 742 t.Fatalf("expected at least 1 CompiledGoFile for %s, got none", pkg.PkgPath) 743 } 744 } 745 }) 746 747 } 748 749 // Tests golang/go#35973, fixed in Go 1.14. 750 func TestInvalidFilesBeforeOverlayContains(t *testing.T) { 751 testAllOrModulesParallel(t, testInvalidFilesBeforeOverlayContains) 752 } 753 func testInvalidFilesBeforeOverlayContains(t *testing.T, exporter packagestest.Exporter) { 754 exported := packagestest.Export(t, exporter, []packagestest.Module{ 755 { 756 Name: "golang.org/fake", 757 Files: map[string]interface{}{ 758 "d/d.go": `package d; import "net/http"; const Get = http.MethodGet; const Hello = "hello";`, 759 "d/util.go": ``, 760 "d/d_test.go": ``, 761 "main.go": ``, 762 }, 763 }, 764 }) 765 defer exported.Cleanup() 766 767 dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "d/d.go"))) 768 769 // Additional tests for test variants. 770 for i, tt := range []struct { 771 name string 772 overlay map[string][]byte 773 want string // expected value of d.D 774 wantID string // expected value for the package ID 775 }{ 776 // Overlay with a test variant. 777 { 778 "test_variant", 779 map[string][]byte{ 780 filepath.Join(dir, "d", "d_test.go"): []byte(`package d; import "testing"; const D = Get + "_test"; func TestD(t *testing.T) {};`), 781 }, 782 `"GET_test"`, "golang.org/fake/d [golang.org/fake/d.test]", 783 }, 784 // Overlay in package. 785 { 786 "second_file", 787 map[string][]byte{ 788 filepath.Join(dir, "d", "util.go"): []byte(`package d; const D = Get + "_util";`), 789 }, 790 `"GET_util"`, "golang.org/fake/d", 791 }, 792 // Overlay on the main file. 793 { 794 "main", 795 map[string][]byte{ 796 filepath.Join(dir, "main.go"): []byte(`package main; import "golang.org/fake/d"; const D = d.Get + "_main"; func main() {};`), 797 }, 798 `"GET_main"`, "golang.org/fake", 799 }, 800 { 801 "xtest", 802 map[string][]byte{ 803 filepath.Join(dir, "d", "d_test.go"): []byte(`package d_test; import "golang.org/fake/d"; import "testing"; const D = d.Get + "_xtest"; func TestD(t *testing.T) {};`), 804 }, 805 `"GET_xtest"`, "golang.org/fake/d_test [golang.org/fake/d.test]", 806 }, 807 } { 808 t.Run(tt.name, func(t *testing.T) { 809 exported.Config.Overlay = tt.overlay 810 exported.Config.Mode = everythingMode 811 exported.Config.Tests = true 812 813 for f := range tt.overlay { 814 initial, err := packages.Load(exported.Config, fmt.Sprintf("file=%s", f)) 815 if err != nil { 816 t.Fatal(err) 817 } 818 if len(initial) != 1 && 819 (len(initial) != 2 || !isTestVariant(initial[0].ID, initial[1].ID)) { 820 t.Fatalf("expected 1 package (perhaps with test variant), got %v", len(initial)) 821 } 822 pkg := initial[0] 823 if pkg.ID != tt.wantID { 824 t.Fatalf("expected package ID %q, got %q", tt.wantID, pkg.ID) 825 } 826 var containsFile bool 827 for _, goFile := range pkg.CompiledGoFiles { 828 if f == goFile { 829 containsFile = true 830 break 831 } 832 } 833 if !containsFile { 834 t.Fatalf("expected %s in CompiledGoFiles, got %v", f, pkg.CompiledGoFiles) 835 } 836 // Check value of d.D. 837 D := constant(pkg, "D") 838 if D == nil { 839 t.Fatalf("%d. D: got nil", i) 840 } 841 got := D.Val().String() 842 if got != tt.want { 843 t.Fatalf("%d. D: got %s, want %s", i, got, tt.want) 844 } 845 } 846 }) 847 } 848 } 849 850 func isTestVariant(libID, testID string) bool { 851 variantID := fmt.Sprintf("%[1]s [%[1]s.test]", libID) 852 return variantID == testID 853 } 854 855 func TestInvalidXTestInGOPATH(t *testing.T) { 856 testAllOrModulesParallel(t, testInvalidXTestInGOPATH) 857 } 858 func testInvalidXTestInGOPATH(t *testing.T, exporter packagestest.Exporter) { 859 t.Skip("Not fixed yet. See golang.org/issue/40825.") 860 861 exported := packagestest.Export(t, exporter, []packagestest.Module{ 862 { 863 Name: "golang.org/fake", 864 Files: map[string]interface{}{ 865 "x/x.go": `package x`, 866 "x/x_test.go": ``, 867 }, 868 }, 869 }) 870 defer exported.Cleanup() 871 872 dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "x/x.go"))) 873 874 exported.Config.Mode = everythingMode 875 exported.Config.Tests = true 876 877 initial, err := packages.Load(exported.Config, fmt.Sprintf("%s/...", dir)) 878 if err != nil { 879 t.Fatal(err) 880 } 881 pkg := initial[0] 882 if len(pkg.CompiledGoFiles) != 2 { 883 t.Fatalf("expected at least 2 CompiledGoFiles for %s, got %v", pkg.PkgPath, len(pkg.CompiledGoFiles)) 884 } 885 } 886 887 // Reproduces golang/go#40685. 888 func TestAddImportInOverlay(t *testing.T) { 889 testAllOrModulesParallel(t, testAddImportInOverlay) 890 } 891 func testAddImportInOverlay(t *testing.T, exporter packagestest.Exporter) { 892 exported := packagestest.Export(t, exporter, []packagestest.Module{ 893 { 894 Name: "golang.org/fake", 895 Files: map[string]interface{}{ 896 "a/a.go": `package a 897 898 import ( 899 "fmt" 900 ) 901 902 func _() { 903 fmt.Println("") 904 os.Stat("") 905 }`, 906 "a/a_test.go": `package a 907 908 import ( 909 "os" 910 "testing" 911 ) 912 913 func TestA(t *testing.T) { 914 os.Stat("") 915 }`, 916 }, 917 }, 918 }) 919 defer exported.Cleanup() 920 921 exported.Config.Mode = everythingMode 922 exported.Config.Tests = true 923 924 dir := filepath.Dir(exported.File("golang.org/fake", "a/a.go")) 925 exported.Config.Overlay = map[string][]byte{ 926 filepath.Join(dir, "a.go"): []byte(`package a 927 928 import ( 929 "fmt" 930 "os" 931 ) 932 933 func _() { 934 fmt.Println("") 935 os.Stat("") 936 } 937 `), 938 } 939 initial, err := packages.Load(exported.Config, "golang.org/fake/a") 940 if err != nil { 941 t.Fatal(err) 942 } 943 pkg := initial[0] 944 var foundOs bool 945 for _, imp := range pkg.Imports { 946 if imp.PkgPath == "os" { 947 foundOs = true 948 break 949 } 950 } 951 if !foundOs { 952 t.Fatalf(`expected import "os", found none: %v`, pkg.Imports) 953 } 954 } 955 956 // Tests that overlays are applied for different kinds of load patterns. 957 func TestLoadDifferentPatterns(t *testing.T) { 958 testAllOrModulesParallel(t, testLoadDifferentPatterns) 959 } 960 func testLoadDifferentPatterns(t *testing.T, exporter packagestest.Exporter) { 961 exported := packagestest.Export(t, exporter, []packagestest.Module{ 962 { 963 Name: "golang.org/fake", 964 Files: map[string]interface{}{ 965 "foo.txt": "placeholder", 966 "b/b.go": `package b 967 import "golang.org/fake/a" 968 func _() { 969 a.Hi() 970 } 971 `, 972 }, 973 }, 974 }) 975 defer exported.Cleanup() 976 977 exported.Config.Mode = everythingMode 978 exported.Config.Tests = true 979 980 dir := filepath.Dir(exported.File("golang.org/fake", "foo.txt")) 981 exported.Config.Overlay = map[string][]byte{ 982 filepath.Join(dir, "a", "a.go"): []byte(`package a 983 import "fmt" 984 func Hi() { 985 fmt.Println("") 986 } 987 `), 988 } 989 for _, tc := range []struct { 990 pattern string 991 }{ 992 {"golang.org/fake/a"}, 993 {"golang.org/fake/..."}, 994 {fmt.Sprintf("file=%s", filepath.Join(dir, "a", "a.go"))}, 995 } { 996 t.Run(tc.pattern, func(t *testing.T) { 997 initial, err := packages.Load(exported.Config, tc.pattern) 998 if err != nil { 999 t.Fatal(err) 1000 } 1001 var match *packages.Package 1002 for _, pkg := range initial { 1003 if pkg.PkgPath == "golang.org/fake/a" { 1004 match = pkg 1005 break 1006 } 1007 } 1008 if match == nil { 1009 t.Fatalf(`expected package path "golang.org/fake/a", got none`) 1010 } 1011 if match.PkgPath != "golang.org/fake/a" { 1012 t.Fatalf(`expected package path "golang.org/fake/a", got %q`, match.PkgPath) 1013 } 1014 if _, ok := match.Imports["fmt"]; !ok { 1015 t.Fatalf(`expected import "fmt", got none`) 1016 } 1017 }) 1018 } 1019 1020 // Now, load "golang.org/fake/b" and confirm that "golang.org/fake/a" is 1021 // not returned as a root. 1022 initial, err := packages.Load(exported.Config, "golang.org/fake/b") 1023 if err != nil { 1024 t.Fatal(err) 1025 } 1026 if len(initial) > 1 { 1027 t.Fatalf("expected 1 package, got %v", initial) 1028 } 1029 pkg := initial[0] 1030 if pkg.PkgPath != "golang.org/fake/b" { 1031 t.Fatalf(`expected package path "golang.org/fake/b", got %q`, pkg.PkgPath) 1032 } 1033 if _, ok := pkg.Imports["golang.org/fake/a"]; !ok { 1034 t.Fatalf(`expected import "golang.org/fake/a", got none`) 1035 } 1036 } 1037 1038 // Tests that overlays are applied for a replaced module. 1039 // This does not use go/packagestest because it needs to write a replace 1040 // directive with an absolute path in one of the module's go.mod files. 1041 func TestOverlaysInReplace(t *testing.T) { 1042 testenv.NeedsGoPackages(t) 1043 t.Parallel() 1044 1045 // Create module b.com in a temporary directory. Do not add any Go files 1046 // on disk. 1047 tmpPkgs, err := os.MkdirTemp("", "modules") 1048 if err != nil { 1049 t.Fatal(err) 1050 } 1051 defer os.RemoveAll(tmpPkgs) 1052 1053 dirB := filepath.Join(tmpPkgs, "b") 1054 if err := os.Mkdir(dirB, 0775); err != nil { 1055 t.Fatal(err) 1056 } 1057 if err := os.WriteFile(filepath.Join(dirB, "go.mod"), []byte(fmt.Sprintf("module %s.com", dirB)), 0775); err != nil { 1058 t.Fatal(err) 1059 } 1060 if err := os.MkdirAll(filepath.Join(dirB, "inner"), 0775); err != nil { 1061 t.Fatal(err) 1062 } 1063 1064 // Create a separate module that requires and replaces b.com. 1065 tmpWorkspace, err := os.MkdirTemp("", "workspace") 1066 if err != nil { 1067 t.Fatal(err) 1068 } 1069 defer os.RemoveAll(tmpWorkspace) 1070 goModContent := fmt.Sprintf(`module workspace.com 1071 1072 require ( 1073 b.com v0.0.0-00010101000000-000000000000 1074 ) 1075 1076 replace ( 1077 b.com => %s 1078 ) 1079 `, dirB) 1080 if err := os.WriteFile(filepath.Join(tmpWorkspace, "go.mod"), []byte(goModContent), 0775); err != nil { 1081 t.Fatal(err) 1082 } 1083 1084 // Add Go files for b.com/inner in an overlay and try loading it from the 1085 // workspace.com module. 1086 config := &packages.Config{ 1087 Dir: tmpWorkspace, 1088 Mode: packages.LoadAllSyntax, 1089 Logf: t.Logf, 1090 Overlay: map[string][]byte{ 1091 filepath.Join(dirB, "inner", "b.go"): []byte(`package inner; import "fmt"; func _() { fmt.Println("");`), 1092 }, 1093 } 1094 initial, err := packages.Load(config, "b.com/...") 1095 if err != nil { 1096 t.Error(err) 1097 } 1098 if len(initial) != 1 { 1099 t.Fatalf(`expected 1 package, got %v`, len(initial)) 1100 } 1101 pkg := initial[0] 1102 if pkg.PkgPath != "b.com/inner" { 1103 t.Fatalf(`expected package path "b.com/inner", got %q`, pkg.PkgPath) 1104 } 1105 if _, ok := pkg.Imports["fmt"]; !ok { 1106 t.Fatalf(`expected import "fmt", got none`) 1107 } 1108 }