Documentation ¶
Overview ¶
Package integration provides a framework for writing integration tests of gopls.
The behaviors that matter to users, and the scenarios they typically describe in bug report, are usually expressed in terms of editor interactions. For example: "When I open my editor in this directory, navigate to this file, and change this line, I get a diagnostic that doesn't make sense". The integration package provides an API for gopls maintainers to express these types of user interactions in ordinary Go tests, validate them, and run them in a variety of execution modes.
Test package setup ¶
The integration test package uses a couple of uncommon patterns to reduce boilerplate in test bodies. First, it is intended to be imported as "." so that helpers do not need to be qualified. Second, it requires some setup that is currently implemented in the integration.Main function, which must be invoked by TestMain. Therefore, a minimal integration testing package looks like this:
package feature import ( "fmt" "testing" "github.com/gnoverse/gnopls/internal/hooks" . "github.com/gnoverse/gnopls/internal/test/integration" ) func TestMain(m *testing.M) { os.Exit(Main(m, hooks.Options)) }
Writing a simple integration test ¶
To run an integration test use the integration.Run function, which accepts a txtar-encoded archive defining the initial workspace state. This function sets up the workspace in a temporary directory, creates a fake text editor, starts gopls, and initializes an LSP session. It then invokes the provided test function with an *Env encapsulating the newly created environment. Because gopls may be run in various modes (as a sidecar or daemon process, with different settings), the test runner may perform this process multiple times, re-running the test function each time with a new environment.
func TestOpenFile(t *testing.T) { const files = ` -- go.mod -- module mod.com go 1.12 -- foo.go -- package foo ` Run(t, files, func(t *testing.T, env *Env) { env.OpenFile("foo.go") }) }
Configuring integration test execution ¶
The integration package exposes several options that affect the setup process described above. To use these options, use the WithOptions function:
WithOptions(opts...).Run(...)
See options.go for a full list of available options.
Operating on editor state ¶
To operate on editor state within the test body, the Env type provides access to the workspace directory (Env.SandBox), text editor (Env.Editor), LSP server (Env.Server), and 'awaiter' (Env.Awaiter).
In most cases, operations on these primitive building blocks of the integration test environment expect a Context (which should be a child of env.Ctx), and return an error. To avoid boilerplate, the Env exposes a set of wrappers in wrappers.go for use in scripting:
env.CreateBuffer("c/c.go", "") env.EditBuffer("c/c.go", editor.Edit{ Text: `package c`, })
These wrappers thread through Env.Ctx, and call t.Fatal on any errors.
Expressing expectations ¶
The general pattern for an integration test is to script interactions with the fake editor and sandbox, and assert that gopls behaves correctly after each state change. Unfortunately, this is complicated by the fact that state changes are communicated to gopls via unidirectional client->server notifications (didOpen, didChange, etc.), and resulting gopls behavior such as diagnostics, logs, or messages is communicated back via server->client notifications. Therefore, within integration tests we must be able to say "do this, and then eventually gopls should do that". To achieve this, the integration package provides a framework for expressing conditions that must eventually be met, in terms of the Expectation type.
To express the assertion that "eventually gopls must meet these expectations", use env.Await(...):
env.RegexpReplace("x/x.go", `package x`, `package main`) env.Await(env.DiagnosticAtRegexp("x/main.go", `fmt`))
Await evaluates the provided expectations atomically, whenever the client receives a state-changing notification from gopls. See expectation.go for a full list of available expectations.
A problem with this model is that if gopls never meets the provided expectations, the test runner will hang until the test timeout (which defaults to 10m). There are two ways to work around this poor behavior:
Use a precondition to define precisely when we expect conditions to be met. Gopls provides the OnceMet(precondition, expectations...) pattern to express ("once this precondition is met, the following expectations must all hold"). To instrument preconditions, gopls uses verbose progress notifications to inform the client about ongoing work (see CompletedWork). The most common precondition is to wait for gopls to be done processing all change notifications, for which the integration package provides the AfterChange helper. For example:
// We expect diagnostics to be cleared after gopls is done processing the // didSave notification. env.SaveBuffer("a/go.mod") env.AfterChange(EmptyDiagnostics("a/go.mod"))
Set a shorter timeout during development, if you expect to be breaking tests. By setting the environment variable GOPLS_INTEGRATION_TEST_TIMEOUT=5s, integration tests will time out after 5 seconds.
Tips & Tricks ¶
Here are some tips and tricks for working with integration tests:
- Set the environment variable GOPLS_INTEGRRATION_TEST_TIMEOUT=5s during development.
- Run tests with -short. This will only run integration tests in the default gopls execution mode.
- Use capture groups to narrow regexp positions. All regular-expression based positions (such as DiagnosticAtRegexp) will match the position of the first capture group, if any are provided. This can be used to identify a specific position in the code for a pattern that may occur in multiple places. For example `var (mu) sync.Mutex` matches the position of "mu" within the variable declaration.
- Read diagnostics into a variable to implement more complicated assertions about diagnostic state in the editor. To do this, use the pattern OnceMet(precondition, ReadDiagnostics("file.go", &d)) to capture the current diagnostics as soon as the precondition is met. This is preferable to accessing the diagnostics directly, as it avoids races.
Index ¶
- Variables
- func IgnoreTelemetryPromptWork(title, msg string) bool
- func Main(m *testing.M) (code int)
- func Run(t *testing.T, files string, f TestFunc)
- func WithOptions(opts ...RunOption) configuredRunner
- type Awaiter
- type DiagnosticFilter
- func AtPosition(name string, line, character uint32) DiagnosticFilter
- func ForFile(name string) DiagnosticFilter
- func FromSource(source string) DiagnosticFilter
- func WithMessage(substring string) DiagnosticFilter
- func WithSeverityTags(diagName string, severity protocol.DiagnosticSeverity, ...) DiagnosticFilter
- type Env
- func (e *Env) AcceptCompletion(loc protocol.Location, item protocol.CompletionItem)
- func (e *Env) AfterChange(expectations ...Expectation)
- func (e *Env) ApplyCodeAction(action protocol.CodeAction)
- func (e *Env) ApplyQuickFixes(path string, diagnostics []protocol.Diagnostic)
- func (e *Env) AtRegexp(name, pattern string) DiagnosticFilter
- func (e *Env) Await(expectations ...Expectation)
- func (e *Env) BufferText(name string) string
- func (e *Env) ChangeConfiguration(newConfig fake.EditorConfig)
- func (e *Env) ChangeWorkspaceFolders(newFolders ...string)
- func (e *Env) CheckForFileChanges()
- func (e *Env) Close()
- func (e *Env) CloseBuffer(name string)
- func (e *Env) CodeAction(loc protocol.Location, diagnostics []protocol.Diagnostic, ...) []protocol.CodeAction
- func (e *Env) CodeActionForFile(path string, diagnostics []protocol.Diagnostic) []protocol.CodeAction
- func (e *Env) CodeLens(path string) []protocol.CodeLens
- func (e *Env) Completion(loc protocol.Location) *protocol.CompletionList
- func (e *Env) CreateBuffer(name string, content string)
- func (e *Env) DocumentHighlight(loc protocol.Location) []protocol.DocumentHighlight
- func (e *Env) DocumentLink(name string) []protocol.DocumentLink
- func (e *Env) DoneDiagnosingChanges() Expectation
- func (e *Env) DoneWithChange() Expectation
- func (e *Env) DoneWithChangeWatchedFiles() Expectation
- func (e *Env) DoneWithClose() Expectation
- func (e *Env) DoneWithOpen() Expectation
- func (e *Env) DoneWithSave() Expectation
- func (e *Env) DumpGoSum(dir string)
- func (e *Env) EditBuffer(name string, edits ...protocol.TextEdit)
- func (e *Env) ExecuteCodeLensCommand(path string, cmd command.Command, result interface{})
- func (e *Env) ExecuteCommand(params *protocol.ExecuteCommandParams, result interface{})
- func (e *Env) FileContent(name string) string
- func (e *Env) FileContentAt(location protocol.Location) string
- func (e *Env) FormatBuffer(name string)
- func (e *Env) GetQuickFixes(path string, diagnostics []protocol.Diagnostic) []protocol.CodeAction
- func (e *Env) GoToDefinition(loc protocol.Location) protocol.Location
- func (e *Env) GoVersion() int
- func (e *Env) Hover(loc protocol.Location) (*protocol.MarkupContent, protocol.Location)
- func (e *Env) Implementations(loc protocol.Location) []protocol.Location
- func (e *Env) InlayHints(path string) []protocol.InlayHint
- func (e *Env) ListFiles(dir string) []string
- func (e *Env) OnceMet(precondition Expectation, mustMeets ...Expectation)
- func (e *Env) OpenFile(name string)
- func (e *Env) OrganizeImports(name string)
- func (e *Env) ReadWorkspaceFile(name string) string
- func (e *Env) References(loc protocol.Location) []protocol.Location
- func (e *Env) RegexpReplace(name, regexpStr, replace string)
- func (e *Env) RegexpSearch(name, re string) protocol.Location
- func (e *Env) RemoveWorkspaceFile(name string)
- func (e *Env) Rename(loc protocol.Location, newName string)
- func (e *Env) RenameFile(oldPath, newPath string)
- func (e *Env) RunGenerate(dir string)
- func (e *Env) RunGoCommand(verb string, args ...string)
- func (e *Env) RunGoCommandInDir(dir, verb string, args ...string)
- func (e *Env) RunGoCommandInDirWithEnv(dir string, env []string, verb string, args ...string)
- func (e *Env) SaveBuffer(name string)
- func (e *Env) SaveBufferWithoutActions(name string)
- func (e *Env) SemanticTokensFull(path string) []fake.SemanticToken
- func (e *Env) SemanticTokensRange(loc protocol.Location) []fake.SemanticToken
- func (e *Env) SetBufferContent(name string, content string)
- func (e *Env) SetSuggestionInsertReplaceMode(useReplaceMode bool)
- func (e *Env) SignatureHelp(loc protocol.Location) *protocol.SignatureHelp
- func (e *Env) StartProfile() (stop func() string)
- func (e *Env) StartedChange() Expectation
- func (e *Env) StartedChangeWatchedFiles() Expectation
- func (e *Env) Symbol(query string) []protocol.SymbolInformation
- func (e *Env) TypeDefinition(loc protocol.Location) protocol.Location
- func (e *Env) Views() []command.View
- func (e *Env) WriteWorkspaceFile(name, content string)
- func (e *Env) WriteWorkspaceFiles(files map[string]string)
- type EnvVars
- type Expectation
- func AllOf(allOf ...Expectation) Expectation
- func AnyOf(anyOf ...Expectation) Expectation
- func CompletedProgress(token protocol.ProgressToken, into *WorkStatus) Expectation
- func CompletedWork(title string, count uint64, atLeast bool) Expectation
- func Diagnostics(filters ...DiagnosticFilter) Expectation
- func FileWatchMatching(re string) Expectation
- func LogMatching(typ protocol.MessageType, re string, count int, atLeast bool) Expectation
- func NoDiagnostics(filters ...DiagnosticFilter) Expectation
- func NoErrorLogs() Expectation
- func NoFileWatchMatching(re string) Expectation
- func NoLogMatching(typ protocol.MessageType, re string) Expectation
- func NoOutstandingWork(ignore func(title, msg string) bool) Expectation
- func NoShownMessage(subString string) Expectation
- func Not(e Expectation) Expectation
- func OnceMet(precondition Expectation, mustMeets ...Expectation) Expectation
- func OutstandingWork(title, msg string) Expectation
- func ReadAllDiagnostics(into *map[string]*protocol.PublishDiagnosticsParams) Expectation
- func ReadDiagnostics(fileName string, into *protocol.PublishDiagnosticsParams) Expectation
- func ShownDocument(uri protocol.URI) Expectation
- func ShownDocuments(into *[]*protocol.ShowDocumentParams) Expectation
- func ShownMessage(containing string) Expectation
- func ShownMessageRequest(messageRegexp string) Expectation
- func StartedWork(title string, atLeast uint64) Expectation
- type FolderSettings
- type Mode
- type RunMultiple
- type RunOption
- func CapabilitiesJSON(capabilities []byte) RunOption
- func ClientName(name string) RunOption
- func FakeGoPackagesDriver(t *testing.T) RunOption
- func InGOPATH() RunOption
- func MessageResponder(...) RunOption
- func Modes(modes Mode) RunOption
- func NoLogsOnError() RunOption
- func ProxyFiles(txt string) RunOption
- func WindowsLineEndings() RunOption
- func WorkspaceFolders(relFolders ...string) RunOption
- func WriteGoSum(dirs ...string) RunOption
- type Runner
- type Settings
- type State
- type TestFunc
- type Verdict
- type WorkStatus
Constants ¶
This section is empty.
Variables ¶
var ( // InitialWorkspaceLoad is an expectation that the workspace initial load has // completed. It is verified via workdone reporting. InitialWorkspaceLoad = CompletedWork(server.DiagnosticWorkTitle(server.FromInitialWorkspaceLoad), 1, false) )
Functions ¶
func IgnoreTelemetryPromptWork ¶
IgnoreTelemetryPromptWork may be used in conjunction with NoOutStandingWork to ignore the telemetry prompt.
func WithOptions ¶
func WithOptions(opts ...RunOption) configuredRunner
Types ¶
type Awaiter ¶
type Awaiter struct {
// contains filtered or unexported fields
}
An Awaiter keeps track of relevant LSP state, so that it may be asserted upon with Expectations.
Wire it into a fake.Editor using Awaiter.Hooks().
TODO(rfindley): consider simply merging Awaiter with the fake.Editor. It probably is not worth its own abstraction.
func NewAwaiter ¶
func (*Awaiter) Await ¶
func (a *Awaiter) Await(ctx context.Context, expectations ...Expectation) error
Await waits for all expectations to simultaneously be met. It should only be called from the main test goroutine.
func (*Awaiter) Hooks ¶
func (a *Awaiter) Hooks() fake.ClientHooks
Hooks returns LSP client hooks required for awaiting asynchronous expectations.
func (*Awaiter) ResetShownDocuments ¶
func (a *Awaiter) ResetShownDocuments()
ResetShownDocuments resets the set of accumulated ShownDocuments seen so far.
type DiagnosticFilter ¶
type DiagnosticFilter struct {
// contains filtered or unexported fields
}
A DiagnosticFilter filters the set of diagnostics, for assertion with Diagnostics or NoDiagnostics.
func AtPosition ¶
func AtPosition(name string, line, character uint32) DiagnosticFilter
AtPosition filters to diagnostics at location name:line:character, for a sandbox-relative path name.
Line and character are 0-based, and character measures UTF-16 codes.
Note: prefer the more readable AtRegexp.
func ForFile ¶
func ForFile(name string) DiagnosticFilter
ForFile filters to diagnostics matching the sandbox-relative file name.
func FromSource ¶
func FromSource(source string) DiagnosticFilter
FromSource filters to diagnostics matching the given diagnostics source.
func WithMessage ¶
func WithMessage(substring string) DiagnosticFilter
WithMessage filters to diagnostics whose message contains the given substring.
func WithSeverityTags ¶
func WithSeverityTags(diagName string, severity protocol.DiagnosticSeverity, tags []protocol.DiagnosticTag) DiagnosticFilter
WithSeverityTags filters to diagnostics whose severity and tags match the given expectation.
type Env ¶
type Env struct { T testing.TB // TODO(rfindley): rename to TB Ctx context.Context // Most tests should not need to access the scratch area, editor, server, or // connection, but they are available if needed. Sandbox *fake.Sandbox Server servertest.Connector // Editor is owned by the Env, and shut down Editor *fake.Editor Awaiter *Awaiter }
Env holds the building blocks of an editor testing environment, providing wrapper methods that hide the boilerplate of plumbing contexts and checking errors.
func ConnectGoplsEnv ¶
func ConnectGoplsEnv(t testing.TB, ctx context.Context, sandbox *fake.Sandbox, config fake.EditorConfig, connector servertest.Connector) *Env
ConnectGoplsEnv creates a new Gopls environment for the given sandbox, editor config, and server connector.
TODO(rfindley): significantly refactor the way testing environments are constructed.
func (*Env) AcceptCompletion ¶
func (e *Env) AcceptCompletion(loc protocol.Location, item protocol.CompletionItem)
AcceptCompletion accepts a completion for the given item at the given position.
func (*Env) AfterChange ¶
func (e *Env) AfterChange(expectations ...Expectation)
AfterChange expects that the given expectations will be met after all state-changing notifications have been processed by the server. Specifically, it awaits the awaits completion of the process of diagnosis after the following notifications, before checking the given expectations:
- textDocument/didOpen
- textDocument/didChange
- textDocument/didSave
- textDocument/didClose
- workspace/didChangeWatchedFiles
- workspace/didChangeConfiguration
func (*Env) ApplyCodeAction ¶
func (e *Env) ApplyCodeAction(action protocol.CodeAction)
ApplyCodeAction applies the given code action.
func (*Env) ApplyQuickFixes ¶
func (e *Env) ApplyQuickFixes(path string, diagnostics []protocol.Diagnostic)
ApplyQuickFixes processes the quickfix codeAction, calling t.Fatal on any error.
func (*Env) AtRegexp ¶
func (e *Env) AtRegexp(name, pattern string) DiagnosticFilter
AtRegexp filters to diagnostics in the file with sandbox-relative path name, at the first position matching the given regexp pattern.
TODO(rfindley): pass in the editor to expectations, so that they may depend on editor state and AtRegexp can be a function rather than a method.
func (*Env) Await ¶
func (e *Env) Await(expectations ...Expectation)
Await blocks until the given expectations are all simultaneously met.
Generally speaking Await should be avoided because it blocks indefinitely if gopls ends up in a state where the expectations are never going to be met. Use AfterChange or OnceMet instead, so that the runner knows when to stop waiting.
func (*Env) BufferText ¶
BufferText returns the current buffer contents for the file with the given relative path, calling t.Fatal if the file is not open in a buffer.
func (*Env) ChangeConfiguration ¶
func (e *Env) ChangeConfiguration(newConfig fake.EditorConfig)
ChangeConfiguration updates the editor config, calling t.Fatal on any error.
func (*Env) ChangeWorkspaceFolders ¶
ChangeWorkspaceFolders updates the editor workspace folders, calling t.Fatal on any error.
func (*Env) CheckForFileChanges ¶
func (e *Env) 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 (*Env) Close ¶
func (e *Env) Close()
Close shuts down the editor session and cleans up the sandbox directory, calling t.Error on any error.
func (*Env) CloseBuffer ¶
CloseBuffer closes an editor buffer without saving, calling t.Fatal on any error.
func (*Env) CodeAction ¶
func (e *Env) CodeAction(loc protocol.Location, diagnostics []protocol.Diagnostic, trigger protocol.CodeActionTriggerKind) []protocol.CodeAction
CodeAction calls textDocument/codeAction for a selection, and calls t.Fatal if there were errors.
func (*Env) CodeActionForFile ¶
func (e *Env) CodeActionForFile(path string, diagnostics []protocol.Diagnostic) []protocol.CodeAction
CodeActionForFile calls textDocument/codeAction for the entire file, and calls t.Fatal if there were errors.
func (*Env) CodeLens ¶
CodeLens calls textDocument/codeLens for the given path, calling t.Fatal on any error.
func (*Env) Completion ¶
func (e *Env) Completion(loc protocol.Location) *protocol.CompletionList
Completion executes a completion request on the server.
func (*Env) CreateBuffer ¶
CreateBuffer creates a buffer in the editor, calling t.Fatal on any error.
func (*Env) DocumentHighlight ¶
func (e *Env) DocumentHighlight(loc protocol.Location) []protocol.DocumentHighlight
func (*Env) DocumentLink ¶
func (e *Env) DocumentLink(name string) []protocol.DocumentLink
func (*Env) DoneDiagnosingChanges ¶
func (e *Env) DoneDiagnosingChanges() Expectation
DoneDiagnosingChanges expects that diagnostics are complete from common change notifications: didOpen, didChange, didSave, didChangeWatchedFiles, and didClose.
This can be used when multiple notifications may have been sent, such as when a didChange is immediately followed by a didSave. It is insufficient to simply await NoOutstandingWork, because the LSP client has no control over when the server starts processing a notification. Therefore, we must keep track of
func (*Env) DoneWithChange ¶
func (e *Env) DoneWithChange() Expectation
DoneWithChange expects all didChange notifications currently sent by the editor to be completely processed.
func (*Env) DoneWithChangeWatchedFiles ¶
func (e *Env) DoneWithChangeWatchedFiles() Expectation
DoneWithChangeWatchedFiles expects all didChangeWatchedFiles notifications currently sent by the editor to be completely processed.
func (*Env) DoneWithClose ¶
func (e *Env) DoneWithClose() Expectation
DoneWithClose expects all didClose notifications currently sent by the editor to be completely processed.
func (*Env) DoneWithOpen ¶
func (e *Env) DoneWithOpen() Expectation
DoneWithOpen expects all didOpen notifications currently sent by the editor to be completely processed.
func (*Env) DoneWithSave ¶
func (e *Env) DoneWithSave() Expectation
DoneWithSave expects all didSave notifications currently sent by the editor to be completely processed.
func (*Env) DumpGoSum ¶
DumpGoSum prints the correct go.sum contents for dir in txtar format, for use in creating integration tests.
func (*Env) EditBuffer ¶
EditBuffer applies edits to an editor buffer, calling t.Fatal on any error.
func (*Env) ExecuteCodeLensCommand ¶
ExecuteCodeLensCommand executes the command for the code lens matching the given command name.
func (*Env) ExecuteCommand ¶
func (e *Env) ExecuteCommand(params *protocol.ExecuteCommandParams, result interface{})
func (*Env) FileContent ¶
FileContent returns the file content for name that applies to the current editing session: it returns the buffer content for an open file, the on-disk content for an unopened file, or "" for a non-existent file.
func (*Env) FileContentAt ¶
FileContentAt returns the file content at the given location, using the file's mapper.
func (*Env) FormatBuffer ¶
FormatBuffer formats the editor buffer, calling t.Fatal on any error.
func (*Env) GetQuickFixes ¶
func (e *Env) GetQuickFixes(path string, diagnostics []protocol.Diagnostic) []protocol.CodeAction
GetQuickFixes returns the available quick fix code actions.
func (*Env) GoToDefinition ¶
GoToDefinition goes to definition in the editor, calling t.Fatal on any error. It returns the path and position of the resulting jump.
TODO(rfindley): rename this to just 'Definition'.
func (*Env) Hover ¶
Hover in the editor, calling t.Fatal on any error. It may return (nil, zero) even on success.
func (*Env) Implementations ¶
Implementations wraps Editor.Implementations, calling t.Fatal on any error.
func (*Env) InlayHints ¶
InlayHints calls textDocument/inlayHints for the given path, calling t.Fatal on any error.
func (*Env) ListFiles ¶
ListFiles lists relative paths to files in the given directory. It calls t.Fatal on any error.
func (*Env) OnceMet ¶
func (e *Env) OnceMet(precondition Expectation, mustMeets ...Expectation)
OnceMet blocks until the precondition is met by the state or becomes unmeetable. If it was met, OnceMet checks that the state meets all expectations in mustMeets.
func (*Env) OrganizeImports ¶
OrganizeImports processes the source.organizeImports codeAction, calling t.Fatal on any error.
func (*Env) ReadWorkspaceFile ¶
ReadWorkspaceFile reads a file from the workspace, calling t.Fatal on any error.
func (*Env) References ¶
References wraps Editor.References, calling t.Fatal on any error.
func (*Env) RegexpReplace ¶
RegexpReplace replaces the first group in the first match of regexpStr with the replace text, calling t.Fatal on any error.
func (*Env) RegexpSearch ¶
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 (*Env) RemoveWorkspaceFile ¶
RemoveWorkspaceFile deletes a file on disk but does nothing in the editor. It calls t.Fatal on any error.
func (*Env) RenameFile ¶
RenameFile wraps Editor.RenameFile, calling t.Fatal on any error.
func (*Env) RunGenerate ¶
RunGenerate runs "go generate" in 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 (*Env) RunGoCommand ¶
RunGoCommand runs the given command in the sandbox's default working directory.
func (*Env) RunGoCommandInDir ¶
RunGoCommandInDir is like RunGoCommand, but executes in the given relative directory of the sandbox.
func (*Env) RunGoCommandInDirWithEnv ¶
RunGoCommandInDirWithEnv is like RunGoCommand, but executes in the given relative directory of the sandbox with the given additional environment variables.
func (*Env) SaveBuffer ¶
SaveBuffer saves an editor buffer, calling t.Fatal on any error.
func (*Env) SaveBufferWithoutActions ¶
func (*Env) SemanticTokensFull ¶
func (e *Env) SemanticTokensFull(path string) []fake.SemanticToken
SemanticTokensFull invokes textDocument/semanticTokens/full, calling t.Fatal on any error.
func (*Env) SemanticTokensRange ¶
func (e *Env) SemanticTokensRange(loc protocol.Location) []fake.SemanticToken
SemanticTokensRange invokes textDocument/semanticTokens/range, calling t.Fatal on any error.
func (*Env) SetBufferContent ¶
func (*Env) SetSuggestionInsertReplaceMode ¶
func (*Env) SignatureHelp ¶
func (e *Env) SignatureHelp(loc protocol.Location) *protocol.SignatureHelp
SignatureHelp wraps Editor.SignatureHelp, calling t.Fatal on error
func (*Env) StartProfile ¶
StartProfile starts a CPU profile with the given name, using the gopls.start_profile custom command. It calls t.Fatal on any error.
The resulting stop function must be called to stop profiling (using the gopls.stop_profile custom command).
func (*Env) StartedChange ¶
func (e *Env) StartedChange() Expectation
StartedChange expects that the server has at least started processing all didChange notifications sent from the client.
func (*Env) StartedChangeWatchedFiles ¶
func (e *Env) StartedChangeWatchedFiles() Expectation
StartedChangeWatchedFiles expects that the server has at least started processing all didChangeWatchedFiles notifications sent from the client.
func (*Env) Symbol ¶
func (e *Env) Symbol(query string) []protocol.SymbolInformation
Symbol calls workspace/symbol
func (*Env) WriteWorkspaceFile ¶
WriteWorkspaceFile writes a file to disk but does nothing in the editor. It calls t.Fatal on any error.
func (*Env) WriteWorkspaceFiles ¶
WriteWorkspaceFiles deletes a file on disk but does nothing in the editor. It calls t.Fatal on any error.
type EnvVars ¶
EnvVars sets environment variables for the LSP session. When applying these variables to the session, the special string $SANDBOX_WORKDIR is replaced by the absolute path to the sandbox working directory.
type Expectation ¶
type Expectation struct { Check func(State) Verdict // Description holds a noun-phrase identifying what the expectation checks. // // TODO(rfindley): revisit existing descriptions to ensure they compose nicely. Description string }
An Expectation is an expected property of the state of the LSP client. The Check function reports whether the property is met.
Expectations are combinators. By composing them, tests may express complex expectations in terms of simpler ones.
TODO(rfindley): as expectations are combined, it becomes harder to identify why they failed. A better signature for Check would be
func(State) (Verdict, string)
returning a reason for the verdict that can be composed similarly to descriptions.
func AllOf ¶
func AllOf(allOf ...Expectation) Expectation
AllOf expects that all given expectations are met.
TODO(rfindley): the problem with these types of combinators (OnceMet, AnyOf and AllOf) is that we lose the information of *why* they failed: the Awaiter is not smart enough to look inside.
Refactor the API such that the Check function is responsible for explaining why an expectation failed. This should allow us to significantly improve test output: we won't need to summarize state at all, as the verdict explanation itself should describe clearly why the expectation not met.
func AnyOf ¶
func AnyOf(anyOf ...Expectation) Expectation
AnyOf returns an expectation that is satisfied when any of the given expectations is met.
func CompletedProgress ¶
func CompletedProgress(token protocol.ProgressToken, into *WorkStatus) Expectation
CompletedProgress expects that workDone progress is complete for the given progress token. When non-nil WorkStatus is provided, it will be filled when the expectation is met.
If the token is not a progress token that the client has seen, this expectation is Unmeetable.
func CompletedWork ¶
func CompletedWork(title string, count uint64, atLeast bool) Expectation
CompletedWork expects a work item to have been completed >= atLeast times.
Since the Progress API doesn't include any hidden metadata, we must use the progress notification title to identify the work we expect to be completed.
func Diagnostics ¶
func Diagnostics(filters ...DiagnosticFilter) Expectation
Diagnostics asserts that there is at least one diagnostic matching the given filters.
func FileWatchMatching ¶
func FileWatchMatching(re string) Expectation
FileWatchMatching expects that a file registration matches re.
func LogMatching ¶
func LogMatching(typ protocol.MessageType, re string, count int, atLeast bool) Expectation
LogMatching asserts that the client has received a log message of type typ matching the regexp re a certain number of times.
The count argument specifies the expected number of matching logs. If atLeast is set, this is a lower bound, otherwise there must be exactly count matching logs.
Logs are asynchronous to other LSP messages, so this expectation should not be used with combinators such as OnceMet or AfterChange that assert on ordering with respect to other operations.
func NoDiagnostics ¶
func NoDiagnostics(filters ...DiagnosticFilter) Expectation
NoDiagnostics asserts that there are no diagnostics matching the given filters. Notably, if no filters are supplied this assertion checks that there are no diagnostics at all, for any file.
func NoErrorLogs ¶
func NoErrorLogs() Expectation
NoErrorLogs asserts that the client has not received any log messages of error severity.
func NoFileWatchMatching ¶
func NoFileWatchMatching(re string) Expectation
NoFileWatchMatching expects that no file registration matches re.
func NoLogMatching ¶
func NoLogMatching(typ protocol.MessageType, re string) Expectation
NoLogMatching asserts that the client has not received a log message of type typ matching the regexp re. If re is an empty string, any log message is considered a match.
func NoOutstandingWork ¶
func NoOutstandingWork(ignore func(title, msg string) bool) Expectation
NoOutstandingWork asserts that there is no work initiated using the LSP $/progress API that has not completed.
If non-nil, the ignore func is used to ignore certain work items for the purpose of this check.
TODO(rfindley): consider refactoring to treat outstanding work the same way we treat diagnostics: with an algebra of filters.
func NoShownMessage ¶
func NoShownMessage(subString string) Expectation
NoShownMessage asserts that the editor has not received a ShowMessage.
func Not ¶
func Not(e Expectation) Expectation
Not inverts the sense of an expectation: a met expectation is unmet, and an unmet expectation is met.
func OnceMet ¶
func OnceMet(precondition Expectation, mustMeets ...Expectation) Expectation
OnceMet returns an Expectation that, once the precondition is met, asserts that mustMeet is met.
func OutstandingWork ¶
func OutstandingWork(title, msg string) Expectation
OutstandingWork expects a work item to be outstanding. The given title must be an exact match, whereas the given msg must only be contained in the work item's message.
func ReadAllDiagnostics ¶
func ReadAllDiagnostics(into *map[string]*protocol.PublishDiagnosticsParams) Expectation
ReadAllDiagnostics is an expectation that stores all published diagnostics into the provided map, whenever it is evaluated.
It can be used in combination with OnceMet or AfterChange to capture the state of diagnostics when other expectations are satisfied.
func ReadDiagnostics ¶
func ReadDiagnostics(fileName string, into *protocol.PublishDiagnosticsParams) Expectation
ReadDiagnostics is an Expectation that stores the current diagnostics for fileName in into, whenever it is evaluated.
It can be used in combination with OnceMet or AfterChange to capture the state of diagnostics when other expectations are satisfied.
func ShownDocument ¶
func ShownDocument(uri protocol.URI) Expectation
ShownDocument asserts that the client has received a ShowDocumentRequest for the given URI.
func ShownDocuments ¶
func ShownDocuments(into *[]*protocol.ShowDocumentParams) Expectation
ShownDocuments is an expectation that appends each showDocument request into the provided slice, whenever it is evaluated.
It can be used in combination with OnceMet or AfterChange to capture the set of showDocument requests when other expectations are satisfied.
func ShownMessage ¶
func ShownMessage(containing string) Expectation
ShownMessage asserts that the editor has received a ShowMessageRequest containing the given substring.
func ShownMessageRequest ¶
func ShownMessageRequest(messageRegexp string) Expectation
ShownMessageRequest asserts that the editor has received a ShowMessageRequest with message matching the given regular expression.
func StartedWork ¶
func StartedWork(title string, atLeast uint64) Expectation
StartedWork expect a work item to have been started >= atLeast times.
See CompletedWork.
type FolderSettings ¶
FolderSettings defines per-folder workspace settings, keyed by relative path to the folder.
Use in conjunction with WorkspaceFolders to have different settings for different folders.
type Mode ¶
type Mode int
Mode is a bitmask that defines for which execution modes a test should run.
Each mode controls several aspects of gopls' configuration:
- Which server options to use for gopls sessions
- Whether to use a shared cache
- Whether to use a shared server
- Whether to run the server in-process or in a separate process
The behavior of each mode with respect to these aspects is summarized below. TODO(rfindley, cleanup): rather than using arbitrary names for these modes, we can compose them explicitly out of the features described here, allowing individual tests more freedom in constructing problematic execution modes. For example, a test could assert on a certain behavior when running on a separate process. Moreover, we could unify 'Modes' with 'Options', and use RunMultiple rather than a hard-coded loop through modes.
Mode | Options | Shared Cache? | Shared Server? | In-process? --------------------------------------------------------------------------- Default | Default | Y | N | Y Forwarded | Default | Y | Y | Y SeparateProcess | Default | Y | Y | N
const ( // Default mode runs gopls with the default options, communicating over pipes // to emulate the lsp sidecar execution mode, which communicates over // stdin/stdout. // // It uses separate servers for each test, but a shared cache, to avoid // duplicating work when processing GOROOT. Default Mode = 1 << iota // Forwarded uses the default options, but forwards connections to a shared // in-process gopls server. Forwarded // SeparateProcess uses the default options, but forwards connection to an // external gopls daemon. // // Only supported on GOOS=linux. SeparateProcess )
func DefaultModes ¶
func DefaultModes() Mode
DefaultModes returns the default modes to run for each regression test (they may be reconfigured by the tests themselves).
type RunMultiple ¶
type RunMultiple []struct { Name string Runner interface { Run(t *testing.T, files string, f TestFunc) } }
RunMultiple runs a test multiple times, with different options. The runner should be constructed with WithOptions.
TODO(rfindley): replace Modes with selective use of RunMultiple.
type RunOption ¶
type RunOption interface {
// contains filtered or unexported methods
}
A RunOption augments the behavior of the test runner.
func CapabilitiesJSON ¶
CapabilitiesJSON sets the capabalities json.
func FakeGoPackagesDriver ¶
FakeGoPackagesDriver configures gopls to run with a fake GOPACKAGESDRIVER environment variable.
func InGOPATH ¶
func InGOPATH() RunOption
InGOPATH configures the workspace working directory to be GOPATH, rather than a separate working directory for use with modules.
func MessageResponder ¶
func MessageResponder(f func(*protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error)) RunOption
MessageResponder configures the editor to respond to window/showMessageRequest messages using the provided function.
func Modes ¶
Modes configures the execution modes that the test should run in.
By default, modes are configured by the test runner. If this option is set, it overrides the set of default modes and the test runs in exactly these modes.
func NoLogsOnError ¶
func NoLogsOnError() RunOption
NoLogsOnError turns off dumping the LSP logs on test failures.
func ProxyFiles ¶
ProxyFiles configures a file proxy using the given txtar-encoded string.
func WindowsLineEndings ¶
func WindowsLineEndings() RunOption
WindowsLineEndings configures the editor to use windows line endings.
func WorkspaceFolders ¶
WorkspaceFolders configures the workdir-relative workspace folders or uri to send to the LSP server. By default the editor sends a single workspace folder corresponding to the workdir root. To explicitly configure no workspace folders, use WorkspaceFolders with no arguments.
func WriteGoSum ¶
WriteGoSum causes the environment to write a go.sum file for the requested relative directories (via `go list -mod=mod`), before starting gopls.
Useful for tests that use ProxyFiles, but don't care about crafting the go.sum content.
type Runner ¶
type Runner struct { // Configuration DefaultModes Mode // modes to run for each test Timeout time.Duration // per-test timeout, if set PrintGoroutinesOnFailure bool // whether to dump goroutines on test failure SkipCleanup bool // if set, don't delete test data directories when the test exits // contains filtered or unexported fields }
A Runner runs tests in gopls execution environments, as specified by its modes. For modes that share state (for example, a shared cache or common remote), any tests that execute on the same Runner will share the same state.
type Settings ¶
type Settings map[string]interface{}
Settings sets user-provided configuration for the LSP server.
As a special case, the env setting must not be provided via Settings: use EnvVars instead.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State encapsulates the server state TODO: explain more
type Verdict ¶
type Verdict int
A Verdict is the result of checking an expectation against the current editor state.
const ( // Met indicates that an expectation is satisfied by the current state. Met Verdict = iota // Unmet indicates that an expectation is not currently met, but could be met // in the future. Unmet // Unmeetable indicates that an expectation cannot be satisfied in the // future. Unmeetable )
Order matters for the following constants: verdicts are sorted in order of decisiveness.
type WorkStatus ¶
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
The bench package implements benchmarks for various LSP operations.
|
The bench package implements benchmarks for various LSP operations. |
Package fake provides a fake implementation of an LSP-enabled text editor, its LSP client plugin, and a Sandbox environment for use in integration tests.
|
Package fake provides a fake implementation of an LSP-enabled text editor, its LSP client plugin, and a Sandbox environment for use in integration tests. |
glob
Package glob implements an LSP-compliant glob pattern matcher for testing.
|
Package glob implements an LSP-compliant glob pattern matcher for testing. |