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 5package regtest 6 7import ( 8 "context" 9 "fmt" 10 "log" 11 "os" 12 "testing" 13 14 "golang.org/x/tools/internal/lsp" 15 "golang.org/x/tools/internal/lsp/fake" 16 "golang.org/x/tools/internal/lsp/protocol" 17 "golang.org/x/tools/internal/lsp/tests" 18 "golang.org/x/tools/internal/testenv" 19) 20 21// Use mod.com for all go.mod files due to golang/go#35230. 22const exampleProgram = ` 23-- go.mod -- 24module mod.com 25 26go 1.12 27-- main.go -- 28package main 29 30import "fmt" 31 32func main() { 33 fmt.Println("Hello World.") 34}` 35 36func TestDiagnosticErrorInEditedFile(t *testing.T) { 37 // This test is very basic: start with a clean Go program, make an error, and 38 // get a diagnostic for that error. However, it also demonstrates how to 39 // combine Expectations to await more complex state in the editor. 40 runner.Run(t, exampleProgram, func(t *testing.T, env *Env) { 41 // Deleting the 'n' at the end of Println should generate a single error 42 // diagnostic. 43 env.OpenFile("main.go") 44 env.RegexpReplace("main.go", "Printl(n)", "") 45 env.Await( 46 // Once we have gotten diagnostics for the change above, we should 47 // satisfy the DiagnosticAtRegexp assertion. 48 OnceMet( 49 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 50 env.DiagnosticAtRegexp("main.go", "Printl"), 51 ), 52 // Assert that this test has sent no error logs to the client. This is not 53 // strictly necessary for testing this regression, but is included here 54 // as an example of using the NoErrorLogs() expectation. Feel free to 55 // delete. 56 NoErrorLogs(), 57 ) 58 }) 59} 60 61func TestMissingImportDiagsClearOnFirstFile(t *testing.T) { 62 const onlyMod = ` 63-- go.mod -- 64module mod.com 65 66go 1.12 67` 68 runner.Run(t, onlyMod, func(t *testing.T, env *Env) { 69 env.CreateBuffer("main.go", `package main 70 71func m() { 72 log.Println() 73} 74`) 75 env.Await( 76 env.DiagnosticAtRegexp("main.go", "log"), 77 ) 78 env.SaveBuffer("main.go") 79 env.Await( 80 EmptyDiagnostics("main.go"), 81 ) 82 }) 83} 84 85func TestDiagnosticErrorInNewFile(t *testing.T) { 86 const brokenFile = `package main 87 88const Foo = "abc 89` 90 runner.Run(t, brokenFile, func(t *testing.T, env *Env) { 91 env.CreateBuffer("broken.go", brokenFile) 92 env.Await(env.DiagnosticAtRegexp("broken.go", "\"abc")) 93 }) 94} 95 96// badPackage contains a duplicate definition of the 'a' const. 97const badPackage = ` 98-- go.mod -- 99module mod.com 100 101go 1.12 102-- a.go -- 103package consts 104 105const a = 1 106-- b.go -- 107package consts 108 109const a = 2 110` 111 112func TestDiagnosticClearingOnEdit(t *testing.T) { 113 runner.Run(t, badPackage, func(t *testing.T, env *Env) { 114 env.OpenFile("b.go") 115 env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2")) 116 117 // Fix the error by editing the const name in b.go to `b`. 118 env.RegexpReplace("b.go", "(a) = 2", "b") 119 env.Await( 120 EmptyDiagnostics("a.go"), 121 EmptyDiagnostics("b.go"), 122 ) 123 }) 124} 125 126func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) { 127 runner.Run(t, badPackage, func(t *testing.T, env *Env) { 128 env.OpenFile("a.go") 129 env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2")) 130 env.RemoveWorkspaceFile("b.go") 131 132 env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go")) 133 }) 134} 135 136func TestDiagnosticClearingOnClose(t *testing.T) { 137 runner.Run(t, badPackage, func(t *testing.T, env *Env) { 138 env.CreateBuffer("c.go", `package consts 139 140const a = 3`) 141 env.Await( 142 env.DiagnosticAtRegexp("a.go", "a = 1"), 143 env.DiagnosticAtRegexp("b.go", "a = 2"), 144 env.DiagnosticAtRegexp("c.go", "a = 3")) 145 env.CloseBuffer("c.go") 146 env.Await( 147 env.DiagnosticAtRegexp("a.go", "a = 1"), 148 env.DiagnosticAtRegexp("b.go", "a = 2"), 149 EmptyDiagnostics("c.go")) 150 }) 151} 152 153// Tests golang/go#37978. 154func TestIssue37978(t *testing.T) { 155 runner.Run(t, exampleProgram, func(t *testing.T, env *Env) { 156 // Create a new workspace-level directory and empty file. 157 env.CreateBuffer("c/c.go", "") 158 159 // Write the file contents with a missing import. 160 env.EditBuffer("c/c.go", fake.Edit{ 161 Text: `package c 162 163const a = http.MethodGet 164`, 165 }) 166 env.Await( 167 env.DiagnosticAtRegexp("c/c.go", "http.MethodGet"), 168 ) 169 // Save file, which will organize imports, adding the expected import. 170 // Expect the diagnostics to clear. 171 env.SaveBuffer("c/c.go") 172 env.Await( 173 EmptyDiagnostics("c/c.go"), 174 ) 175 }) 176} 177 178// Tests golang/go#38878: good a.go, bad a_test.go, remove a_test.go but its errors remain 179// If the file is open in the editor, this is working as intended 180// If the file is not open in the editor, the errors go away 181const test38878 = ` 182-- go.mod -- 183module foo 184 185go 1.12 186-- a.go -- 187package x 188 189// import "fmt" 190 191func f() {} 192 193-- a_test.go -- 194package x 195 196import "testing" 197 198func TestA(t *testing.T) { 199 f(3) 200} 201` 202 203// Tests golang/go#38878: deleting a test file should clear its errors, and 204// not break the workspace. 205func TestDeleteTestVariant(t *testing.T) { 206 runner.Run(t, test38878, func(t *testing.T, env *Env) { 207 env.Await(env.DiagnosticAtRegexp("a_test.go", `f\((3)\)`)) 208 env.RemoveWorkspaceFile("a_test.go") 209 env.Await(EmptyDiagnostics("a_test.go")) 210 211 // Make sure the test variant has been removed from the workspace by 212 // triggering a metadata load. 213 env.OpenFile("a.go") 214 env.RegexpReplace("a.go", `// import`, "import") 215 env.Await(env.DiagnosticAtRegexp("a.go", `"fmt"`)) 216 }) 217} 218 219// Tests golang/go#38878: deleting a test file on disk while it's still open 220// should not clear its errors. 221func TestDeleteTestVariant_DiskOnly(t *testing.T) { 222 log.SetFlags(log.Lshortfile) 223 runner.Run(t, test38878, func(t *testing.T, env *Env) { 224 env.OpenFile("a_test.go") 225 env.Await(DiagnosticAt("a_test.go", 5, 3)) 226 env.Sandbox.Workdir.RemoveFile(context.Background(), "a_test.go") 227 env.Await(OnceMet( 228 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1), 229 DiagnosticAt("a_test.go", 5, 3))) 230 }) 231} 232 233// TestNoMod confirms that gopls continues to work when a user adds a go.mod 234// file to their workspace. 235func TestNoMod(t *testing.T) { 236 const noMod = ` 237-- main.go -- 238package main 239 240import "mod.com/bob" 241 242func main() { 243 bob.Hello() 244} 245-- bob/bob.go -- 246package bob 247 248func Hello() { 249 var x int 250} 251` 252 253 t.Run("manual", func(t *testing.T) { 254 runner.Run(t, noMod, func(t *testing.T, env *Env) { 255 env.Await( 256 env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`), 257 ) 258 env.CreateBuffer("go.mod", `module mod.com 259 260 go 1.12 261`) 262 env.SaveBuffer("go.mod") 263 env.Await( 264 EmptyDiagnostics("main.go"), 265 ) 266 var d protocol.PublishDiagnosticsParams 267 env.Await( 268 OnceMet( 269 env.DiagnosticAtRegexp("bob/bob.go", "x"), 270 ReadDiagnostics("bob/bob.go", &d), 271 ), 272 ) 273 if len(d.Diagnostics) != 1 { 274 t.Fatalf("expected 1 diagnostic, got %v", len(d.Diagnostics)) 275 } 276 }) 277 }) 278 t.Run("initialized", func(t *testing.T) { 279 runner.Run(t, noMod, func(t *testing.T, env *Env) { 280 env.Await( 281 env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`), 282 ) 283 env.RunGoCommand("mod", "init", "mod.com") 284 env.Await( 285 EmptyDiagnostics("main.go"), 286 env.DiagnosticAtRegexp("bob/bob.go", "x"), 287 ) 288 }) 289 }) 290 291 t.Run("without workspace module", func(t *testing.T) { 292 withOptions( 293 Modes(Singleton), 294 ).run(t, noMod, func(t *testing.T, env *Env) { 295 env.Await( 296 env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`), 297 ) 298 if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}); err != nil { 299 t.Fatal(err) 300 } 301 env.Await( 302 EmptyDiagnostics("main.go"), 303 env.DiagnosticAtRegexp("bob/bob.go", "x"), 304 ) 305 }) 306 }) 307} 308 309// Tests golang/go#38267. 310func TestIssue38267(t *testing.T) { 311 const testPackage = ` 312-- go.mod -- 313module mod.com 314 315go 1.12 316-- lib.go -- 317package lib 318 319func Hello(x string) { 320 _ = x 321} 322-- lib_test.go -- 323package lib 324 325import "testing" 326 327type testStruct struct{ 328 name string 329} 330 331func TestHello(t *testing.T) { 332 testStructs := []*testStruct{ 333 &testStruct{"hello"}, 334 &testStruct{"goodbye"}, 335 } 336 for y := range testStructs { 337 _ = y 338 } 339} 340` 341 342 runner.Run(t, testPackage, func(t *testing.T, env *Env) { 343 env.OpenFile("lib_test.go") 344 env.Await( 345 DiagnosticAt("lib_test.go", 10, 2), 346 DiagnosticAt("lib_test.go", 11, 2), 347 ) 348 env.OpenFile("lib.go") 349 env.RegexpReplace("lib.go", "_ = x", "var y int") 350 env.Await( 351 env.DiagnosticAtRegexp("lib.go", "y int"), 352 EmptyDiagnostics("lib_test.go"), 353 ) 354 }) 355} 356 357// Tests golang/go#38328. 358func TestPackageChange_Issue38328(t *testing.T) { 359 const packageChange = ` 360-- go.mod -- 361module fake 362 363go 1.12 364-- a.go -- 365package foo 366func main() {} 367` 368 runner.Run(t, packageChange, func(t *testing.T, env *Env) { 369 env.OpenFile("a.go") 370 env.RegexpReplace("a.go", "foo", "foox") 371 env.Await( 372 // When the bug reported in #38328 was present, we didn't get erroneous 373 // file diagnostics until after the didChange message generated by the 374 // package renaming was fully processed. Therefore, in order for this 375 // test to actually exercise the bug, we must wait until that work has 376 // completed. 377 OnceMet( 378 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 379 NoDiagnostics("a.go"), 380 ), 381 ) 382 }) 383} 384 385const testPackageWithRequire = ` 386-- go.mod -- 387module mod.com 388 389go 1.12 390 391require foo.test v1.2.3 392-- go.sum -- 393foo.test v1.2.3 h1:TMA+lyd1ck0TqjSFpNe4T6cf/K6TYkoHwOOcMBMjaEw= 394foo.test v1.2.3/go.mod h1:Ij3kyLIe5lzjycjh13NL8I2gX0quZuTdW0MnmlwGBL4= 395-- print.go -- 396package lib 397 398import ( 399 "fmt" 400 401 "foo.test/bar" 402) 403 404func PrintAnswer() { 405 fmt.Printf("answer: %s", bar.Answer) 406} 407` 408 409const testPackageWithRequireProxy = ` 410-- foo.test@v1.2.3/go.mod -- 411module foo.test 412 413go 1.12 414-- foo.test@v1.2.3/bar/const.go -- 415package bar 416 417const Answer = 42 418` 419 420func TestResolveDiagnosticWithDownload(t *testing.T) { 421 runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) { 422 env.OpenFile("print.go") 423 // Check that gopackages correctly loaded this dependency. We should get a 424 // diagnostic for the wrong formatting type. 425 // TODO: we should be able to easily also match the diagnostic message. 426 env.Await(env.DiagnosticAtRegexp("print.go", "fmt.Printf")) 427 }, ProxyFiles(testPackageWithRequireProxy)) 428} 429 430func TestMissingDependency(t *testing.T) { 431 runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) { 432 env.OpenFile("print.go") 433 env.Await(LogMatching(protocol.Error, "initial workspace load failed", 1)) 434 }) 435} 436 437// Tests golang/go#36951. 438func TestAdHocPackages_Issue36951(t *testing.T) { 439 const adHoc = ` 440-- b/b.go -- 441package b 442 443func Hello() { 444 var x int 445} 446` 447 runner.Run(t, adHoc, func(t *testing.T, env *Env) { 448 env.OpenFile("b/b.go") 449 env.Await(env.DiagnosticAtRegexp("b/b.go", "x")) 450 }) 451} 452 453// Tests golang/go#37984: GOPATH should be read from the go command. 454func TestNoGOPATH_Issue37984(t *testing.T) { 455 const files = ` 456-- main.go -- 457package main 458 459func _() { 460 fmt.Println("Hello World") 461} 462` 463 withOptions( 464 EditorConfig{ 465 Env: map[string]string{ 466 "GOPATH": "", 467 "GO111MODULE": "off", 468 }, 469 }).run(t, files, func(t *testing.T, env *Env) { 470 env.OpenFile("main.go") 471 env.Await(env.DiagnosticAtRegexp("main.go", "fmt")) 472 env.SaveBuffer("main.go") 473 env.Await(EmptyDiagnostics("main.go")) 474 }) 475} 476 477// Tests golang/go#38669. 478func TestEqualInEnv_Issue38669(t *testing.T) { 479 const files = ` 480-- go.mod -- 481module mod.com 482 483go 1.12 484-- main.go -- 485package main 486 487var _ = x.X 488-- x/x.go -- 489package x 490 491var X = 0 492` 493 editorConfig := EditorConfig{Env: map[string]string{"GOFLAGS": "-tags=foo"}} 494 withOptions(editorConfig).run(t, files, func(t *testing.T, env *Env) { 495 env.OpenFile("main.go") 496 env.OrganizeImports("main.go") 497 env.Await(EmptyDiagnostics("main.go")) 498 }) 499} 500 501// Tests golang/go#38467. 502func TestNoSuggestedFixesForGeneratedFiles_Issue38467(t *testing.T) { 503 const generated = ` 504-- go.mod -- 505module mod.com 506 507go 1.12 508-- main.go -- 509package main 510 511// Code generated by generator.go. DO NOT EDIT. 512 513func _() { 514 for i, _ := range []string{} { 515 _ = i 516 } 517} 518` 519 runner.Run(t, generated, func(t *testing.T, env *Env) { 520 env.OpenFile("main.go") 521 original := env.ReadWorkspaceFile("main.go") 522 var d protocol.PublishDiagnosticsParams 523 env.Await( 524 OnceMet( 525 DiagnosticAt("main.go", 5, 8), 526 ReadDiagnostics("main.go", &d), 527 ), 528 ) 529 // Apply fixes and save the buffer. 530 env.ApplyQuickFixes("main.go", d.Diagnostics) 531 env.SaveBuffer("main.go") 532 fixed := env.ReadWorkspaceFile("main.go") 533 if original != fixed { 534 t.Fatalf("generated file was changed by quick fixes:\n%s", tests.Diff(t, original, fixed)) 535 } 536 }) 537} 538 539// Expect a module/GOPATH error if there is an error in the file at startup. 540// Tests golang/go#37279. 541func TestShowCriticalError_Issue37279(t *testing.T) { 542 const noModule = ` 543-- a.go -- 544package foo 545 546import "mod.com/hello" 547 548func f() { 549 hello.Goodbye() 550} 551` 552 runner.Run(t, noModule, func(t *testing.T, env *Env) { 553 env.OpenFile("a.go") 554 env.Await( 555 OutstandingWork(lsp.WorkspaceLoadFailure, "outside of a module"), 556 ) 557 env.RegexpReplace("a.go", `import "mod.com/hello"`, "") 558 env.Await( 559 NoOutstandingWork(), 560 ) 561 }) 562} 563 564func TestNonGoFolder(t *testing.T) { 565 const files = ` 566-- hello.txt -- 567hi mom 568` 569 for _, go111module := range []string{"on", "off", ""} { 570 t.Run(fmt.Sprintf("GO111MODULE_%v", go111module), func(t *testing.T) { 571 withOptions(EditorConfig{ 572 Env: map[string]string{"GO111MODULE": go111module}, 573 }).run(t, files, func(t *testing.T, env *Env) { 574 env.OpenFile("hello.txt") 575 env.Await( 576 OnceMet( 577 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1), 578 NoShowMessage(), 579 ), 580 ) 581 }) 582 }) 583 } 584} 585 586// Tests golang/go#38602. 587func TestNonexistentFileDiagnostics_Issue38602(t *testing.T) { 588 const collision = ` 589-- x/x.go -- 590package x 591 592import "x/hello" 593 594func Hello() { 595 hello.HiThere() 596} 597-- x/main.go -- 598package main 599 600func main() { 601 fmt.Println("") 602} 603` 604 runner.Run(t, collision, func(t *testing.T, env *Env) { 605 env.OpenFile("x/main.go") 606 env.Await( 607 env.DiagnosticAtRegexp("x/main.go", "fmt.Println"), 608 ) 609 env.OrganizeImports("x/main.go") 610 // span.Parse misparses the error message when multiple packages are 611 // defined in the same directory, creating a garbage filename. 612 // Previously, we would send diagnostics for this nonexistent file. 613 // This test checks that we don't send diagnostics for this file. 614 dir, err := os.Getwd() 615 if err != nil { 616 t.Fatal(err) 617 } 618 badFile := fmt.Sprintf("%s/found packages main (main.go) and x (x.go) in %s/src/x", dir, env.Sandbox.GOPATH()) 619 env.Await( 620 OnceMet( 621 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 622 EmptyDiagnostics("x/main.go"), 623 ), 624 NoDiagnostics(badFile), 625 ) 626 }, InGOPATH()) 627} 628 629const ardanLabsProxy = ` 630-- github.com/ardanlabs/conf@v1.2.3/go.mod -- 631module github.com/ardanlabs/conf 632 633go 1.12 634-- github.com/ardanlabs/conf@v1.2.3/conf.go -- 635package conf 636 637var ErrHelpWanted error 638` 639 640// Test for golang/go#38211. 641func Test_Issue38211(t *testing.T) { 642 testenv.NeedsGo1Point(t, 14) 643 const ardanLabs = ` 644-- go.mod -- 645module mod.com 646 647go 1.14 648-- main.go -- 649package main 650 651import "github.com/ardanlabs/conf" 652 653func main() { 654 _ = conf.ErrHelpWanted 655} 656` 657 withOptions( 658 ProxyFiles(ardanLabsProxy), 659 ).run(t, ardanLabs, func(t *testing.T, env *Env) { 660 // Expect a diagnostic with a suggested fix to add 661 // "github.com/ardanlabs/conf" to the go.mod file. 662 env.OpenFile("go.mod") 663 env.OpenFile("main.go") 664 var d protocol.PublishDiagnosticsParams 665 env.Await( 666 OnceMet( 667 env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`), 668 ReadDiagnostics("main.go", &d), 669 ), 670 ) 671 env.ApplyQuickFixes("main.go", d.Diagnostics) 672 env.SaveBuffer("go.mod") 673 env.Await( 674 EmptyDiagnostics("main.go"), 675 ) 676 // Comment out the line that depends on conf and expect a 677 // diagnostic and a fix to remove the import. 678 env.RegexpReplace("main.go", "_ = conf.ErrHelpWanted", "//_ = conf.ErrHelpWanted") 679 env.Await( 680 env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`), 681 ) 682 env.SaveBuffer("main.go") 683 // Expect a diagnostic and fix to remove the dependency in the go.mod. 684 env.Await(EmptyDiagnostics("main.go")) 685 env.Await( 686 OnceMet( 687 env.DiagnosticAtRegexp("go.mod", "require github.com/ardanlabs/conf"), 688 ReadDiagnostics("go.mod", &d), 689 ), 690 ) 691 env.ApplyQuickFixes("go.mod", d.Diagnostics) 692 env.SaveBuffer("go.mod") 693 env.Await( 694 EmptyDiagnostics("go.mod"), 695 ) 696 // Uncomment the lines and expect a new diagnostic for the import. 697 env.RegexpReplace("main.go", "//_ = conf.ErrHelpWanted", "_ = conf.ErrHelpWanted") 698 env.SaveBuffer("main.go") 699 env.Await( 700 env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`), 701 ) 702 }) 703} 704 705// Test for golang/go#38207. 706func TestNewModule_Issue38207(t *testing.T) { 707 testenv.NeedsGo1Point(t, 14) 708 const emptyFile = ` 709-- go.mod -- 710module mod.com 711 712go 1.12 713-- main.go -- 714` 715 withOptions( 716 ProxyFiles(ardanLabsProxy), 717 ).run(t, emptyFile, func(t *testing.T, env *Env) { 718 env.OpenFile("main.go") 719 env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main 720 721import "github.com/ardanlabs/conf" 722 723func main() { 724 _ = conf.ErrHelpWanted 725} 726`)) 727 env.SaveBuffer("main.go") 728 var d protocol.PublishDiagnosticsParams 729 env.Await( 730 OnceMet( 731 env.DiagnosticAtRegexpWithMessage("main.go", `"github.com/ardanlabs/conf"`, "your go.mod file"), 732 ReadDiagnostics("main.go", &d), 733 ), 734 ) 735 env.ApplyQuickFixes("main.go", d.Diagnostics) 736 env.CheckForFileChanges() 737 env.Await( 738 EmptyDiagnostics("main.go"), 739 ) 740 }) 741} 742 743// Test for golang/go#36960. 744func TestNewFileBadImports_Issue36960(t *testing.T) { 745 testenv.NeedsGo1Point(t, 14) 746 const simplePackage = ` 747-- go.mod -- 748module mod.com 749 750go 1.14 751-- a/a1.go -- 752package a 753 754import "fmt" 755 756func _() { 757 fmt.Println("hi") 758} 759` 760 runner.Run(t, simplePackage, func(t *testing.T, env *Env) { 761 env.OpenFile("a/a1.go") 762 env.CreateBuffer("a/a2.go", ``) 763 env.SaveBufferWithoutActions("a/a2.go") 764 env.Await( 765 OnceMet( 766 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1), 767 NoDiagnostics("a/a1.go"), 768 ), 769 ) 770 env.EditBuffer("a/a2.go", fake.NewEdit(0, 0, 0, 0, `package a`)) 771 env.Await( 772 OnceMet( 773 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 774 NoDiagnostics("a/a1.go"), 775 ), 776 ) 777 }) 778} 779 780// This test tries to replicate the workflow of a user creating a new x test. 781// It also tests golang/go#39315. 782func TestManuallyCreatingXTest(t *testing.T) { 783 // Only for 1.15 because of golang/go#37971. 784 testenv.NeedsGo1Point(t, 15) 785 786 // Create a package that already has a test variant (in-package test). 787 const testVariant = ` 788-- go.mod -- 789module mod.com 790 791go 1.15 792-- hello/hello.go -- 793package hello 794 795func Hello() { 796 var x int 797} 798-- hello/hello_test.go -- 799package hello 800 801import "testing" 802 803func TestHello(t *testing.T) { 804 var x int 805 Hello() 806} 807` 808 runner.Run(t, testVariant, func(t *testing.T, env *Env) { 809 // Open the file, triggering the workspace load. 810 // There are errors in the code to ensure all is working as expected. 811 env.OpenFile("hello/hello.go") 812 env.Await( 813 env.DiagnosticAtRegexp("hello/hello.go", "x"), 814 env.DiagnosticAtRegexp("hello/hello_test.go", "x"), 815 ) 816 817 // Create an empty file with the intention of making it an x test. 818 // This resembles a typical flow in an editor like VS Code, in which 819 // a user would create an empty file and add content, saving 820 // intermittently. 821 // TODO(rstambler): There might be more edge cases here, as file 822 // content can be added incrementally. 823 env.CreateBuffer("hello/hello_x_test.go", ``) 824 825 // Save the empty file (no actions since formatting will fail). 826 env.SaveBufferWithoutActions("hello/hello_x_test.go") 827 828 // Add the content. The missing import is for the package under test. 829 env.EditBuffer("hello/hello_x_test.go", fake.NewEdit(0, 0, 0, 0, `package hello_test 830 831import ( 832 "testing" 833) 834 835func TestHello(t *testing.T) { 836 hello.Hello() 837} 838`)) 839 // Expect a diagnostic for the missing import. Save, which should 840 // trigger import organization. The diagnostic should clear. 841 env.Await( 842 env.DiagnosticAtRegexp("hello/hello_x_test.go", "hello.Hello"), 843 ) 844 env.SaveBuffer("hello/hello_x_test.go") 845 env.Await( 846 EmptyDiagnostics("hello/hello_x_test.go"), 847 ) 848 }) 849} 850 851// Reproduce golang/go#40690. 852func TestCreateOnlyXTest(t *testing.T) { 853 testenv.NeedsGo1Point(t, 13) 854 855 const mod = ` 856-- go.mod -- 857module mod.com 858 859go 1.12 860-- foo/foo.go -- 861package foo 862-- foo/bar_test.go -- 863` 864 run(t, mod, func(t *testing.T, env *Env) { 865 env.OpenFile("foo/bar_test.go") 866 env.EditBuffer("foo/bar_test.go", fake.NewEdit(0, 0, 0, 0, "package foo")) 867 env.Await( 868 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 869 ) 870 env.RegexpReplace("foo/bar_test.go", "package foo", `package foo_test 871 872import "testing" 873 874func TestX(t *testing.T) { 875 var x int 876} 877`) 878 env.Await( 879 env.DiagnosticAtRegexp("foo/bar_test.go", "x"), 880 ) 881 }) 882} 883 884func TestChangePackageName(t *testing.T) { 885 t.Skip("This issue hasn't been fixed yet. See golang.org/issue/41061.") 886 887 const mod = ` 888-- go.mod -- 889module mod.com 890 891go 1.12 892-- foo/foo.go -- 893package foo 894-- foo/bar_test.go -- 895package foo_ 896` 897 run(t, mod, func(t *testing.T, env *Env) { 898 env.OpenFile("foo/bar_test.go") 899 env.RegexpReplace("foo/bar_test.go", "package foo_", "package foo_test") 900 env.SaveBuffer("foo/bar_test.go") 901 env.Await( 902 OnceMet( 903 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1), 904 NoDiagnostics("foo/bar_test.go"), 905 ), 906 OnceMet( 907 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1), 908 NoDiagnostics("foo/foo.go"), 909 ), 910 ) 911 }) 912} 913 914// Reproduces golang/go#40825. 915func TestEmptyGOPATHXTest_40825(t *testing.T) { 916 const files = ` 917-- x.go -- 918package x 919-- x_test.go -- 920` 921 922 withOptions(InGOPATH()).run(t, files, func(t *testing.T, env *Env) { 923 env.OpenFile("x_test.go") 924 env.EditBuffer("x_test.go", fake.NewEdit(0, 0, 0, 0, "pack")) 925 env.Await( 926 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 927 NoShowMessage(), 928 ) 929 }) 930} 931 932func TestIgnoredFiles(t *testing.T) { 933 const ws = ` 934-- go.mod -- 935module mod.com 936 937go 1.12 938-- _foo/x.go -- 939package x 940 941var _ = foo.Bar 942` 943 runner.Run(t, ws, func(t *testing.T, env *Env) { 944 env.OpenFile("_foo/x.go") 945 env.Await( 946 OnceMet( 947 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1), 948 NoDiagnostics("_foo/x.go"), 949 )) 950 }) 951} 952 953// Partially reproduces golang/go#38977, moving a file between packages. 954// It also gets hit by some go command bug fixed in 1.15, but we don't 955// care about that so much here. 956func TestDeletePackage(t *testing.T) { 957 const ws = ` 958-- go.mod -- 959module mod.com 960 961go 1.15 962-- a/a.go -- 963package a 964 965const A = 1 966 967-- b/b.go -- 968package b 969 970import "mod.com/a" 971 972const B = a.A 973 974-- c/c.go -- 975package c 976 977import "mod.com/a" 978 979const C = a.A 980` 981 runner.Run(t, ws, func(t *testing.T, env *Env) { 982 env.OpenFile("b/b.go") 983 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1)) 984 // Delete c/c.go, the only file in package c. 985 env.RemoveWorkspaceFile("c/c.go") 986 987 // We should still get diagnostics for files that exist. 988 env.RegexpReplace("b/b.go", `a.A`, "a.Nonexistant") 989 env.Await(env.DiagnosticAtRegexp("b/b.go", `Nonexistant`)) 990 }) 991} 992 993// This is a copy of the scenario_default/quickfix_empty_files.txt test from 994// govim. Reproduces golang/go#39646. 995func TestQuickFixEmptyFiles(t *testing.T) { 996 testenv.NeedsGo1Point(t, 15) 997 998 const mod = ` 999-- go.mod -- 1000module mod.com 1001 1002go 1.12 1003` 1004 // To fully recreate the govim tests, we create files by inserting 1005 // a newline, adding to the file, and then deleting the newline. 1006 // Wait for each event to process to avoid cancellations and force 1007 // package loads. 1008 writeGoVim := func(env *Env, name, content string) { 1009 env.WriteWorkspaceFile(name, "") 1010 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1)) 1011 1012 env.CreateBuffer(name, "\n") 1013 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1)) 1014 1015 env.EditBuffer(name, fake.NewEdit(1, 0, 1, 0, content)) 1016 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1)) 1017 1018 env.EditBuffer(name, fake.NewEdit(0, 0, 1, 0, "")) 1019 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1)) 1020 } 1021 1022 const p = `package p; func DoIt(s string) {};` 1023 const main = `package main 1024 1025import "mod.com/p" 1026 1027func main() { 1028 p.DoIt(5) 1029} 1030` 1031 // A simple version of the test that reproduces most of the problems it 1032 // exposes. 1033 t.Run("short", func(t *testing.T) { 1034 runner.Run(t, mod, func(t *testing.T, env *Env) { 1035 writeGoVim(env, "p/p.go", p) 1036 writeGoVim(env, "main.go", main) 1037 env.Await(env.DiagnosticAtRegexp("main.go", "5")) 1038 }) 1039 }) 1040 1041 // A full version that replicates the whole flow of the test. 1042 t.Run("full", func(t *testing.T) { 1043 runner.Run(t, mod, func(t *testing.T, env *Env) { 1044 writeGoVim(env, "p/p.go", p) 1045 writeGoVim(env, "main.go", main) 1046 writeGoVim(env, "p/p_test.go", `package p 1047 1048import "testing" 1049 1050func TestDoIt(t *testing.T) { 1051 DoIt(5) 1052} 1053`) 1054 writeGoVim(env, "p/x_test.go", `package p_test 1055 1056import ( 1057 "testing" 1058 1059 "mod.com/p" 1060) 1061 1062func TestDoIt(t *testing.T) { 1063 p.DoIt(5) 1064} 1065`) 1066 env.Await( 1067 env.DiagnosticAtRegexp("main.go", "5"), 1068 env.DiagnosticAtRegexp("p/p_test.go", "5"), 1069 env.DiagnosticAtRegexp("p/x_test.go", "5"), 1070 ) 1071 env.RegexpReplace("p/p.go", "s string", "i int") 1072 env.Await( 1073 EmptyDiagnostics("main.go"), 1074 EmptyDiagnostics("p/p_test.go"), 1075 EmptyDiagnostics("p/x_test.go"), 1076 ) 1077 }) 1078 }) 1079} 1080 1081func TestSingleFile(t *testing.T) { 1082 const mod = ` 1083-- go.mod -- 1084module mod.com 1085 1086go 1.13 1087-- a/a.go -- 1088package a 1089 1090func _() { 1091 var x int 1092} 1093` 1094 withOptions( 1095 // Empty workspace folders. 1096 WorkspaceFolders(), 1097 ).run(t, mod, func(t *testing.T, env *Env) { 1098 env.OpenFile("a/a.go") 1099 env.Await( 1100 env.DiagnosticAtRegexp("a/a.go", "x"), 1101 ) 1102 }) 1103} 1104 1105// Reproduces the case described in 1106// https://github.com/golang/go/issues/39296#issuecomment-652058883. 1107func TestPkgm(t *testing.T) { 1108 const basic = ` 1109-- go.mod -- 1110module mod.com 1111 1112go 1.15 1113-- foo/foo.go -- 1114package foo 1115 1116import "fmt" 1117 1118func Foo() { 1119 fmt.Println("") 1120} 1121` 1122 runner.Run(t, basic, func(t *testing.T, env *Env) { 1123 testenv.NeedsGo1Point(t, 15) 1124 1125 env.WriteWorkspaceFile("foo/foo_test.go", `package main 1126 1127func main() { 1128 1129}`) 1130 env.OpenFile("foo/foo_test.go") 1131 env.RegexpReplace("foo/foo_test.go", `package main`, `package foo`) 1132 env.Await( 1133 OnceMet( 1134 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 1135 NoDiagnostics("foo/foo.go"), 1136 ), 1137 ) 1138 }) 1139} 1140 1141func TestClosingBuffer(t *testing.T) { 1142 const basic = ` 1143-- go.mod -- 1144module mod.com 1145 1146go 1.14 1147-- main.go -- 1148package main 1149 1150func main() {} 1151` 1152 runner.Run(t, basic, func(t *testing.T, env *Env) { 1153 env.Editor.CreateBuffer(env.Ctx, "foo.go", `package main`) 1154 env.Await( 1155 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1), 1156 ) 1157 env.CloseBuffer("foo.go") 1158 env.Await( 1159 OnceMet( 1160 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidClose), 1), 1161 NoLogMatching(protocol.Info, "packages=0"), 1162 ), 1163 ) 1164 }) 1165} 1166 1167// Reproduces golang/go#38424. 1168func TestCutAndPaste(t *testing.T) { 1169 const basic = ` 1170-- go.mod -- 1171module mod.com 1172 1173go 1.14 1174-- main2.go -- 1175package main 1176` 1177 runner.Run(t, basic, func(t *testing.T, env *Env) { 1178 env.CreateBuffer("main.go", "") 1179 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1)) 1180 1181 env.SaveBufferWithoutActions("main.go") 1182 env.Await( 1183 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1), 1184 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1), 1185 ) 1186 1187 env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main 1188 1189func main() { 1190} 1191`)) 1192 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1)) 1193 1194 env.SaveBuffer("main.go") 1195 env.Await( 1196 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 2), 1197 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 2), 1198 ) 1199 1200 env.EditBuffer("main.go", fake.NewEdit(0, 0, 4, 0, "")) 1201 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 2)) 1202 1203 env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main 1204 1205func main() { 1206 var x int 1207} 1208`)) 1209 env.Await( 1210 env.DiagnosticAtRegexp("main.go", "x"), 1211 ) 1212 }) 1213} 1214 1215// Reproduces golang/go#39763. 1216func TestInvalidPackageName(t *testing.T) { 1217 testenv.NeedsGo1Point(t, 15) 1218 1219 const pkgDefault = ` 1220-- go.mod -- 1221module mod.com 1222 1223go 1.12 1224-- main.go -- 1225package default 1226 1227func main() {} 1228` 1229 runner.Run(t, pkgDefault, func(t *testing.T, env *Env) { 1230 env.OpenFile("main.go") 1231 env.Await( 1232 env.DiagnosticAtRegexp("main.go", "default"), 1233 ) 1234 }) 1235} 1236 1237// This tests the functionality of the "limitWorkspaceScope" 1238func TestLimitWorkspaceScope(t *testing.T) { 1239 const mod = ` 1240-- go.mod -- 1241module mod.com 1242 1243go 1.12 1244-- a/main.go -- 1245package main 1246 1247func main() {} 1248-- main.go -- 1249package main 1250 1251func main() { 1252 var x int 1253} 1254` 1255 withOptions( 1256 WorkspaceFolders("a"), 1257 ).run(t, mod, func(t *testing.T, env *Env) { 1258 env.OpenFile("a/main.go") 1259 env.Await( 1260 env.DiagnosticAtRegexp("main.go", "x"), 1261 ) 1262 }) 1263 withOptions( 1264 WorkspaceFolders("a"), 1265 LimitWorkspaceScope(), 1266 ).run(t, mod, func(t *testing.T, env *Env) { 1267 env.OpenFile("a/main.go") 1268 env.Await( 1269 NoDiagnostics("main.go"), 1270 ) 1271 }) 1272} 1273 1274func TestStaticcheckDiagnostic(t *testing.T) { 1275 const files = ` 1276-- go.mod -- 1277module mod.com 1278 1279go 1.12 1280-- main.go -- 1281package main 1282 1283import "fmt" 1284 1285type t struct { 1286 msg string 1287} 1288 1289func main() { 1290 x := []t{t{"msg"}} 1291 fmt.Println(x) 1292} 1293` 1294 1295 withOptions( 1296 EditorConfig{EnableStaticcheck: true}, 1297 ).run(t, files, func(t *testing.T, env *Env) { 1298 env.OpenFile("main.go") 1299 // Staticcheck should generate a diagnostic to simplify this literal. 1300 env.Await(env.DiagnosticAtRegexp("main.go", `t{"msg"}`)) 1301 }) 1302} 1303 1304// Test some secondary diagnostics 1305func TestSecondaryDiagnostics(t *testing.T) { 1306 const dir = ` 1307-- go.mod -- 1308module mod.com 1309 1310go 1.12 1311-- main.go -- 1312package main 1313func main() { 1314 panic("not here") 1315} 1316-- other.go -- 1317package main 1318func main() {} 1319` 1320 runner.Run(t, dir, func(t *testing.T, env *Env) { 1321 log.SetFlags(log.Lshortfile) 1322 env.OpenFile("main.go") 1323 env.OpenFile("other.go") 1324 x := env.DiagnosticsFor("main.go") 1325 if x == nil { 1326 t.Fatalf("expected 1 diagnostic, got none") 1327 } 1328 if len(x.Diagnostics) != 1 { 1329 t.Fatalf("main.go, got %d diagnostics, expected 1", len(x.Diagnostics)) 1330 } 1331 keep := x.Diagnostics[0] 1332 y := env.DiagnosticsFor("other.go") 1333 if len(y.Diagnostics) != 1 { 1334 t.Fatalf("other.go: got %d diagnostics, expected 1", len(y.Diagnostics)) 1335 } 1336 if len(y.Diagnostics[0].RelatedInformation) != 1 { 1337 t.Fatalf("got %d RelatedInformations, expected 1", len(y.Diagnostics[0].RelatedInformation)) 1338 } 1339 // check that the RelatedInformation matches the error from main.go 1340 c := y.Diagnostics[0].RelatedInformation[0] 1341 if c.Location.Range != keep.Range { 1342 t.Errorf("locations don't match. Got %v expected %v", c.Location.Range, keep.Range) 1343 } 1344 }) 1345} 1346 1347func TestNotifyOrphanedFiles(t *testing.T) { 1348 // Need GO111MODULE=on for this test to work with Go 1.12. 1349 testenv.NeedsGo1Point(t, 13) 1350 1351 const files = ` 1352-- go.mod -- 1353module mod.com 1354 1355go 1.12 1356-- a/a.go -- 1357package a 1358 1359func main() { 1360 var x int 1361} 1362-- a/a_ignore.go -- 1363// +build ignore 1364 1365package a 1366 1367func _() { 1368 var x int 1369} 1370` 1371 run(t, files, func(t *testing.T, env *Env) { 1372 env.OpenFile("a/a.go") 1373 env.Await( 1374 env.DiagnosticAtRegexp("a/a.go", "x"), 1375 ) 1376 env.OpenFile("a/a_ignore.go") 1377 env.Await( 1378 DiagnosticAt("a/a_ignore.go", 2, 8), 1379 ) 1380 }) 1381} 1382 1383func TestEnableAllExperiments(t *testing.T) { 1384 const mod = ` 1385-- go.mod -- 1386module mod.com 1387 1388go 1.12 1389-- main.go -- 1390package main 1391 1392import "bytes" 1393 1394func b(c bytes.Buffer) { 1395 _ = 1 1396} 1397` 1398 withOptions( 1399 EditorConfig{ 1400 AllExperiments: true, 1401 }, 1402 ).run(t, mod, func(t *testing.T, env *Env) { 1403 // Confirm that the setting doesn't cause any warnings. 1404 env.Await(NoShowMessage()) 1405 }) 1406} 1407 1408func TestSwig(t *testing.T) { 1409 t.Skipf("skipped until golang/go#37098 is resolved") 1410 1411 const mod = ` 1412-- go.mod -- 1413module mod.com 1414 1415go 1.12 1416-- pkg/simple/export_swig.go -- 1417package simple 1418 1419func ExportSimple(x, y int) int { 1420 return Gcd(x, y) 1421} 1422-- pkg/simple/simple.swigcxx -- 1423%module simple 1424 1425%inline %{ 1426extern int gcd(int x, int y) 1427{ 1428 int g; 1429 g = y; 1430 while (x > 0) { 1431 g = x; 1432 x = y % x; 1433 y = g; 1434 } 1435 return g; 1436} 1437%} 1438-- main.go -- 1439package a 1440 1441func main() { 1442 var x int 1443} 1444` 1445 run(t, mod, func(t *testing.T, env *Env) { 1446 env.Await( 1447 OnceMet( 1448 InitialWorkspaceLoad, 1449 NoDiagnosticWithMessage("", "illegal character U+0023 '#'"), 1450 ), 1451 ) 1452 }) 1453} 1454 1455// When foo_test.go is opened, gopls will object to the borked package name. 1456// This test asserts that when the package name is fixed, gopls will soon after 1457// have no more complaints about it. 1458// https://github.com/golang/go/issues/41061 1459func TestRenamePackage(t *testing.T) { 1460 testenv.NeedsGo1Point(t, 16) 1461 1462 const contents = ` 1463-- go.mod -- 1464module mod.com 1465 1466go 1.12 1467-- main.go -- 1468package main 1469 1470import "example.com/blah" 1471 1472func main() { 1473 blah.Hello() 1474} 1475-- bob.go -- 1476package main 1477-- foo/foo.go -- 1478package foo 1479-- foo/foo_test.go -- 1480package foo_ 1481` 1482 1483 withOptions( 1484 ProxyFiles(proxy), 1485 InGOPATH(), 1486 ).run(t, contents, func(t *testing.T, env *Env) { 1487 // Simulate typing character by character. 1488 env.OpenFile("foo/foo_test.go") 1489 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1)) 1490 env.RegexpReplace("foo/foo_test.go", "_", "_t") 1491 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1)) 1492 env.RegexpReplace("foo/foo_test.go", "_t", "_test") 1493 env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 2)) 1494 1495 env.Await( 1496 EmptyDiagnostics("foo/foo_test.go"), 1497 NoOutstandingWork(), 1498 ) 1499 }) 1500} 1501 1502// TestProgressBarErrors confirms that critical workspace load errors are shown 1503// and updated via progress reports. 1504func TestProgressBarErrors(t *testing.T) { 1505 testenv.NeedsGo1Point(t, 14) 1506 1507 const pkg = ` 1508-- go.mod -- 1509modul mod.com 1510 1511go 1.12 1512-- main.go -- 1513package main 1514` 1515 run(t, pkg, func(t *testing.T, env *Env) { 1516 env.OpenFile("go.mod") 1517 env.Await( 1518 OutstandingWork(lsp.WorkspaceLoadFailure, "unknown directive"), 1519 ) 1520 env.EditBuffer("go.mod", fake.NewEdit(0, 0, 3, 0, `module mod.com 1521 1522go 1.hello 1523`)) 1524 // As of golang/go#42529, go.mod changes do not reload the workspace until 1525 // they are saved. 1526 env.SaveBufferWithoutActions("go.mod") 1527 env.Await( 1528 OutstandingWork(lsp.WorkspaceLoadFailure, "invalid go version"), 1529 ) 1530 env.RegexpReplace("go.mod", "go 1.hello", "go 1.12") 1531 env.SaveBufferWithoutActions("go.mod") 1532 env.Await( 1533 NoOutstandingWork(), 1534 ) 1535 }) 1536} 1537 1538func TestDeleteDirectory(t *testing.T) { 1539 testenv.NeedsGo1Point(t, 14) 1540 1541 const mod = ` 1542-- bob/bob.go -- 1543package bob 1544 1545func Hello() { 1546 var x int 1547} 1548-- go.mod -- 1549module mod.com 1550-- main.go -- 1551package main 1552 1553import "mod.com/bob" 1554 1555func main() { 1556 bob.Hello() 1557} 1558` 1559 run(t, mod, func(t *testing.T, env *Env) { 1560 env.RemoveWorkspaceFile("bob") 1561 env.Await( 1562 env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`), 1563 EmptyDiagnostics("bob/bob.go"), 1564 ) 1565 }) 1566} 1567 1568// Confirms that circular imports are tested and reported. 1569func TestCircularImports(t *testing.T) { 1570 const mod = ` 1571-- go.mod -- 1572module mod.com 1573 1574go 1.12 1575-- self/self.go -- 1576package self 1577 1578import _ "mod.com/self" 1579func Hello() {} 1580-- double/a/a.go -- 1581package a 1582 1583import _ "mod.com/double/b" 1584-- double/b/b.go -- 1585package b 1586 1587import _ "mod.com/double/a" 1588-- triple/a/a.go -- 1589package a 1590 1591import _ "mod.com/triple/b" 1592-- triple/b/b.go -- 1593package b 1594 1595import _ "mod.com/triple/c" 1596-- triple/c/c.go -- 1597package c 1598 1599import _ "mod.com/triple/a" 1600` 1601 run(t, mod, func(t *testing.T, env *Env) { 1602 env.Await( 1603 env.DiagnosticAtRegexpWithMessage("self/self.go", `_ "mod.com/self"`, "import cycle not allowed"), 1604 env.DiagnosticAtRegexpWithMessage("double/a/a.go", `_ "mod.com/double/b"`, "import cycle not allowed"), 1605 env.DiagnosticAtRegexpWithMessage("triple/a/a.go", `_ "mod.com/triple/b"`, "import cycle not allowed"), 1606 ) 1607 }) 1608} 1609 1610func TestBadImport(t *testing.T) { 1611 testenv.NeedsGo1Point(t, 14) 1612 1613 const mod = ` 1614-- go.mod -- 1615module mod.com 1616 1617go 1.12 1618-- main.go -- 1619package main 1620 1621import ( 1622 _ "nosuchpkg" 1623) 1624` 1625 t.Run("module", func(t *testing.T) { 1626 run(t, mod, func(t *testing.T, env *Env) { 1627 env.Await( 1628 env.DiagnosticAtRegexpWithMessage("main.go", `"nosuchpkg"`, `could not import nosuchpkg (no required module provides package "nosuchpkg"`), 1629 ) 1630 }) 1631 }) 1632 t.Run("GOPATH", func(t *testing.T) { 1633 withOptions( 1634 InGOPATH(), 1635 EditorConfig{ 1636 Env: map[string]string{"GO111MODULE": "off"}, 1637 }, 1638 Modes(Singleton), 1639 ).run(t, mod, func(t *testing.T, env *Env) { 1640 env.Await( 1641 env.DiagnosticAtRegexpWithMessage("main.go", `"nosuchpkg"`, `cannot find package "nosuchpkg" in any of`), 1642 ) 1643 }) 1644 }) 1645} 1646 1647func TestMultipleModules_Warning(t *testing.T) { 1648 const modules = ` 1649-- a/go.mod -- 1650module a.com 1651 1652go 1.12 1653-- a/a.go -- 1654package a 1655-- b/go.mod -- 1656module b.com 1657 1658go 1.12 1659-- b/b.go -- 1660package b 1661` 1662 for _, go111module := range []string{"on", "auto"} { 1663 t.Run("GO111MODULE="+go111module, func(t *testing.T) { 1664 withOptions( 1665 Modes(Singleton), 1666 EditorConfig{ 1667 Env: map[string]string{ 1668 "GO111MODULE": go111module, 1669 }, 1670 }, 1671 ).run(t, modules, func(t *testing.T, env *Env) { 1672 env.OpenFile("a/a.go") 1673 env.OpenFile("b/go.mod") 1674 env.Await( 1675 env.DiagnosticAtRegexp("a/a.go", "package a"), 1676 env.DiagnosticAtRegexp("b/go.mod", "module b.com"), 1677 OutstandingWork(lsp.WorkspaceLoadFailure, "gopls requires a module at the root of your workspace."), 1678 ) 1679 }) 1680 }) 1681 } 1682 1683 // Expect no warning if GO111MODULE=auto in a directory in GOPATH. 1684 t.Run("GOPATH_GO111MODULE_auto", func(t *testing.T) { 1685 withOptions( 1686 Modes(Singleton), 1687 EditorConfig{ 1688 Env: map[string]string{ 1689 "GO111MODULE": "auto", 1690 }, 1691 }, 1692 InGOPATH(), 1693 ).run(t, modules, func(t *testing.T, env *Env) { 1694 env.OpenFile("a/a.go") 1695 env.Await( 1696 OnceMet( 1697 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1), 1698 NoDiagnostics("a/a.go"), 1699 ), 1700 NoOutstandingWork(), 1701 ) 1702 }) 1703 }) 1704} 1705 1706func TestNestedModules(t *testing.T) { 1707 const proxy = ` 1708-- nested.com@v1.0.0/go.mod -- 1709module nested.com 1710 1711go 1.12 1712-- nested.com@v1.0.0/hello/hello.go -- 1713package hello 1714 1715func Hello() {} 1716` 1717 1718 const nested = ` 1719-- go.mod -- 1720module mod.com 1721 1722go 1.12 1723 1724require nested.com v1.0.0 1725-- go.sum -- 1726nested.com v1.0.0 h1:I6spLE4CgFqMdBPc+wTV2asDO2QJ3tU0YAT+jkLeN1I= 1727nested.com v1.0.0/go.mod h1:ly53UzXQgVjSlV7wicdBB4p8BxfytuGT1Xcyv0ReJfI= 1728-- main.go -- 1729package main 1730 1731import "nested.com/hello" 1732 1733func main() { 1734 hello.Hello() 1735} 1736-- nested/go.mod -- 1737module nested.com 1738 1739-- nested/hello/hello.go -- 1740package hello 1741 1742func Hello() { 1743 helloHelper() 1744} 1745-- nested/hello/hello_helper.go -- 1746package hello 1747 1748func helloHelper() {} 1749` 1750 withOptions( 1751 ProxyFiles(proxy), 1752 Modes(Singleton), 1753 ).run(t, nested, func(t *testing.T, env *Env) { 1754 // Expect a diagnostic in a nested module. 1755 env.OpenFile("nested/hello/hello.go") 1756 didOpen := CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1) 1757 env.Await( 1758 OnceMet( 1759 didOpen, 1760 env.DiagnosticAtRegexp("nested/hello/hello.go", "helloHelper"), 1761 ), 1762 OnceMet( 1763 didOpen, 1764 env.DiagnosticAtRegexpWithMessage("nested/hello/hello.go", "package hello", "nested module"), 1765 ), 1766 OnceMet( 1767 didOpen, 1768 OutstandingWork(lsp.WorkspaceLoadFailure, "nested module"), 1769 ), 1770 ) 1771 }) 1772} 1773 1774func TestAdHocPackagesReloading(t *testing.T) { 1775 const nomod = ` 1776-- main.go -- 1777package main 1778 1779func main() {} 1780` 1781 run(t, nomod, func(t *testing.T, env *Env) { 1782 env.OpenFile("main.go") 1783 env.RegexpReplace("main.go", "{}", "{ var x int; }") // simulate typing 1784 env.Await( 1785 OnceMet( 1786 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1), 1787 NoLogMatching(protocol.Info, "packages=1"), 1788 ), 1789 ) 1790 }) 1791} 1792