mirror of
https://github.com/golang/go
synced 2024-10-01 03:08:33 -06:00
8f5be0d382
One of the tricky things about asserting on conditions in regtests is the asynchronous nature of LSP. For example, as the LSP client we cannot be sure when we've received all diagnostics for a given file. Currently, regtests are implemented by awaiting specific diagnostic expectations. This means that if gopls generates diagnostics that do not match those expectations, we can only time out the test. Ideally, we would want to know that gopls is done generating all diagnostics for the current file state. This is not possible without knowing the status of diagnostics for. Barring this, we would want to know that diagnostics are done for the current file version. Unfortunately, that also is not possible, because a new version of file B can affect diagnostics in file A. So in lieu of this information, this CL exposes a few tools that can be used to improve the experience of writing new regtests. - A new expectation is added: AnyDiagnosticAtCurrentVersion, that is satisfied if any diagnostics have been received for the current buffer version. - ExpectDiagnostics is added to Env, to help check whether the current diagnostics matches expectations. Updates golang/go#38113 Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1 Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
197 lines
4.3 KiB
Go
197 lines
4.3 KiB
Go
// 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/fake"
|
|
)
|
|
|
|
// Use mod.com for all go.mod files due to golang/go#35230.
|
|
const exampleProgram = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- main.go --
|
|
package main
|
|
|
|
import "fmt"
|
|
|
|
func main() {
|
|
fmt.Println("Hello World.")
|
|
}`
|
|
|
|
func TestDiagnosticErrorInEditedFile(t *testing.T) {
|
|
runner.Run(t, exampleProgram, func(env *Env) {
|
|
// Deleting the 'n' at the end of Println should generate a single error
|
|
// diagnostic.
|
|
env.OpenFile("main.go")
|
|
env.RegexpReplace("main.go", "Printl(n)", "")
|
|
env.Await(env.DiagnosticAtRegexp("main.go", "Printl"))
|
|
})
|
|
}
|
|
|
|
const onlyMod = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
`
|
|
|
|
func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
|
|
t.Skip("skipping due to golang.org/issues/37195")
|
|
t.Parallel()
|
|
runner.Run(t, onlyMod, func(env *Env) {
|
|
env.CreateBuffer("main.go", "package main\n\nfunc m() {\nlog.Println()\n}")
|
|
env.SaveBuffer("main.go")
|
|
// TODO: this shouldn't actually happen
|
|
env.Await(env.DiagnosticAtRegexp("main.go", "Println"))
|
|
})
|
|
}
|
|
|
|
const brokenFile = `package main
|
|
|
|
const Foo = "abc
|
|
`
|
|
|
|
func TestDiagnosticErrorInNewFile(t *testing.T) {
|
|
runner.Run(t, brokenFile, func(env *Env) {
|
|
env.CreateBuffer("broken.go", brokenFile)
|
|
env.Await(env.DiagnosticAtRegexp("broken.go", "\"abc"))
|
|
})
|
|
}
|
|
|
|
// badPackage contains a duplicate definition of the 'a' const.
|
|
const badPackage = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- a.go --
|
|
package consts
|
|
|
|
const a = 1
|
|
-- b.go --
|
|
package consts
|
|
|
|
const a = 2
|
|
`
|
|
|
|
func TestDiagnosticClearingOnEdit(t *testing.T) {
|
|
runner.Run(t, badPackage, func(env *Env) {
|
|
env.OpenFile("b.go")
|
|
env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
|
|
|
|
// Fix the error by editing the const name in b.go to `b`.
|
|
env.RegexpReplace("b.go", "(a) = 2", "b")
|
|
env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go"))
|
|
})
|
|
}
|
|
|
|
func TestDiagnosticClearingOnDelete(t *testing.T) {
|
|
runner.Run(t, badPackage, func(env *Env) {
|
|
env.OpenFile("a.go")
|
|
env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
|
|
env.RemoveFileFromWorkspace("b.go")
|
|
|
|
env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go"))
|
|
})
|
|
}
|
|
|
|
func TestDiagnosticClearingOnClose(t *testing.T) {
|
|
runner.Run(t, badPackage, func(env *Env) {
|
|
env.CreateBuffer("c.go", `package consts
|
|
|
|
const a = 3`)
|
|
env.Await(
|
|
env.DiagnosticAtRegexp("a.go", "a = 1"),
|
|
env.DiagnosticAtRegexp("b.go", "a = 2"),
|
|
env.DiagnosticAtRegexp("c.go", "a = 3"))
|
|
env.CloseBuffer("c.go")
|
|
env.Await(
|
|
env.DiagnosticAtRegexp("a.go", "a = 1"),
|
|
env.DiagnosticAtRegexp("b.go", "a = 2"),
|
|
EmptyDiagnostics("c.go"))
|
|
})
|
|
}
|
|
|
|
func TestIssue37978(t *testing.T) {
|
|
runner.Run(t, exampleProgram, func(env *Env) {
|
|
// Create a new workspace-level directory and empty file.
|
|
env.CreateBuffer("c/c.go", "")
|
|
|
|
// Write the file contents with a missing import.
|
|
env.EditBuffer("c/c.go", fake.Edit{
|
|
Text: `package c
|
|
|
|
const a = http.MethodGet
|
|
`,
|
|
})
|
|
env.Await(
|
|
env.DiagnosticAtRegexp("c/c.go", "http.MethodGet"),
|
|
)
|
|
// Save file, which will organize imports, adding the expected import.
|
|
// Expect the diagnostics to clear.
|
|
env.SaveBuffer("c/c.go")
|
|
env.Await(
|
|
EmptyDiagnostics("c/c.go"),
|
|
)
|
|
})
|
|
}
|
|
|
|
const noMod = `
|
|
-- main.go --
|
|
package main
|
|
|
|
import "mod.com/bob"
|
|
|
|
func main() {
|
|
bob.Hello()
|
|
}
|
|
-- bob/bob.go --
|
|
package bob
|
|
|
|
func Hello() {
|
|
var x int
|
|
}
|
|
`
|
|
|
|
// TestNoMod confirms that gopls continues to work when a user adds a go.mod
|
|
// file to their workspace.
|
|
func TestNoMod(t *testing.T) {
|
|
t.Run("manual", func(t *testing.T) {
|
|
runner.Run(t, noMod, func(env *Env) {
|
|
env.Await(
|
|
env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
|
|
)
|
|
env.CreateBuffer("go.mod", `module mod.com
|
|
|
|
go 1.12
|
|
`)
|
|
env.SaveBuffer("go.mod")
|
|
env.Await(
|
|
EmptyDiagnostics("main.go"),
|
|
env.DiagnosticAtRegexp("bob/bob.go", "x"),
|
|
)
|
|
})
|
|
})
|
|
t.Run("initialized", func(t *testing.T) {
|
|
runner.Run(t, noMod, func(env *Env) {
|
|
env.Await(
|
|
env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
|
|
)
|
|
if err := env.W.RunGoCommand(env.Ctx, "mod", "init", "mod.com"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
env.Await(
|
|
EmptyDiagnostics("main.go"),
|
|
env.DiagnosticAtRegexp("bob/bob.go", "x"),
|
|
)
|
|
})
|
|
})
|
|
}
|