integration

package
v0.0.0-...-0c86418 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 17, 2024 License: MIT Imports: 37 Imported by: 0

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:

  1. 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"))

  2. 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:

  1. Set the environment variable GOPLS_INTEGRRATION_TEST_TIMEOUT=5s during development.
  2. Run tests with -short. This will only run integration tests in the default gopls execution mode.
  3. 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.
  4. 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

Constants

This section is empty.

Variables

View Source
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

func IgnoreTelemetryPromptWork(title, msg string) bool

IgnoreTelemetryPromptWork may be used in conjunction with NoOutStandingWork to ignore the telemetry prompt.

func Main

func Main(m *testing.M) (code int)

Main sets up and tears down the shared integration test state.

func Run

func Run(t *testing.T, files string, f TestFunc)

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 NewAwaiter(workdir *fake.Workdir) *Awaiter

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

func (e *Env) BufferText(name string) string

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

func (e *Env) ChangeWorkspaceFolders(newFolders ...string)

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

func (e *Env) CloseBuffer(name string)

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

func (e *Env) CodeLens(path string) []protocol.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

func (e *Env) CreateBuffer(name string, content string)

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 (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

func (e *Env) DumpGoSum(dir string)

DumpGoSum prints the correct go.sum contents for dir in txtar format, for use in creating integration tests.

func (*Env) EditBuffer

func (e *Env) EditBuffer(name string, edits ...protocol.TextEdit)

EditBuffer applies edits to an editor buffer, calling t.Fatal on any error.

func (*Env) ExecuteCodeLensCommand

func (e *Env) ExecuteCodeLensCommand(path string, cmd command.Command, result interface{})

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

func (e *Env) FileContent(name string) string

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

func (e *Env) FileContentAt(location protocol.Location) string

FileContentAt returns the file content at the given location, using the file's mapper.

func (*Env) FormatBuffer

func (e *Env) FormatBuffer(name string)

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

func (e *Env) GoToDefinition(loc protocol.Location) protocol.Location

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) GoVersion

func (e *Env) GoVersion() int

GoVersion checks the version of the go command. It returns the X in Go 1.X.

func (*Env) Hover

Hover in the editor, calling t.Fatal on any error. It may return (nil, zero) even on success.

func (*Env) Implementations

func (e *Env) Implementations(loc protocol.Location) []protocol.Location

Implementations wraps Editor.Implementations, calling t.Fatal on any error.

func (*Env) InlayHints

func (e *Env) InlayHints(path string) []protocol.InlayHint

InlayHints calls textDocument/inlayHints for the given path, calling t.Fatal on any error.

func (*Env) ListFiles

func (e *Env) ListFiles(dir string) []string

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) OpenFile

func (e *Env) OpenFile(name string)

OpenFile opens a file in the editor, calling t.Fatal on any error.

func (*Env) OrganizeImports

func (e *Env) OrganizeImports(name string)

OrganizeImports processes the source.organizeImports codeAction, calling t.Fatal on any error.

func (*Env) ReadWorkspaceFile

func (e *Env) ReadWorkspaceFile(name string) string

ReadWorkspaceFile reads a file from the workspace, calling t.Fatal on any error.

func (*Env) References

func (e *Env) References(loc protocol.Location) []protocol.Location

References wraps Editor.References, calling t.Fatal on any error.

func (*Env) RegexpReplace

func (e *Env) RegexpReplace(name, regexpStr, replace string)

RegexpReplace replaces the first group in the first match of regexpStr with the replace text, calling t.Fatal on any error.

func (*Env) RegexpSearch

func (e *Env) RegexpSearch(name, re string) protocol.Location

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

func (e *Env) RemoveWorkspaceFile(name string)

RemoveWorkspaceFile deletes a file on disk but does nothing in the editor. It calls t.Fatal on any error.

func (*Env) Rename

func (e *Env) Rename(loc protocol.Location, newName string)

Rename wraps Editor.Rename, calling t.Fatal on any error.

func (*Env) RenameFile

func (e *Env) RenameFile(oldPath, newPath string)

