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