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.
10 . "golang.org/x/tools/gopls/internal/regtest"
12 "golang.org/x/tools/internal/lsp/fake"
13 "golang.org/x/tools/internal/lsp/protocol"
14 "golang.org/x/tools/internal/testenv"
17 func TestMain(m *testing.M) {
21 func TestEditFile(t *testing.T) {
34 // Edit the file when it's *not open* in the workspace, and check that
35 // diagnostics are updated.
36 t.Run("unopened", func(t *testing.T) {
37 Run(t, pkg, func(t *testing.T, env *Env) {
39 env.DiagnosticAtRegexp("a/a.go", "x"),
41 env.WriteWorkspaceFile("a/a.go", `package a; func _() {};`)
43 EmptyDiagnostics("a/a.go"),
48 // Edit the file when it *is open* in the workspace, and check that
49 // diagnostics are *not* updated.
50 t.Run("opened", func(t *testing.T) {
51 Run(t, pkg, func(t *testing.T, env *Env) {
52 env.OpenFile("a/a.go")
53 // Insert a trivial edit so that we don't automatically update the buffer
55 env.EditBuffer("a/a.go", fake.NewEdit(0, 0, 0, 0, " "))
56 env.Await(env.DoneWithOpen())
57 env.WriteWorkspaceFile("a/a.go", `package a; func _() {};`)
60 env.DoneWithChangeWatchedFiles(),
61 env.DiagnosticAtRegexp("a/a.go", "x"),
67 // Edit a dependency on disk and expect a new diagnostic.
68 func TestEditDependency(t *testing.T) {
77 func B() int { return 0 }
89 Run(t, pkg, func(t *testing.T, env *Env) {
90 env.OpenFile("a/a.go")
91 env.Await(env.DoneWithOpen())
92 env.WriteWorkspaceFile("b/b.go", `package b; func B() {};`)
94 env.DiagnosticAtRegexp("a/a.go", "b.B"),
99 // Edit both the current file and one of its dependencies on disk and
100 // expect diagnostic changes.
101 func TestEditFileAndDependency(t *testing.T) {
110 func B() int { return 0 }
123 Run(t, pkg, func(t *testing.T, env *Env) {
125 env.DiagnosticAtRegexp("a/a.go", "x"),
127 env.WriteWorkspaceFiles(map[string]string{
128 "b/b.go": `package b; func B() {};`,
138 EmptyDiagnostics("a/a.go"),
139 NoDiagnostics("b/b.go"),
144 // Delete a dependency and expect a new diagnostic.
145 func TestDeleteDependency(t *testing.T) {
154 func B() int { return 0 }
166 Run(t, pkg, func(t *testing.T, env *Env) {
167 env.OpenFile("a/a.go")
168 env.Await(env.DoneWithOpen())
169 env.RemoveWorkspaceFile("b/b.go")
171 env.DiagnosticAtRegexp("a/a.go", "\"mod.com/b\""),
176 // Create a dependency on disk and expect the diagnostic to go away.
177 func TestCreateDependency(t *testing.T) {
186 func B() int { return 0 }
198 Run(t, missing, func(t *testing.T, env *Env) {
199 t.Skip("the initial workspace load fails and never retries")
202 env.DiagnosticAtRegexp("a/a.go", "\"mod.com/c\""),
204 env.WriteWorkspaceFile("c/c.go", `package c; func C() {};`)
206 EmptyDiagnostics("c/c.go"),
211 // Create a new dependency and add it to the file on disk.
212 // This is similar to what might happen if you switch branches.
213 func TestCreateAndAddDependency(t *testing.T) {
224 Run(t, original, func(t *testing.T, env *Env) {
225 env.WriteWorkspaceFile("c/c.go", `package c; func C() {};`)
226 env.WriteWorkspaceFile("a/a.go", `package a; import "mod.com/c"; func _() { c.C() }`)
228 NoDiagnostics("a/a.go"),
233 // Create a new file that defines a new symbol, in the same package.
234 func TestCreateFile(t *testing.T) {
247 Run(t, pkg, func(t *testing.T, env *Env) {
249 env.DiagnosticAtRegexp("a/a.go", "hello"),
251 env.WriteWorkspaceFile("a/a2.go", `package a; func hello() {};`)
253 EmptyDiagnostics("a/a.go"),
258 // Add a new method to an interface and implement it.
259 // Inspired by the structure of internal/lsp/source and internal/lsp/cache.
260 func TestCreateImplementation(t *testing.T) {
273 func SayHello(bee B) {
283 func (_ X) Hello() string {
292 const newMethod = `package b
298 func SayHello(bee B) {
301 const implementation = `package a
307 func (_ X) Hello() string {
311 func (_ X) Bye() string {
320 // Add the new method before the implementation. Expect diagnostics.
321 t.Run("method before implementation", func(t *testing.T) {
322 Run(t, pkg, func(t *testing.T, env *Env) {
323 env.WriteWorkspaceFile("b/b.go", newMethod)
326 env.DoneWithChangeWatchedFiles(),
327 DiagnosticAt("a/a.go", 12, 12),
330 env.WriteWorkspaceFile("a/a.go", implementation)
332 EmptyDiagnostics("a/a.go"),
336 // Add the new implementation before the new method. Expect no diagnostics.
337 t.Run("implementation before method", func(t *testing.T) {
338 Run(t, pkg, func(t *testing.T, env *Env) {
339 env.WriteWorkspaceFile("a/a.go", implementation)
342 env.DoneWithChangeWatchedFiles(),
343 NoDiagnostics("a/a.go"),
346 env.WriteWorkspaceFile("b/b.go", newMethod)
348 NoDiagnostics("a/a.go"),
352 // Add both simultaneously. Expect no diagnostics.
353 t.Run("implementation and method simultaneously", func(t *testing.T) {
354 Run(t, pkg, func(t *testing.T, env *Env) {
355 env.WriteWorkspaceFiles(map[string]string{
356 "a/a.go": implementation,
361 env.DoneWithChangeWatchedFiles(),
362 NoDiagnostics("a/a.go"),
364 NoDiagnostics("b/b.go"),
370 // Tests golang/go#38498. Delete a file and then force a reload.
371 // Assert that we no longer try to load the file.
372 func TestDeleteFiles(t *testing.T) {
373 testenv.NeedsGo1Point(t, 13) // Poor overlay support causes problems on 1.12.
385 -- a/a_unneeded.go --
388 t.Run("close then delete", func(t *testing.T) {
389 WithOptions(EditorConfig{
391 }).Run(t, pkg, func(t *testing.T, env *Env) {
392 env.OpenFile("a/a.go")
393 env.OpenFile("a/a_unneeded.go")
397 LogMatching(protocol.Info, "a_unneeded.go", 1),
401 // Close and delete the open file, mimicking what an editor would do.
402 env.CloseBuffer("a/a_unneeded.go")
403 env.RemoveWorkspaceFile("a/a_unneeded.go")
404 env.RegexpReplace("a/a.go", "var _ int", "fmt.Println(\"\")")
406 env.DiagnosticAtRegexp("a/a.go", "fmt"),
408 env.SaveBuffer("a/a.go")
412 // There should only be one log message containing
413 // a_unneeded.go, from the initial workspace load, which we
414 // check for earlier. If there are more, there's a bug.
415 LogMatching(protocol.Info, "a_unneeded.go", 1),
417 EmptyDiagnostics("a/a.go"),
422 t.Run("delete then close", func(t *testing.T) {
424 EditorConfig{VerboseOutput: true},
425 ).Run(t, pkg, func(t *testing.T, env *Env) {
426 env.OpenFile("a/a.go")
427 env.OpenFile("a/a_unneeded.go")
431 LogMatching(protocol.Info, "a_unneeded.go", 1),
435 // Delete and then close the file.
436 env.RemoveWorkspaceFile("a/a_unneeded.go")
437 env.CloseBuffer("a/a_unneeded.go")
438 env.RegexpReplace("a/a.go", "var _ int", "fmt.Println(\"\")")
440 env.DiagnosticAtRegexp("a/a.go", "fmt"),
442 env.SaveBuffer("a/a.go")
446 // There should only be one log message containing
447 // a_unneeded.go, from the initial workspace load, which we
448 // check for earlier. If there are more, there's a bug.
449 LogMatching(protocol.Info, "a_unneeded.go", 1),
451 EmptyDiagnostics("a/a.go"),
457 // This change reproduces the behavior of switching branches, with multiple
458 // files being created and deleted. The key change here is the movement of a
459 // symbol from one file to another in a given package through a deletion and
460 // creation. To reproduce an issue with metadata invalidation in batched
461 // changes, the last change in the batch is an on-disk file change that doesn't
462 // require metadata invalidation.
463 func TestMoveSymbol(t *testing.T) {
488 Run(t, pkg, func(t *testing.T, env *Env) {
489 env.ChangeFilesOnDisk([]fake.FileEvent{
496 ProtocolEvent: protocol.FileEvent{
497 URI: env.Sandbox.Workdir.URI("a/a3.go"),
498 Type: protocol.Created,
503 ProtocolEvent: protocol.FileEvent{
504 URI: env.Sandbox.Workdir.URI("a/a1.go"),
505 Type: protocol.Deleted,
510 Content: `package a; func _() {};`,
511 ProtocolEvent: protocol.FileEvent{
512 URI: env.Sandbox.Workdir.URI("a/a2.go"),
513 Type: protocol.Changed,
519 env.DoneWithChangeWatchedFiles(),
520 NoDiagnostics("main.go"),
526 // Reproduce golang/go#40456.
527 func TestChangeVersion(t *testing.T) {
529 -- example.com@v1.2.3/go.mod --
533 -- example.com@v1.2.3/blah/blah.go --
539 -- example.com@v1.2.2/go.mod --
543 -- example.com@v1.2.2/blah/blah.go --
549 -- random.org@v1.2.3/go.mod --
553 -- random.org@v1.2.3/blah/blah.go --
564 require example.com v1.2.2
568 import "example.com/blah"
574 WithOptions(ProxyFiles(proxy)).Run(t, mod, func(t *testing.T, env *Env) {
575 env.WriteWorkspaceFiles(map[string]string{
576 "go.mod": `module mod.com
580 require example.com v1.2.3
582 "main.go": `package main
594 env.DoneWithChangeWatchedFiles(),
595 NoDiagnostics("main.go"),
600 // Reproduces golang/go#40340.
601 func TestSwitchFromGOPATHToModules(t *testing.T) {
602 testenv.NeedsGo1Point(t, 13)
605 -- foo/blah/blah.go --
620 Modes(Experimental), // module is in a subdirectory
621 ).Run(t, files, func(t *testing.T, env *Env) {
622 env.OpenFile("foo/main.go")
623 env.Await(env.DiagnosticAtRegexp("foo/main.go", `"blah"`))
624 if err := env.Sandbox.RunGoCommand(env.Ctx, "foo", "mod", []string{"init", "mod.com"}); err != nil {
629 env.DoneWithChangeWatchedFiles(),
630 env.DiagnosticAtRegexp("foo/main.go", `"blah"`),
633 env.RegexpReplace("foo/main.go", `"blah"`, `"mod.com/blah"`)
635 EmptyDiagnostics("foo/main.go"),
640 // Reproduces golang/go#40487.
641 func TestSwitchFromModulesToGOPATH(t *testing.T) {
642 testenv.NeedsGo1Point(t, 13)
649 -- foo/blah/blah.go --
656 import "mod.com/blah"
664 ).Run(t, files, func(t *testing.T, env *Env) {
665 env.OpenFile("foo/main.go")
666 env.RemoveWorkspaceFile("foo/go.mod")
669 env.DoneWithChangeWatchedFiles(),
670 env.DiagnosticAtRegexp("foo/main.go", `"mod.com/blah"`),
673 env.RegexpReplace("foo/main.go", `"mod.com/blah"`, `"foo/blah"`)
675 EmptyDiagnostics("foo/main.go"),
680 func TestNewSymbolInTestVariant(t *testing.T) {
695 func TestBob(t *testing.T) {
699 Run(t, files, func(t *testing.T, env *Env) {
700 // Add a new symbol to the package under test and use it in the test
701 // variant. Expect no diagnostics.
702 env.WriteWorkspaceFiles(map[string]string{
708 "a/a_test.go": `package a
712 func TestAll(t *testing.T) {
720 env.DoneWithChangeWatchedFiles(),
721 NoDiagnostics("a/a.go"),
724 env.DoneWithChangeWatchedFiles(),
725 NoDiagnostics("a/a_test.go"),
728 // Now, add a new file to the test variant and use its symbol in the
729 // original test file. Expect no diagnostics.
730 env.WriteWorkspaceFiles(map[string]string{
731 "a/a_test.go": `package a
735 func TestAll(t *testing.T) {
741 "a/a2_test.go": `package a
747 func TestSomething(t *testing.T) {}
752 env.DoneWithChangeWatchedFiles(),
753 NoDiagnostics("a/a_test.go"),
756 env.DoneWithChangeWatchedFiles(),
757 NoDiagnostics("a/a2_test.go"),