2020-02-27 11:35:46 -07:00
|
|
|
// 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
|
|
|
|
|
2020-02-28 12:56:20 -07:00
|
|
|
import (
|
2020-04-30 13:59:37 -06:00
|
|
|
"io"
|
|
|
|
"testing"
|
|
|
|
|
2020-02-28 12:56:20 -07:00
|
|
|
"golang.org/x/tools/internal/lsp/fake"
|
2020-04-01 19:31:43 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2020-08-26 15:41:45 -06:00
|
|
|
errors "golang.org/x/xerrors"
|
2020-02-28 12:56:20 -07:00
|
|
|
)
|
2020-02-27 11:35:46 -07:00
|
|
|
|
2020-07-25 01:36:36 -06:00
|
|
|
func (e *Env) ChangeFilesOnDisk(events []fake.FileEvent) {
|
|
|
|
e.T.Helper()
|
|
|
|
if err := e.Sandbox.Workdir.ChangeFilesOnDisk(e.Ctx, events); err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 19:22:35 -06:00
|
|
|
// RemoveWorkspaceFile deletes a file on disk but does nothing in the
|
2020-02-27 11:35:46 -07:00
|
|
|
// editor. It calls t.Fatal on any error.
|
2020-06-15 19:22:35 -06:00
|
|
|
func (e *Env) RemoveWorkspaceFile(name string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Sandbox.Workdir.RemoveFile(e.Ctx, name); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 15:20:53 -07:00
|
|
|
// ReadWorkspaceFile reads a file from the workspace, calling t.Fatal on any
|
|
|
|
// error.
|
|
|
|
func (e *Env) ReadWorkspaceFile(name string) string {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
content, err := e.Sandbox.Workdir.ReadFile(name)
|
2020-02-27 15:20:53 -07:00
|
|
|
if err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 15:20:53 -07:00
|
|
|
}
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
|
2020-06-18 10:30:59 -06:00
|
|
|
// WriteWorkspaceFile writes a file to disk but does nothing in the editor.
|
|
|
|
// It calls t.Fatal on any error.
|
2020-06-15 19:22:35 -06:00
|
|
|
func (e *Env) WriteWorkspaceFile(name, content string) {
|
|
|
|
e.T.Helper()
|
|
|
|
if err := e.Sandbox.Workdir.WriteFile(e.Ctx, name, content); err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 23:25:24 -06:00
|
|
|
// WriteWorkspaceFiles deletes a file on disk but does nothing in the
|
|
|
|
// editor. It calls t.Fatal on any error.
|
|
|
|
func (e *Env) WriteWorkspaceFiles(files map[string]string) {
|
|
|
|
e.T.Helper()
|
|
|
|
if err := e.Sandbox.Workdir.WriteFiles(e.Ctx, files); err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 11:35:46 -07:00
|
|
|
// OpenFile opens a file in the editor, calling t.Fatal on any error.
|
|
|
|
func (e *Env) OpenFile(name string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.OpenFile(e.Ctx, name); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 18:01:53 -06:00
|
|
|
func (e *Env) OpenFileWithContent(name, content string) {
|
|
|
|
e.T.Helper()
|
|
|
|
if err := e.Editor.OpenFileWithContent(e.Ctx, name, content); err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 11:35:46 -07:00
|
|
|
// CreateBuffer creates a buffer in the editor, calling t.Fatal on any error.
|
|
|
|
func (e *Env) CreateBuffer(name string, content string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.CreateBuffer(e.Ctx, name, content); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloseBuffer closes an editor buffer without saving, calling t.Fatal on any
|
|
|
|
// error.
|
|
|
|
func (e *Env) CloseBuffer(name string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.CloseBuffer(e.Ctx, name); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// EditBuffer applies edits to an editor buffer, calling t.Fatal on any error.
|
|
|
|
func (e *Env) EditBuffer(name string, edits ...fake.Edit) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.EditBuffer(e.Ctx, name, edits); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-28 12:56:20 -07:00
|
|
|
// RegexpSearch returns the starting position of the first match for re in the
|
|
|
|
// buffer specified by name, calling t.Fatal on any error. It first searches
|
|
|
|
// for the position in open buffers, then in workspace files.
|
|
|
|
func (e *Env) RegexpSearch(name, re string) fake.Pos {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
pos, err := e.Editor.RegexpSearch(name, re)
|
2020-02-28 12:56:20 -07:00
|
|
|
if err == fake.ErrUnknownBuffer {
|
2020-04-28 22:00:52 -06:00
|
|
|
pos, err = e.Sandbox.Workdir.RegexpSearch(name, re)
|
2020-02-28 12:56:20 -07:00
|
|
|
}
|
|
|
|
if err != nil {
|
2020-03-24 16:29:27 -06:00
|
|
|
e.T.Fatalf("RegexpSearch: %v, %v", name, err)
|
2020-02-28 12:56:20 -07:00
|
|
|
}
|
|
|
|
return pos
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegexpReplace replaces the first group in the first match of regexpStr with
|
|
|
|
// the replace text, calling t.Fatal on any error.
|
|
|
|
func (e *Env) RegexpReplace(name, regexpStr, replace string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.RegexpReplace(e.Ctx, name, regexpStr, replace); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatalf("RegexpReplace: %v", err)
|
2020-02-28 12:56:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 11:35:46 -07:00
|
|
|
// SaveBuffer saves an editor buffer, calling t.Fatal on any error.
|
|
|
|
func (e *Env) SaveBuffer(name string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.SaveBuffer(e.Ctx, name); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GoToDefinition goes to definition in the editor, calling t.Fatal on any
|
|
|
|
// error.
|
|
|
|
func (e *Env) GoToDefinition(name string, pos fake.Pos) (string, fake.Pos) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
n, p, err := e.Editor.GoToDefinition(e.Ctx, name, pos)
|
2020-02-27 11:35:46 -07:00
|
|
|
if err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
|
|
|
return n, p
|
|
|
|
}
|
|
|
|
|
2020-04-10 07:38:38 -06:00
|
|
|
// Symbol returns symbols matching query
|
|
|
|
func (e *Env) Symbol(query string) []fake.SymbolInformation {
|
|
|
|
e.T.Helper()
|
|
|
|
r, err := e.Editor.Symbol(e.Ctx, query)
|
|
|
|
if err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2020-02-27 15:20:53 -07:00
|
|
|
// FormatBuffer formats the editor buffer, calling t.Fatal on any error.
|
|
|
|
func (e *Env) FormatBuffer(name string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.FormatBuffer(e.Ctx, name); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 15:20:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// OrganizeImports processes the source.organizeImports codeAction, calling
|
|
|
|
// t.Fatal on any error.
|
|
|
|
func (e *Env) OrganizeImports(name string) {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.OrganizeImports(e.Ctx, name); err != nil {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Fatal(err)
|
2020-02-27 15:20:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-01 19:31:43 -06:00
|
|
|
// ApplyQuickFixes processes the quickfix codeAction, calling t.Fatal on any error.
|
|
|
|
func (e *Env) ApplyQuickFixes(path string, diagnostics []protocol.Diagnostic) {
|
|
|
|
e.T.Helper()
|
2020-07-10 18:02:44 -06:00
|
|
|
if err := e.Editor.ApplyQuickFixes(e.Ctx, path, nil, diagnostics); err != nil {
|
2020-04-01 19:31:43 -06:00
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 11:42:14 -06:00
|
|
|
// Hover in the editor, calling t.Fatal on any error.
|
|
|
|
func (e *Env) Hover(name string, pos fake.Pos) (*protocol.MarkupContent, fake.Pos) {
|
|
|
|
e.T.Helper()
|
|
|
|
c, p, err := e.Editor.Hover(e.Ctx, name, pos)
|
|
|
|
if err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
return c, p
|
|
|
|
}
|
|
|
|
|
2020-06-15 20:21:06 -06:00
|
|
|
func (e *Env) DocumentLink(name string) []protocol.DocumentLink {
|
|
|
|
e.T.Helper()
|
|
|
|
links, err := e.Editor.DocumentLink(e.Ctx, name)
|
|
|
|
if err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
return links
|
|
|
|
}
|
|
|
|
|
2020-04-30 13:59:37 -06:00
|
|
|
func checkIsFatal(t *testing.T, err error) {
|
|
|
|
t.Helper()
|
|
|
|
if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrClosedPipe) {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 11:35:46 -07:00
|
|
|
// CloseEditor shuts down the editor, calling t.Fatal on any error.
|
|
|
|
func (e *Env) CloseEditor() {
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-06-05 07:17:05 -06:00
|
|
|
checkIsFatal(e.T, e.Editor.Close(e.Ctx))
|
2020-02-27 11:35:46 -07:00
|
|
|
}
|
2020-04-27 15:00:34 -06:00
|
|
|
|
|
|
|
// RunGenerate runs go:generate on the given dir, calling t.Fatal on any error.
|
|
|
|
// It waits for the generate command to complete and checks for file changes
|
|
|
|
// before returning.
|
|
|
|
func (e *Env) RunGenerate(dir string) {
|
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Editor.RunGenerate(e.Ctx, dir); err != nil {
|
2020-04-27 15:00:34 -06:00
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
2020-08-17 20:50:34 -06:00
|
|
|
e.Await(NoOutstandingWork())
|
2020-04-27 15:00:34 -06:00
|
|
|
// Ideally the fake.Workspace would handle all synthetic file watching, but
|
|
|
|
// we help it out here as we need to wait for the generate command to
|
|
|
|
// complete before checking the filesystem.
|
|
|
|
e.CheckForFileChanges()
|
|
|
|
}
|
|
|
|
|
|
|
|
// CheckForFileChanges triggers a manual poll of the workspace for any file
|
|
|
|
// changes since creation, or since last polling. It is a workaround for the
|
|
|
|
// lack of true file watching support in the fake workspace.
|
|
|
|
func (e *Env) CheckForFileChanges() {
|
|
|
|
e.T.Helper()
|
2020-04-28 22:00:52 -06:00
|
|
|
if err := e.Sandbox.Workdir.CheckForFileChanges(e.Ctx); err != nil {
|
2020-04-27 15:00:34 -06:00
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2020-05-06 20:54:50 -06:00
|
|
|
|
|
|
|
// CodeLens calls textDocument/codeLens for the given path, calling t.Fatal on
|
|
|
|
// any error.
|
|
|
|
func (e *Env) CodeLens(path string) []protocol.CodeLens {
|
|
|
|
e.T.Helper()
|
|
|
|
lens, err := e.Editor.CodeLens(e.Ctx, path)
|
|
|
|
if err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
return lens
|
|
|
|
}
|
2020-05-09 06:54:23 -06:00
|
|
|
|
2020-08-26 14:54:52 -06:00
|
|
|
// References calls textDocument/references for the given path at the given
|
|
|
|
// position.
|
2020-08-17 12:36:26 -06:00
|
|
|
func (e *Env) References(path string, pos fake.Pos) []protocol.Location {
|
2020-07-20 23:34:22 -06:00
|
|
|
e.T.Helper()
|
|
|
|
locations, err := e.Editor.References(e.Ctx, path, pos)
|
|
|
|
if err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
return locations
|
|
|
|
}
|
|
|
|
|
2020-05-09 06:54:23 -06:00
|
|
|
// CodeAction calls testDocument/codeAction for the given path, and calls
|
|
|
|
// t.Fatal if there are errors.
|
|
|
|
func (e *Env) CodeAction(path string) []protocol.CodeAction {
|
|
|
|
e.T.Helper()
|
2020-07-10 18:02:44 -06:00
|
|
|
actions, err := e.Editor.CodeAction(e.Ctx, path, nil)
|
2020-05-09 06:54:23 -06:00
|
|
|
if err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
return actions
|
|
|
|
}
|
2020-06-15 07:23:58 -06:00
|
|
|
|
|
|
|
// ChangeEnv modifies the editor environment and reconfigures the LSP client.
|
|
|
|
// TODO: extend this to "ChangeConfiguration", once we refactor the way editor
|
|
|
|
// configuration is defined.
|
2020-07-22 12:03:48 -06:00
|
|
|
func (e *Env) ChangeEnv(overlay map[string]string) {
|
2020-06-15 07:23:58 -06:00
|
|
|
e.T.Helper()
|
|
|
|
// TODO: to be correct, this should probably be synchronized, but right now
|
|
|
|
// configuration is only ever modified synchronously in a regtest, so this
|
|
|
|
// correctness can wait for the previously mentioned refactoring.
|
2020-07-22 12:03:48 -06:00
|
|
|
if e.Editor.Config.Env == nil {
|
|
|
|
e.Editor.Config.Env = make(map[string]string)
|
|
|
|
}
|
|
|
|
for k, v := range overlay {
|
|
|
|
e.Editor.Config.Env[k] = v
|
|
|
|
}
|
2020-06-15 07:23:58 -06:00
|
|
|
var params protocol.DidChangeConfigurationParams
|
|
|
|
if err := e.Editor.Server.DidChangeConfiguration(e.Ctx, ¶ms); err != nil {
|
|
|
|
e.T.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|