github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/loader/loader_test.go (about) 1 // Copyright 2013 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 // +build go1.5 6 7 // No testdata on Android. 8 9 // +build !android 10 11 package loader_test 12 13 import ( 14 "fmt" 15 "go/build" 16 "path/filepath" 17 "reflect" 18 "sort" 19 "strings" 20 "sync" 21 "testing" 22 23 "golang.org/x/tools/go/buildutil" 24 "golang.org/x/tools/go/loader" 25 ) 26 27 // TestFromArgs checks that conf.FromArgs populates conf correctly. 28 // It does no I/O. 29 func TestFromArgs(t *testing.T) { 30 type result struct { 31 Err string 32 Rest []string 33 ImportPkgs map[string]bool 34 CreatePkgs []loader.PkgSpec 35 } 36 for _, test := range []struct { 37 args []string 38 tests bool 39 want result 40 }{ 41 // Mix of existing and non-existent packages. 42 { 43 args: []string{"nosuchpkg", "errors"}, 44 want: result{ 45 ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false}, 46 }, 47 }, 48 // Same, with -test flag. 49 { 50 args: []string{"nosuchpkg", "errors"}, 51 tests: true, 52 want: result{ 53 ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true}, 54 }, 55 }, 56 // Surplus arguments. 57 { 58 args: []string{"fmt", "errors", "--", "surplus"}, 59 want: result{ 60 Rest: []string{"surplus"}, 61 ImportPkgs: map[string]bool{"errors": false, "fmt": false}, 62 }, 63 }, 64 // Ad hoc package specified as *.go files. 65 { 66 args: []string{"foo.go", "bar.go"}, 67 want: result{CreatePkgs: []loader.PkgSpec{{ 68 Filenames: []string{"foo.go", "bar.go"}, 69 }}}, 70 }, 71 // Mixture of *.go and import paths. 72 { 73 args: []string{"foo.go", "fmt"}, 74 want: result{ 75 Err: "named files must be .go files: fmt", 76 }, 77 }, 78 } { 79 var conf loader.Config 80 rest, err := conf.FromArgs(test.args, test.tests) 81 got := result{ 82 Rest: rest, 83 ImportPkgs: conf.ImportPkgs, 84 CreatePkgs: conf.CreatePkgs, 85 } 86 if err != nil { 87 got.Err = err.Error() 88 } 89 if !reflect.DeepEqual(got, test.want) { 90 t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want) 91 } 92 } 93 } 94 95 func TestLoad_NoInitialPackages(t *testing.T) { 96 var conf loader.Config 97 98 const wantErr = "no initial packages were loaded" 99 100 prog, err := conf.Load() 101 if err == nil { 102 t.Errorf("Load succeeded unexpectedly, want %q", wantErr) 103 } else if err.Error() != wantErr { 104 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) 105 } 106 if prog != nil { 107 t.Errorf("Load unexpectedly returned a Program") 108 } 109 } 110 111 func TestLoad_MissingInitialPackage(t *testing.T) { 112 var conf loader.Config 113 conf.Import("nosuchpkg") 114 conf.Import("errors") 115 116 const wantErr = "couldn't load packages due to errors: nosuchpkg" 117 118 prog, err := conf.Load() 119 if err == nil { 120 t.Errorf("Load succeeded unexpectedly, want %q", wantErr) 121 } else if err.Error() != wantErr { 122 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) 123 } 124 if prog != nil { 125 t.Errorf("Load unexpectedly returned a Program") 126 } 127 } 128 129 func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) { 130 var conf loader.Config 131 conf.AllowErrors = true 132 conf.Import("nosuchpkg") 133 conf.ImportWithTests("errors") 134 135 prog, err := conf.Load() 136 if err != nil { 137 t.Errorf("Load failed unexpectedly: %v", err) 138 } 139 if prog == nil { 140 t.Fatalf("Load returned a nil Program") 141 } 142 if got, want := created(prog), "errors_test"; got != want { 143 t.Errorf("Created = %s, want %s", got, want) 144 } 145 if got, want := imported(prog), "errors"; got != want { 146 t.Errorf("Imported = %s, want %s", got, want) 147 } 148 } 149 150 func TestCreateUnnamedPackage(t *testing.T) { 151 var conf loader.Config 152 conf.CreateFromFilenames("") 153 prog, err := conf.Load() 154 if err != nil { 155 t.Fatalf("Load failed: %v", err) 156 } 157 if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { 158 t.Errorf("InitialPackages = %s, want %s", got, want) 159 } 160 } 161 162 func TestLoad_MissingFileInCreatedPackage(t *testing.T) { 163 var conf loader.Config 164 conf.CreateFromFilenames("", "missing.go") 165 166 const wantErr = "couldn't load packages due to errors: (unnamed)" 167 168 prog, err := conf.Load() 169 if prog != nil { 170 t.Errorf("Load unexpectedly returned a Program") 171 } 172 if err == nil { 173 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr) 174 } 175 if err.Error() != wantErr { 176 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr) 177 } 178 } 179 180 func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) { 181 conf := loader.Config{AllowErrors: true} 182 conf.CreateFromFilenames("", "missing.go") 183 184 prog, err := conf.Load() 185 if err != nil { 186 t.Errorf("Load failed: %v", err) 187 } 188 if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { 189 t.Fatalf("InitialPackages = %s, want %s", got, want) 190 } 191 } 192 193 func TestLoad_ParseError(t *testing.T) { 194 var conf loader.Config 195 conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go") 196 197 const wantErr = "couldn't load packages due to errors: badpkg" 198 199 prog, err := conf.Load() 200 if prog != nil { 201 t.Errorf("Load unexpectedly returned a Program") 202 } 203 if err == nil { 204 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr) 205 } 206 if err.Error() != wantErr { 207 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr) 208 } 209 } 210 211 func TestLoad_ParseError_AllowErrors(t *testing.T) { 212 var conf loader.Config 213 conf.AllowErrors = true 214 conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go") 215 216 prog, err := conf.Load() 217 if err != nil { 218 t.Errorf("Load failed unexpectedly: %v", err) 219 } 220 if prog == nil { 221 t.Fatalf("Load returned a nil Program") 222 } 223 if got, want := created(prog), "badpkg"; got != want { 224 t.Errorf("Created = %s, want %s", got, want) 225 } 226 227 badpkg := prog.Created[0] 228 if len(badpkg.Files) != 1 { 229 t.Errorf("badpkg has %d files, want 1", len(badpkg.Files)) 230 } 231 wantErr := filepath.Join("testdata", "badpkgdecl.go") + ":1:34: expected 'package', found 'EOF'" 232 if !hasError(badpkg.Errors, wantErr) { 233 t.Errorf("badpkg.Errors = %v, want %s", badpkg.Errors, wantErr) 234 } 235 } 236 237 func TestLoad_FromSource_Success(t *testing.T) { 238 var conf loader.Config 239 conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go") 240 241 prog, err := conf.Load() 242 if err != nil { 243 t.Errorf("Load failed unexpectedly: %v", err) 244 } 245 if prog == nil { 246 t.Fatalf("Load returned a nil Program") 247 } 248 if got, want := created(prog), "P"; got != want { 249 t.Errorf("Created = %s, want %s", got, want) 250 } 251 } 252 253 func TestLoad_FromImports_Success(t *testing.T) { 254 var conf loader.Config 255 conf.ImportWithTests("fmt") 256 conf.ImportWithTests("errors") 257 258 prog, err := conf.Load() 259 if err != nil { 260 t.Errorf("Load failed unexpectedly: %v", err) 261 } 262 if prog == nil { 263 t.Fatalf("Load returned a nil Program") 264 } 265 if got, want := created(prog), "errors_test fmt_test"; got != want { 266 t.Errorf("Created = %q, want %s", got, want) 267 } 268 if got, want := imported(prog), "errors fmt"; got != want { 269 t.Errorf("Imported = %s, want %s", got, want) 270 } 271 // Check set of transitive packages. 272 // There are >30 and the set may grow over time, so only check a few. 273 want := map[string]bool{ 274 "strings": true, 275 "time": true, 276 "runtime": true, 277 "testing": true, 278 "unicode": true, 279 } 280 for _, path := range all(prog) { 281 delete(want, path) 282 } 283 if len(want) > 0 { 284 t.Errorf("AllPackages is missing these keys: %q", keys(want)) 285 } 286 } 287 288 func TestLoad_MissingIndirectImport(t *testing.T) { 289 pkgs := map[string]string{ 290 "a": `package a; import _ "b"`, 291 "b": `package b; import _ "c"`, 292 } 293 conf := loader.Config{Build: fakeContext(pkgs)} 294 conf.Import("a") 295 296 const wantErr = "couldn't load packages due to errors: b" 297 298 prog, err := conf.Load() 299 if err == nil { 300 t.Errorf("Load succeeded unexpectedly, want %q", wantErr) 301 } else if err.Error() != wantErr { 302 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) 303 } 304 if prog != nil { 305 t.Errorf("Load unexpectedly returned a Program") 306 } 307 } 308 309 func TestLoad_BadDependency_AllowErrors(t *testing.T) { 310 for _, test := range []struct { 311 descr string 312 pkgs map[string]string 313 wantPkgs string 314 }{ 315 316 { 317 descr: "missing dependency", 318 pkgs: map[string]string{ 319 "a": `package a; import _ "b"`, 320 "b": `package b; import _ "c"`, 321 }, 322 wantPkgs: "a b", 323 }, 324 { 325 descr: "bad package decl in dependency", 326 pkgs: map[string]string{ 327 "a": `package a; import _ "b"`, 328 "b": `package b; import _ "c"`, 329 "c": `package`, 330 }, 331 wantPkgs: "a b", 332 }, 333 { 334 descr: "parse error in dependency", 335 pkgs: map[string]string{ 336 "a": `package a; import _ "b"`, 337 "b": `package b; import _ "c"`, 338 "c": `package c; var x = `, 339 }, 340 wantPkgs: "a b c", 341 }, 342 } { 343 conf := loader.Config{ 344 AllowErrors: true, 345 Build: fakeContext(test.pkgs), 346 } 347 conf.Import("a") 348 349 prog, err := conf.Load() 350 if err != nil { 351 t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err) 352 } 353 if prog == nil { 354 t.Fatalf("%s: Load returned a nil Program", test.descr) 355 } 356 357 if got, want := imported(prog), "a"; got != want { 358 t.Errorf("%s: Imported = %s, want %s", test.descr, got, want) 359 } 360 if got := all(prog); strings.Join(got, " ") != test.wantPkgs { 361 t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs) 362 } 363 } 364 } 365 366 func TestCwd(t *testing.T) { 367 ctxt := fakeContext(map[string]string{"one/two/three": `package three`}) 368 for _, test := range []struct { 369 cwd, arg, want string 370 }{ 371 {cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"}, 372 {cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"}, 373 {cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"}, 374 {cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"}, 375 {cwd: "/go/src/one", arg: "two/three", want: ""}, 376 } { 377 conf := loader.Config{ 378 Cwd: test.cwd, 379 Build: ctxt, 380 } 381 conf.Import(test.arg) 382 383 var got string 384 prog, err := conf.Load() 385 if prog != nil { 386 got = imported(prog) 387 } 388 if got != test.want { 389 t.Errorf("Load(%s) from %s: Imported = %s, want %s", 390 test.arg, test.cwd, got, test.want) 391 if err != nil { 392 t.Errorf("Load failed: %v", err) 393 } 394 } 395 } 396 } 397 398 func TestLoad_vendor(t *testing.T) { 399 if buildutil.AllowVendor == 0 { 400 // Vendoring requires Go 1.6. 401 // TODO(adonovan): delete in due course. 402 t.Skip() 403 } 404 pkgs := map[string]string{ 405 "a": `package a; import _ "x"`, 406 "a/vendor": ``, // mkdir a/vendor 407 "a/vendor/x": `package xa`, 408 "b": `package b; import _ "x"`, 409 "b/vendor": ``, // mkdir b/vendor 410 "b/vendor/x": `package xb`, 411 "c": `package c; import _ "x"`, 412 "x": `package xc`, 413 } 414 conf := loader.Config{Build: fakeContext(pkgs)} 415 conf.Import("a") 416 conf.Import("b") 417 conf.Import("c") 418 419 prog, err := conf.Load() 420 if err != nil { 421 t.Fatal(err) 422 } 423 424 // Check that a, b, and c see different versions of x. 425 for _, r := range "abc" { 426 name := string(r) 427 got := prog.Package(name).Pkg.Imports()[0] 428 want := "x" + name 429 if got.Name() != want { 430 t.Errorf("package %s import %q = %s, want %s", 431 name, "x", got.Name(), want) 432 } 433 } 434 } 435 436 func TestVendorCwd(t *testing.T) { 437 if buildutil.AllowVendor == 0 { 438 // Vendoring requires Go 1.6. 439 // TODO(adonovan): delete in due course. 440 t.Skip() 441 } 442 // Test the interaction of cwd and vendor directories. 443 ctxt := fakeContext(map[string]string{ 444 "net": ``, // mkdir net 445 "net/http": `package http; import _ "hpack"`, 446 "vendor": ``, // mkdir vendor 447 "vendor/hpack": `package vendorhpack`, 448 "hpack": `package hpack`, 449 }) 450 for i, test := range []struct { 451 cwd, arg, want string 452 }{ 453 {cwd: "/go/src/net", arg: "http"}, // not found 454 {cwd: "/go/src/net", arg: "./http", want: "net/http vendor/hpack"}, 455 {cwd: "/go/src/net", arg: "hpack", want: "hpack"}, 456 {cwd: "/go/src/vendor", arg: "hpack", want: "hpack"}, 457 {cwd: "/go/src/vendor", arg: "./hpack", want: "vendor/hpack"}, 458 } { 459 conf := loader.Config{ 460 Cwd: test.cwd, 461 Build: ctxt, 462 } 463 conf.Import(test.arg) 464 465 var got string 466 prog, err := conf.Load() 467 if prog != nil { 468 got = strings.Join(all(prog), " ") 469 } 470 if got != test.want { 471 t.Errorf("#%d: Load(%s) from %s: got %s, want %s", 472 i, test.arg, test.cwd, got, test.want) 473 if err != nil { 474 t.Errorf("Load failed: %v", err) 475 } 476 } 477 } 478 } 479 480 // TODO(adonovan): more Load tests: 481 // 482 // failures: 483 // - to parse package decl of *_test.go files 484 // - to parse package decl of external *_test.go files 485 // - to parse whole of *_test.go files 486 // - to parse whole of external *_test.go files 487 // - to open a *.go file during import scanning 488 // - to import from binary 489 490 // features: 491 // - InitialPackages 492 // - PackageCreated hook 493 // - TypeCheckFuncBodies hook 494 495 func TestTransitivelyErrorFreeFlag(t *testing.T) { 496 // Create an minimal custom build.Context 497 // that fakes the following packages: 498 // 499 // a --> b --> c! c has an error 500 // \ d and e are transitively error-free. 501 // e --> d 502 // 503 // Each package [a-e] consists of one file, x.go. 504 pkgs := map[string]string{ 505 "a": `package a; import (_ "b"; _ "e")`, 506 "b": `package b; import _ "c"`, 507 "c": `package c; func f() { _ = int(false) }`, // type error within function body 508 "d": `package d;`, 509 "e": `package e; import _ "d"`, 510 } 511 conf := loader.Config{ 512 AllowErrors: true, 513 Build: fakeContext(pkgs), 514 } 515 conf.Import("a") 516 517 prog, err := conf.Load() 518 if err != nil { 519 t.Errorf("Load failed: %s", err) 520 } 521 if prog == nil { 522 t.Fatalf("Load returned nil *Program") 523 } 524 525 for pkg, info := range prog.AllPackages { 526 var wantErr, wantTEF bool 527 switch pkg.Path() { 528 case "a", "b": 529 case "c": 530 wantErr = true 531 case "d", "e": 532 wantTEF = true 533 default: 534 t.Errorf("unexpected package: %q", pkg.Path()) 535 continue 536 } 537 538 if (info.Errors != nil) != wantErr { 539 if wantErr { 540 t.Errorf("Package %q.Error = nil, want error", pkg.Path()) 541 } else { 542 t.Errorf("Package %q has unexpected Errors: %v", 543 pkg.Path(), info.Errors) 544 } 545 } 546 547 if info.TransitivelyErrorFree != wantTEF { 548 t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t", 549 pkg.Path(), info.TransitivelyErrorFree, wantTEF) 550 } 551 } 552 } 553 554 // Test that syntax (scan/parse), type, and loader errors are recorded 555 // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error). 556 func TestErrorReporting(t *testing.T) { 557 pkgs := map[string]string{ 558 "a": `package a; import (_ "b"; _ "c"); var x int = false`, 559 "b": `package b; 'syntax error!`, 560 } 561 conf := loader.Config{ 562 AllowErrors: true, 563 Build: fakeContext(pkgs), 564 } 565 var mu sync.Mutex 566 var allErrors []error 567 conf.TypeChecker.Error = func(err error) { 568 mu.Lock() 569 allErrors = append(allErrors, err) 570 mu.Unlock() 571 } 572 conf.Import("a") 573 574 prog, err := conf.Load() 575 if err != nil { 576 t.Errorf("Load failed: %s", err) 577 } 578 if prog == nil { 579 t.Fatalf("Load returned nil *Program") 580 } 581 582 // TODO(adonovan): test keys of ImportMap. 583 584 // Check errors recorded in each PackageInfo. 585 for pkg, info := range prog.AllPackages { 586 switch pkg.Path() { 587 case "a": 588 if !hasError(info.Errors, "cannot convert false") { 589 t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors) 590 } 591 if !hasError(info.Errors, "could not import c") { 592 t.Errorf("a.Errors = %v, want import (loader) error", info.Errors) 593 } 594 case "b": 595 if !hasError(info.Errors, "rune literal not terminated") { 596 t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors) 597 } 598 } 599 } 600 601 // Check errors reported via error handler. 602 if !hasError(allErrors, "cannot convert false") || 603 !hasError(allErrors, "rune literal not terminated") || 604 !hasError(allErrors, "could not import c") { 605 t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors) 606 } 607 } 608 609 func TestCycles(t *testing.T) { 610 for _, test := range []struct { 611 descr string 612 ctxt *build.Context 613 wantErr string 614 }{ 615 { 616 "self-cycle", 617 fakeContext(map[string]string{ 618 "main": `package main; import _ "selfcycle"`, 619 "selfcycle": `package selfcycle; import _ "selfcycle"`, 620 }), 621 `import cycle: selfcycle -> selfcycle`, 622 }, 623 { 624 "three-package cycle", 625 fakeContext(map[string]string{ 626 "main": `package main; import _ "a"`, 627 "a": `package a; import _ "b"`, 628 "b": `package b; import _ "c"`, 629 "c": `package c; import _ "a"`, 630 }), 631 `import cycle: c -> a -> b -> c`, 632 }, 633 { 634 "self-cycle in dependency of test file", 635 buildutil.FakeContext(map[string]map[string]string{ 636 "main": { 637 "main.go": `package main`, 638 "main_test.go": `package main; import _ "a"`, 639 }, 640 "a": { 641 "a.go": `package a; import _ "a"`, 642 }, 643 }), 644 `import cycle: a -> a`, 645 }, 646 // TODO(adonovan): fix: these fail 647 // { 648 // "two-package cycle in dependency of test file", 649 // buildutil.FakeContext(map[string]map[string]string{ 650 // "main": { 651 // "main.go": `package main`, 652 // "main_test.go": `package main; import _ "a"`, 653 // }, 654 // "a": { 655 // "a.go": `package a; import _ "main"`, 656 // }, 657 // }), 658 // `import cycle: main -> a -> main`, 659 // }, 660 // { 661 // "self-cycle in augmented package", 662 // buildutil.FakeContext(map[string]map[string]string{ 663 // "main": { 664 // "main.go": `package main`, 665 // "main_test.go": `package main; import _ "main"`, 666 // }, 667 // }), 668 // `import cycle: main -> main`, 669 // }, 670 } { 671 conf := loader.Config{ 672 AllowErrors: true, 673 Build: test.ctxt, 674 } 675 var mu sync.Mutex 676 var allErrors []error 677 conf.TypeChecker.Error = func(err error) { 678 mu.Lock() 679 allErrors = append(allErrors, err) 680 mu.Unlock() 681 } 682 conf.ImportWithTests("main") 683 684 prog, err := conf.Load() 685 if err != nil { 686 t.Errorf("%s: Load failed: %s", test.descr, err) 687 } 688 if prog == nil { 689 t.Fatalf("%s: Load returned nil *Program", test.descr) 690 } 691 692 if !hasError(allErrors, test.wantErr) { 693 t.Errorf("%s: Load() errors = %q, want %q", 694 test.descr, allErrors, test.wantErr) 695 } 696 } 697 698 // TODO(adonovan): 699 // - Test that in a legal test cycle, none of the symbols 700 // defined by augmentation are visible via import. 701 } 702 703 // ---- utilities ---- 704 705 // Simplifying wrapper around buildutil.FakeContext for single-file packages. 706 func fakeContext(pkgs map[string]string) *build.Context { 707 pkgs2 := make(map[string]map[string]string) 708 for path, content := range pkgs { 709 pkgs2[path] = map[string]string{"x.go": content} 710 } 711 return buildutil.FakeContext(pkgs2) 712 } 713 714 func hasError(errors []error, substr string) bool { 715 for _, err := range errors { 716 if strings.Contains(err.Error(), substr) { 717 return true 718 } 719 } 720 return false 721 } 722 723 func keys(m map[string]bool) (keys []string) { 724 for key := range m { 725 keys = append(keys, key) 726 } 727 sort.Strings(keys) 728 return 729 } 730 731 // Returns all loaded packages. 732 func all(prog *loader.Program) []string { 733 var pkgs []string 734 for _, info := range prog.AllPackages { 735 pkgs = append(pkgs, info.Pkg.Path()) 736 } 737 sort.Strings(pkgs) 738 return pkgs 739 } 740 741 // Returns initially imported packages, as a string. 742 func imported(prog *loader.Program) string { 743 var pkgs []string 744 for _, info := range prog.Imported { 745 pkgs = append(pkgs, info.Pkg.Path()) 746 } 747 sort.Strings(pkgs) 748 return strings.Join(pkgs, " ") 749 } 750 751 // Returns initially created packages, as a string. 752 func created(prog *loader.Program) string { 753 var pkgs []string 754 for _, info := range prog.Created { 755 pkgs = append(pkgs, info.Pkg.Path()) 756 } 757 return strings.Join(pkgs, " ") 758 }