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