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