RenameFile wraps Editor.RenameFile, calling t.Fatal on any error.

func (*Env) RunGenerate

func (e *Env) RunGenerate(dir string)

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

func (e *Env) RunGoCommand(verb string, args ...string)

RunGoCommand runs the given command in the sandbox's default working directory.

func (*Env) RunGoCommandInDir

func (e *Env) RunGoCommandInDir(dir, verb string, args ...string)

RunGoCommandInDir is like RunGoCommand, but executes in the given relative directory of the sandbox.

func (*Env) RunGoCommandInDirWithEnv

func (e *Env) RunGoCommandInDirWithEnv(dir string, env []string, verb string, args ...string)

RunGoCommandInDirWithEnv is like RunGoCommand, but executes in the given relative directory of the sandbox with the given additional environment variables.

func (*Env) SaveBuffer

func (e *Env) SaveBuffer(name string)

SaveBuffer saves an editor buffer, calling t.Fatal on any error.

func (*Env) SaveBufferWithoutActions

func (e *Env) SaveBufferWithoutActions(name string)

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 (e *Env) SetBufferContent(name string, content string)

func (*Env) SetSuggestionInsertReplaceMode

func (e *Env) SetSuggestionInsertReplaceMode(useReplaceMode bool)

func (*Env) SignatureHelp

func (e *Env) SignatureHelp(loc protocol.Location) *protocol.SignatureHelp

SignatureHelp wraps Editor.SignatureHelp, calling t.Fatal on error

func (*Env) StartProfile

func (e *Env) StartProfile() (stop func() string)

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) TypeDefinition

func (e *Env) TypeDefinition(loc protocol.Location) protocol.Location

func (*Env) Views

func (e *Env) Views() []command.View

Views returns the server's views.

func (*Env) WriteWorkspaceFile

func (e *Env) WriteWorkspaceFile(name, content string)

WriteWorkspaceFile writes a file to disk but does nothing in the editor. It calls t.Fatal on any error.

func (*Env) WriteWorkspaceFiles

func (e *Env) WriteWorkspaceFiles(files map[string]string)

WriteWorkspaceFiles deletes a file on disk but does nothing in the editor. It calls t.Fatal on any error.

type EnvVars

type EnvVars map[string]string

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

type FolderSettings map[string]Settings

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).

func (Mode) String

func (m Mode) String() string

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.

func (RunMultiple) Run

func (r RunMultiple) Run(t *testing.T, files string, f TestFunc)

type RunOption

type RunOption interface {
	// contains filtered or unexported methods
}

A RunOption augments the behavior of the test runner.

func CapabilitiesJSON

func CapabilitiesJSON(capabilities []byte) RunOption

CapabilitiesJSON sets the capabalities json.

func ClientName

func ClientName(name string) RunOption

ClientName sets the LSP client name.

func FakeGoPackagesDriver

func FakeGoPackagesDriver(t *testing.T) RunOption

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

MessageResponder configures the editor to respond to window/showMessageRequest messages using the provided function.

func Modes

func Modes(modes Mode) RunOption

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

func ProxyFiles(txt string) RunOption

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

func WorkspaceFolders(relFolders ...string) RunOption

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

func WriteGoSum(dirs ...string) RunOption

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.

func (*Runner) Close

func (r *Runner) Close() error

Close cleans up resource that have been allocated to this workspace.

func (*Runner) Run

func (r *Runner) Run(t *testing.T, files string, test TestFunc, opts ...RunOption)

Run executes the test function in the default configured gopls execution modes. For each a test run, a new workspace is created containing the un-txtared files specified by filedata.

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

func (State) String

func (s State) String() string

This method, provided for debugging, accesses mutable fields without a lock, so it must not be called concurrent with any State mutation.

type TestFunc

type TestFunc func(t *testing.T, env *Env)

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.

func (Verdict) String

func (v Verdict) String() string

type WorkStatus

type WorkStatus struct {
	// Last seen message from either `begin` or `report` progress.
	Msg string
	// Message sent with `end` progress message.
	EndMsg string
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL