github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/loader/loader14_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 // TODO(adonovan): more Load tests: 399 // 400 // failures: 401 // - to parse package decl of *_test.go files 402 // - to parse package decl of external *_test.go files 403 // - to parse whole of *_test.go files 404 // - to parse whole of external *_test.go files 405 // - to open a *.go file during import scanning 406 // - to import from binary 407 408 // features: 409 // - InitialPackages 410 // - PackageCreated hook 411 // - TypeCheckFuncBodies hook 412 413 func TestTransitivelyErrorFreeFlag(t *testing.T) { 414 // Create an minimal custom build.Context 415 // that fakes the following packages: 416 // 417 // a --> b --> c! c has an error 418 // \ d and e are transitively error-free. 419 // e --> d 420 // 421 // Each package [a-e] consists of one file, x.go. 422 pkgs := map[string]string{ 423 "a": `package a; import (_ "b"; _ "e")`, 424 "b": `package b; import _ "c"`, 425 "c": `package c; func f() { _ = int(false) }`, // type error within function body 426 "d": `package d;`, 427 "e": `package e; import _ "d"`, 428 } 429 conf := loader.Config{ 430 AllowErrors: true, 431 Build: fakeContext(pkgs), 432 } 433 conf.Import("a") 434 435 prog, err := conf.Load() 436 if err != nil { 437 t.Errorf("Load failed: %s", err) 438 } 439 if prog == nil { 440 t.Fatalf("Load returned nil *Program") 441 } 442 443 for pkg, info := range prog.AllPackages { 444 var wantErr, wantTEF bool 445 switch pkg.Path() { 446 case "a", "b": 447 case "c": 448 wantErr = true 449 case "d", "e": 450 wantTEF = true 451 default: 452 t.Errorf("unexpected package: %q", pkg.Path()) 453 continue 454 } 455 456 if (info.Errors != nil) != wantErr { 457 if wantErr { 458 t.Errorf("Package %q.Error = nil, want error", pkg.Path()) 459 } else { 460 t.Errorf("Package %q has unexpected Errors: %v", 461 pkg.Path(), info.Errors) 462 } 463 } 464 465 if info.TransitivelyErrorFree != wantTEF { 466 t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t", 467 pkg.Path(), info.TransitivelyErrorFree, wantTEF) 468 } 469 } 470 } 471 472 // Test that syntax (scan/parse), type, and loader errors are recorded 473 // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error). 474 func TestErrorReporting(t *testing.T) { 475 pkgs := map[string]string{ 476 "a": `package a; import (_ "b"; _ "c"); var x int = false`, 477 "b": `package b; 'syntax error!`, 478 } 479 conf := loader.Config{ 480 AllowErrors: true, 481 Build: fakeContext(pkgs), 482 } 483 var mu sync.Mutex 484 var allErrors []error 485 conf.TypeChecker.Error = func(err error) { 486 mu.Lock() 487 allErrors = append(allErrors, err) 488 mu.Unlock() 489 } 490 conf.Import("a") 491 492 prog, err := conf.Load() 493 if err != nil { 494 t.Errorf("Load failed: %s", err) 495 } 496 if prog == nil { 497 t.Fatalf("Load returned nil *Program") 498 } 499 500 // TODO(adonovan): test keys of ImportMap. 501 502 // Check errors recorded in each PackageInfo. 503 for pkg, info := range prog.AllPackages { 504 switch pkg.Path() { 505 case "a": 506 if !hasError(info.Errors, "cannot convert false") { 507 t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors) 508 } 509 if !hasError(info.Errors, "could not import c") { 510 t.Errorf("a.Errors = %v, want import (loader) error", info.Errors) 511 } 512 case "b": 513 if !hasError(info.Errors, "rune literal not terminated") { 514 t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors) 515 } 516 } 517 } 518 519 // Check errors reported via error handler. 520 if !hasError(allErrors, "cannot convert false") || 521 !hasError(allErrors, "rune literal not terminated") || 522 !hasError(allErrors, "could not import c") { 523 t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors) 524 } 525 } 526 527 func TestCycles(t *testing.T) { 528 for _, test := range []struct { 529 descr string 530 ctxt *build.Context 531 wantErr string 532 }{ 533 { 534 "self-cycle", 535 fakeContext(map[string]string{ 536 "main": `package main; import _ "selfcycle"`, 537 "selfcycle": `package selfcycle; import _ "selfcycle"`, 538 }), 539 `import cycle: selfcycle -> selfcycle`, 540 }, 541 { 542 "three-package cycle", 543 fakeContext(map[string]string{ 544 "main": `package main; import _ "a"`, 545 "a": `package a; import _ "b"`, 546 "b": `package b; import _ "c"`, 547 "c": `package c; import _ "a"`, 548 }), 549 `import cycle: c -> a -> b -> c`, 550 }, 551 { 552 "self-cycle in dependency of test file", 553 buildutil.FakeContext(map[string]map[string]string{ 554 "main": { 555 "main.go": `package main`, 556 "main_test.go": `package main; import _ "a"`, 557 }, 558 "a": { 559 "a.go": `package a; import _ "a"`, 560 }, 561 }), 562 `import cycle: a -> a`, 563 }, 564 // TODO(adonovan): fix: these fail 565 // { 566 // "two-package cycle in dependency of test file", 567 // buildutil.FakeContext(map[string]map[string]string{ 568 // "main": { 569 // "main.go": `package main`, 570 // "main_test.go": `package main; import _ "a"`, 571 // }, 572 // "a": { 573 // "a.go": `package a; import _ "main"`, 574 // }, 575 // }), 576 // `import cycle: main -> a -> main`, 577 // }, 578 // { 579 // "self-cycle in augmented package", 580 // buildutil.FakeContext(map[string]map[string]string{ 581 // "main": { 582 // "main.go": `package main`, 583 // "main_test.go": `package main; import _ "main"`, 584 // }, 585 // }), 586 // `import cycle: main -> main`, 587 // }, 588 } { 589 conf := loader.Config{ 590 AllowErrors: true, 591 Build: test.ctxt, 592 } 593 var mu sync.Mutex 594 var allErrors []error 595 conf.TypeChecker.Error = func(err error) { 596 mu.Lock() 597 allErrors = append(allErrors, err) 598 mu.Unlock() 599 } 600 conf.ImportWithTests("main") 601 602 prog, err := conf.Load() 603 if err != nil { 604 t.Errorf("%s: Load failed: %s", test.descr, err) 605 } 606 if prog == nil { 607 t.Fatalf("%s: Load returned nil *Program", test.descr) 608 } 609 610 if !hasError(allErrors, test.wantErr) { 611 t.Errorf("%s: Load() errors = %q, want %q", 612 test.descr, allErrors, test.wantErr) 613 } 614 } 615 616 // TODO(adonovan): 617 // - Test that in a legal test cycle, none of the symbols 618 // defined by augmentation are visible via import. 619 } 620 621 // ---- utilities ---- 622 623 // Simplifying wrapper around buildutil.FakeContext for single-file packages. 624 func fakeContext(pkgs map[string]string) *build.Context { 625 pkgs2 := make(map[string]map[string]string) 626 for path, content := range pkgs { 627 pkgs2[path] = map[string]string{"x.go": content} 628 } 629 return buildutil.FakeContext(pkgs2) 630 } 631 632 func hasError(errors []error, substr string) bool { 633 for _, err := range errors { 634 if strings.Contains(err.Error(), substr) { 635 return true 636 } 637 } 638 return false 639 } 640 641 func keys(m map[string]bool) (keys []string) { 642 for key := range m { 643 keys = append(keys, key) 644 } 645 sort.Strings(keys) 646 return 647 } 648 649 // Returns all loaded packages. 650 func all(prog *loader.Program) []string { 651 var pkgs []string 652 for _, info := range prog.AllPackages { 653 pkgs = append(pkgs, info.Pkg.Path()) 654 } 655 sort.Strings(pkgs) 656 return pkgs 657 } 658 659 // Returns initially imported packages, as a string. 660 func imported(prog *loader.Program) string { 661 var pkgs []string 662 for _, info := range prog.Imported { 663 pkgs = append(pkgs, info.Pkg.Path()) 664 } 665 sort.Strings(pkgs) 666 return strings.Join(pkgs, " ") 667 } 668 669 // Returns initially created packages, as a string. 670 func created(prog *loader.Program) string { 671 var pkgs []string 672 for _, info := range prog.Created { 673 pkgs = append(pkgs, info.Pkg.Path()) 674 } 675 return strings.Join(pkgs, " ") 676 }