Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools / gopls@v0.5.2 / internal / regtest / watch_test.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools/gopls@v0.5.2/internal/regtest/watch_test.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools/gopls@v0.5.2/internal/regtest/watch_test.go
new file mode 100644 (file)
index 0000000..3b5b69d
--- /dev/null
@@ -0,0 +1,746 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package regtest
+
+import (
+       "testing"
+
+       "golang.org/x/tools/internal/lsp"
+       "golang.org/x/tools/internal/lsp/fake"
+       "golang.org/x/tools/internal/lsp/protocol"
+       "golang.org/x/tools/internal/testenv"
+)
+
+func TestEditFile(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- a/a.go --
+package a
+
+func _() {
+       var x int
+}
+`
+       // Edit the file when it's *not open* in the workspace, and check that
+       // diagnostics are updated.
+       t.Run("unopened", func(t *testing.T) {
+               runner.Run(t, pkg, func(t *testing.T, env *Env) {
+                       env.Await(
+                               env.DiagnosticAtRegexp("a/a.go", "x"),
+                       )
+                       env.WriteWorkspaceFile("a/a.go", `package a; func _() {};`)
+                       env.Await(
+                               EmptyDiagnostics("a/a.go"),
+                       )
+               })
+       })
+
+       // Edit the file when it *is open* in the workspace, and check that
+       // diagnostics are *not* updated.
+       t.Run("opened", func(t *testing.T) {
+               runner.Run(t, pkg, func(t *testing.T, env *Env) {
+                       env.OpenFile("a/a.go")
+                       env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
+                       env.WriteWorkspaceFile("a/a.go", `package a; func _() {};`)
+                       env.Await(
+                               env.DiagnosticAtRegexp("a/a.go", "x"),
+                       )
+               })
+       })
+}
+
+// Edit a dependency on disk and expect a new diagnostic.
+func TestEditDependency(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- b/b.go --
+package b
+
+func B() int { return 0 }
+-- a/a.go --
+package a
+
+import (
+       "mod.com/b"
+)
+
+func _() {
+       _ = b.B()
+}
+`
+       runner.Run(t, pkg, func(t *testing.T, env *Env) {
+               env.OpenFile("a/a.go")
+               env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
+               env.WriteWorkspaceFile("b/b.go", `package b; func B() {};`)
+               env.Await(
+                       env.DiagnosticAtRegexp("a/a.go", "b.B"),
+               )
+       })
+}
+
+// Edit both the current file and one of its dependencies on disk and
+// expect diagnostic changes.
+func TestEditFileAndDependency(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- b/b.go --
+package b
+
+func B() int { return 0 }
+-- a/a.go --
+package a
+
+import (
+       "mod.com/b"
+)
+
+func _() {
+       var x int
+       _ = b.B()
+}
+`
+       runner.Run(t, pkg, func(t *testing.T, env *Env) {
+               env.Await(
+                       env.DiagnosticAtRegexp("a/a.go", "x"),
+               )
+               env.WriteWorkspaceFiles(map[string]string{
+                       "b/b.go": `package b; func B() {};`,
+                       "a/a.go": `package a
+
+import "mod.com/b"
+
+func _() {
+       b.B()
+}`,
+               })
+               env.Await(
+                       EmptyDiagnostics("a/a.go"),
+                       NoDiagnostics("b/b.go"),
+               )
+       })
+}
+
+// Delete a dependency and expect a new diagnostic.
+func TestDeleteDependency(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- b/b.go --
+package b
+
+func B() int { return 0 }
+-- a/a.go --
+package a
+
+import (
+       "mod.com/b"
+)
+
+func _() {
+       _ = b.B()
+}
+`
+       runner.Run(t, pkg, func(t *testing.T, env *Env) {
+               env.OpenFile("a/a.go")
+               env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
+               env.RemoveWorkspaceFile("b/b.go")
+               env.Await(
+                       env.DiagnosticAtRegexp("a/a.go", "\"mod.com/b\""),
+               )
+       })
+}
+
+// Create a dependency on disk and expect the diagnostic to go away.
+func TestCreateDependency(t *testing.T) {
+       const missing = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- b/b.go --
+package b
+
+func B() int { return 0 }
+-- a/a.go --
+package a
+
+import (
+       "mod.com/c"
+)
+
+func _() {
+       c.C()
+}
+`
+       runner.Run(t, missing, func(t *testing.T, env *Env) {
+               t.Skip("the initial workspace load fails and never retries")
+
+               env.Await(
+                       env.DiagnosticAtRegexp("a/a.go", "\"mod.com/c\""),
+               )
+               env.WriteWorkspaceFile("c/c.go", `package c; func C() {};`)
+               env.Await(
+                       EmptyDiagnostics("c/c.go"),
+               )
+       })
+}
+
+// Create a new dependency and add it to the file on disk.
+// This is similar to what might happen if you switch branches.
+func TestCreateAndAddDependency(t *testing.T) {
+       const original = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- a/a.go --
+package a
+
+func _() {}
+`
+       runner.Run(t, original, func(t *testing.T, env *Env) {
+               env.WriteWorkspaceFile("c/c.go", `package c; func C() {};`)
+               env.WriteWorkspaceFile("a/a.go", `package a; import "mod.com/c"; func _() { c.C() }`)
+               env.Await(
+                       NoDiagnostics("a/a.go"),
+               )
+       })
+}
+
+// Create a new file that defines a new symbol, in the same package.
+func TestCreateFile(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- a/a.go --
+package a
+
+func _() {
+       hello()
+}
+`
+       runner.Run(t, pkg, func(t *testing.T, env *Env) {
+               env.Await(
+                       env.DiagnosticAtRegexp("a/a.go", "hello"),
+               )
+               env.WriteWorkspaceFile("a/a2.go", `package a; func hello() {};`)
+               env.Await(
+                       EmptyDiagnostics("a/a.go"),
+               )
+       })
+}
+
+// Add a new method to an interface and implement it.
+// Inspired by the structure of internal/lsp/source and internal/lsp/cache.
+func TestCreateImplementation(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- b/b.go --
+package b
+
+type B interface{
+       Hello() string
+}
+
+func SayHello(bee B) {
+       println(bee.Hello())
+}
+-- a/a.go --
+package a
+
+import "mod.com/b"
+
+type X struct {}
+
+func (_ X) Hello() string {
+       return ""
+}
+
+func _() {
+       x := X{}
+       b.SayHello(x)
+}
+`
+       const newMethod = `package b
+type B interface{
+       Hello() string
+       Bye() string
+}
+
+func SayHello(bee B) {
+       println(bee.Hello())
+}`
+       const implementation = `package a
+
+import "mod.com/b"
+
+type X struct {}
+
+func (_ X) Hello() string {
+       return ""
+}
+
+func (_ X) Bye() string {
+       return ""
+}
+
+func _() {
+       x := X{}
+       b.SayHello(x)
+}`
+
+       // Add the new method before the implementation. Expect diagnostics.
+       t.Run("method before implementation", func(t *testing.T) {
+               runner.Run(t, pkg, func(t *testing.T, env *Env) {
+                       env.WriteWorkspaceFile("b/b.go", newMethod)
+                       env.Await(
+                               OnceMet(
+                                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                                       DiagnosticAt("a/a.go", 12, 12),
+                               ),
+                       )
+                       env.WriteWorkspaceFile("a/a.go", implementation)
+                       env.Await(
+                               EmptyDiagnostics("a/a.go"),
+                       )
+               })
+       })
+       // Add the new implementation before the new method. Expect no diagnostics.
+       t.Run("implementation before method", func(t *testing.T) {
+               runner.Run(t, pkg, func(t *testing.T, env *Env) {
+                       env.WriteWorkspaceFile("a/a.go", implementation)
+                       env.Await(
+                               OnceMet(
+                                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                                       NoDiagnostics("a/a.go"),
+                               ),
+                       )
+                       env.WriteWorkspaceFile("b/b.go", newMethod)
+                       env.Await(
+                               NoDiagnostics("a/a.go"),
+                       )
+               })
+       })
+       // Add both simultaneously. Expect no diagnostics.
+       t.Run("implementation and method simultaneously", func(t *testing.T) {
+               runner.Run(t, pkg, func(t *testing.T, env *Env) {
+                       env.WriteWorkspaceFiles(map[string]string{
+                               "a/a.go": implementation,
+                               "b/b.go": newMethod,
+                       })
+                       env.Await(
+                               OnceMet(
+                                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                                       NoDiagnostics("a/a.go"),
+                               ),
+                               NoDiagnostics("b/b.go"),
+                       )
+               })
+       })
+}
+
+// Tests golang/go#38498. Delete a file and then force a reload.
+// Assert that we no longer try to load the file.
+func TestDeleteFiles(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- a/a.go --
+package a
+
+func _() {
+       var _ int
+}
+-- a/a_unneeded.go --
+package a
+`
+       t.Run("close then delete", func(t *testing.T) {
+               runner.Run(t, pkg, func(t *testing.T, env *Env) {
+                       env.OpenFile("a/a.go")
+                       env.OpenFile("a/a_unneeded.go")
+                       env.Await(
+                               OnceMet(
+                                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 2),
+                                       LogMatching(protocol.Info, "a_unneeded.go", 1),
+                               ),
+                       )
+
+                       // Close and delete the open file, mimicking what an editor would do.
+                       env.CloseBuffer("a/a_unneeded.go")
+                       env.RemoveWorkspaceFile("a/a_unneeded.go")
+                       env.RegexpReplace("a/a.go", "var _ int", "fmt.Println(\"\")")
+                       env.Await(
+                               env.DiagnosticAtRegexp("a/a.go", "fmt"),
+                       )
+                       env.SaveBuffer("a/a.go")
+                       env.Await(
+                               OnceMet(
+                                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1),
+                                       // There should only be one log message containing
+                                       // a_unneeded.go, from the initial workspace load, which we
+                                       // check for earlier. If there are more, there's a bug.
+                                       LogMatching(protocol.Info, "a_unneeded.go", 1),
+                               ),
+                               EmptyDiagnostics("a/a.go"),
+                       )
+               })
+       })
+
+       t.Run("delete then close", func(t *testing.T) {
+               runner.Run(t, pkg, func(t *testing.T, env *Env) {
+                       env.OpenFile("a/a.go")
+                       env.OpenFile("a/a_unneeded.go")
+                       env.Await(
+                               OnceMet(
+                                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 2),
+                                       LogMatching(protocol.Info, "a_unneeded.go", 1),
+                               ),
+                       )
+
+                       // Delete and then close the file.
+                       env.RemoveWorkspaceFile("a/a_unneeded.go")
+                       env.CloseBuffer("a/a_unneeded.go")
+                       env.RegexpReplace("a/a.go", "var _ int", "fmt.Println(\"\")")
+                       env.Await(
+                               env.DiagnosticAtRegexp("a/a.go", "fmt"),
+                       )
+                       env.SaveBuffer("a/a.go")
+                       env.Await(
+                               OnceMet(
+                                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1),
+                                       // There should only be one log message containing
+                                       // a_unneeded.go, from the initial workspace load, which we
+                                       // check for earlier. If there are more, there's a bug.
+                                       LogMatching(protocol.Info, "a_unneeded.go", 1),
+                               ),
+                               EmptyDiagnostics("a/a.go"),
+                       )
+               })
+       })
+}
+
+// This change reproduces the behavior of switching branches, with multiple
+// files being created and deleted. The key change here is the movement of a
+// symbol from one file to another in a given package through a deletion and
+// creation. To reproduce an issue with metadata invalidation in batched
+// changes, the last change in the batch is an on-disk file change that doesn't
+// require metadata invalidation.
+func TestMoveSymbol(t *testing.T) {
+       const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.14
+-- main.go --
+package main
+
+import "mod.com/a"
+
+func main() {
+       var x int
+       x = a.Hello
+       println(x)
+}
+-- a/a1.go --
+package a
+
+var Hello int
+-- a/a2.go --
+package a
+
+func _() {}
+`
+       runner.Run(t, pkg, func(t *testing.T, env *Env) {
+               env.ChangeFilesOnDisk([]fake.FileEvent{
+                       {
+                               Path: "a/a3.go",
+                               Content: `package a
+
+var Hello int
+`,
+                               ProtocolEvent: protocol.FileEvent{
+                                       URI:  env.Sandbox.Workdir.URI("a/a3.go"),
+                                       Type: protocol.Created,
+                               },
+                       },
+                       {
+                               Path: "a/a1.go",
+                               ProtocolEvent: protocol.FileEvent{
+                                       URI:  env.Sandbox.Workdir.URI("a/a1.go"),
+                                       Type: protocol.Deleted,
+                               },
+                       },
+                       {
+                               Path:    "a/a2.go",
+                               Content: `package a; func _() {};`,
+                               ProtocolEvent: protocol.FileEvent{
+                                       URI:  env.Sandbox.Workdir.URI("a/a2.go"),
+                                       Type: protocol.Changed,
+                               },
+                       },
+               })
+               env.Await(
+                       OnceMet(
+                               CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                               NoDiagnostics("main.go"),
+                       ),
+               )
+       })
+}
+
+// Reproduce golang/go#40456.
+func TestChangeVersion(t *testing.T) {
+       const proxy = `
+-- example.com@v1.2.3/go.mod --
+module example.com
+
+go 1.12
+-- example.com@v1.2.3/blah/blah.go --
+package blah
+
+const Name = "Blah"
+
+func X(x int) {}
+-- example.com@v1.2.2/go.mod --
+module example.com
+
+go 1.12
+-- example.com@v1.2.2/blah/blah.go --
+package blah
+
+const Name = "Blah"
+
+func X() {}
+-- random.org@v1.2.3/go.mod --
+module random.org
+
+go 1.12
+-- random.org@v1.2.3/blah/blah.go --
+package hello
+
+const Name = "Hello"
+`
+       const mod = `
+-- go.mod --
+module mod.com
+
+go 1.12
+
+require example.com v1.2.2
+-- main.go --
+package main
+
+import "example.com/blah"
+
+func main() {
+       blah.X()
+}
+`
+       withOptions(WithProxyFiles(proxy)).run(t, mod, func(t *testing.T, env *Env) {
+               env.WriteWorkspaceFiles(map[string]string{
+                       "go.mod": `module mod.com
+
+go 1.12
+
+require example.com v1.2.3
+`,
+                       "main.go": `package main
+
+import (
+       "example.com/blah"
+)
+
+func main() {
+       blah.X(1)
+}
+`,
+               })
+               env.Await(
+                       CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                       NoDiagnostics("main.go"),
+               )
+       })
+}
+
+// Reproduces golang/go#40340.
+func TestSwitchFromGOPATHToModules(t *testing.T) {
+       testenv.NeedsGo1Point(t, 13)
+
+       const files = `
+-- foo/blah/blah.go --
+package blah
+
+const Name = ""
+-- foo/main.go --
+package main
+
+import "blah"
+
+func main() {
+       _ = blah.Name
+}
+`
+       withOptions(
+               InGOPATH(),
+               WithModes(Experimental), // module is in a subdirectory
+       ).run(t, files, func(t *testing.T, env *Env) {
+               env.OpenFile("foo/main.go")
+               env.Await(env.DiagnosticAtRegexp("foo/main.go", `"blah"`))
+               if err := env.Sandbox.RunGoCommand(env.Ctx, "foo", "mod", []string{"init", "mod.com"}); err != nil {
+                       t.Fatal(err)
+               }
+               env.Await(
+                       OnceMet(
+                               CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                               env.DiagnosticAtRegexp("foo/main.go", `"blah"`),
+                       ),
+               )
+               env.RegexpReplace("foo/main.go", `"blah"`, `"mod.com/blah"`)
+               env.Await(
+                       EmptyDiagnostics("foo/main.go"),
+               )
+       })
+}
+
+// Reproduces golang/go#40487.
+func TestSwitchFromModulesToGOPATH(t *testing.T) {
+       testenv.NeedsGo1Point(t, 13)
+
+       const files = `
+-- foo/go.mod --
+module mod.com
+
+go 1.14
+-- foo/blah/blah.go --
+package blah
+
+const Name = ""
+-- foo/main.go --
+package main
+
+import "mod.com/blah"
+
+func main() {
+       _ = blah.Name
+}
+`
+       withOptions(
+               InGOPATH(),
+       ).run(t, files, func(t *testing.T, env *Env) {
+               env.OpenFile("foo/main.go")
+               env.RemoveWorkspaceFile("foo/go.mod")
+               env.Await(
+                       OnceMet(
+                               CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                               env.DiagnosticAtRegexp("foo/main.go", `"mod.com/blah"`),
+                       ),
+               )
+               env.RegexpReplace("foo/main.go", `"mod.com/blah"`, `"foo/blah"`)
+               env.Await(
+                       EmptyDiagnostics("foo/main.go"),
+               )
+       })
+}
+
+func TestNewSymbolInTestVariant(t *testing.T) {
+       const files = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- a/a.go --
+package a
+
+func bob() {}
+-- a/a_test.go --
+package a
+
+import "testing"
+
+func TestBob(t *testing.T) {
+       bob()
+}
+`
+       run(t, files, func(t *testing.T, env *Env) {
+               // Add a new symbol to the package under test and use it in the test
+               // variant. Expect no diagnostics.
+               env.WriteWorkspaceFiles(map[string]string{
+                       "a/a.go": `package a
+
+func bob() {}
+func george() {}
+`,
+                       "a/a_test.go": `package a
+
+import "testing"
+
+func TestAll(t *testing.T) {
+       bob()
+       george()
+}
+`,
+               })
+               env.Await(
+                       OnceMet(
+                               CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                               NoDiagnostics("a/a.go"),
+                       ),
+                       OnceMet(
+                               CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
+                               NoDiagnostics("a/a_test.go"),
+                       ),
+               )
+               // Now, add a new file to the test variant and use its symbol in the
+               // original test file. Expect no diagnostics.
+               env.WriteWorkspaceFiles(map[string]string{
+                       "a/a_test.go": `package a
+
+import "testing"
+
+func TestAll(t *testing.T) {
+       bob()
+       george()
+       hi()
+}
+`,
+                       "a/a2_test.go": `package a
+
+import "testing"
+
+func hi() {}
+
+func TestSomething(t *testing.T) {}
+`,
+               })
+               env.Await(
+                       OnceMet(
+                               CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 2),
+                               NoDiagnostics("a/a_test.go"),
+                       ),
+                       OnceMet(
+                               CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 2),
+                               NoDiagnostics("a/a2_test.go"),
+                       ),
+               )
+       })
+}