tools: Index | Files

package regtest

import ""

Package regtest provides a framework for writing gopls regression tests.

User reported regressions are often 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". In these cases reproducing, diagnosing, and writing a test to protect against this regression can be difficult.

The regtest package provides an API for developers to express these types of user interactions in ordinary Go tests, validate them, and run them in a variety of execution modes (see gopls/doc/ for more information on execution modes). This is achieved roughly as follows:

+ the Runner type starts and connects to a gopls instance for each
  configured execution mode.
+ the Env type provides a collection of resources to use in writing tests
  (for example a temporary working directory and fake text editor)
+ user interactions with these resources are scripted using test wrappers
  around the API provided by the

Regressions are expressed in terms of Expectations, which at a high level are conditions that we expect to be met (or not to be met) at some point after performing the interactions in the test. This is necessary because the LSP is by construction asynchronous: both client and server can send eachother notifications without formal acknowledgement that they have been fully processed.

Simple Expectations may be combined to match specific conditions reported by the user. In the example above, a regtest validating that the user-reported bug had been fixed would "expect" that the editor never displays the confusing diagnostic.


Package Files

doc.go env.go runner.go wrappers.go

type DiagnosticExpectation Uses

type DiagnosticExpectation struct {
    // contains filtered or unexported fields

A DiagnosticExpectation is a condition that must be met by the current set of diagnostics for a file.

func DiagnosticAt Uses

func DiagnosticAt(name string, line, col int) DiagnosticExpectation

DiagnosticAt asserts that there is a diagnostic entry at the position specified by line and col, for the workdir-relative path name.

func (DiagnosticExpectation) Check Uses

func (e DiagnosticExpectation) Check(s State) (Verdict, interface{})

Check implements the Expectation interface.

func (DiagnosticExpectation) Description Uses

func (e DiagnosticExpectation) Description() string

Description implements the Expectation interface.

type Env Uses

type Env struct {
    T   *testing.T
    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
    Editor  *fake.Editor
    Server  servertest.Connector
    // contains filtered or unexported fields

Env holds an initialized fake Editor, Workspace, and Server, which may be used for writing tests. It also provides adapter methods that call t.Fatal on any error, so that tests for the happy path may be written without checking errors.

func NewEnv Uses

func NewEnv(ctx context.Context, t *testing.T, scratch *fake.Sandbox, ts servertest.Connector, editorConfig fake.EditorConfig) *Env

NewEnv creates a new test environment using the given scratch environment and gopls server.

func (*Env) AnyDiagnosticAtCurrentVersion Uses

func (e *Env) AnyDiagnosticAtCurrentVersion(name string) DiagnosticExpectation

AnyDiagnosticAtCurrentVersion asserts that there is a diagnostic report for the current edited version of the buffer corresponding to the given workdir-relative pathname.

func (*Env) ApplyQuickFixes Uses

func (e *Env) ApplyQuickFixes(path string, diagnostics []protocol.Diagnostic)

ApplyQuickFixes processes the quickfix codeAction, calling t.Fatal on any error.

func (*Env) Await Uses

func (e *Env) Await(expectations ...Expectation) []interface{}

Await waits for all expectations to simultaneously be met. It should only be called from the main test goroutine.

func (*Env) ChangeEnv Uses

func (e *Env) ChangeEnv(envvars ...string)

ChangeEnv modifies the editor environment and reconfigures the LSP client. TODO: extend this to "ChangeConfiguration", once we refactor the way editor configuration is defined.

func (*Env) CheckForFileChanges Uses

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) CloseBuffer Uses

func (e *Env) CloseBuffer(name string)

CloseBuffer closes an editor buffer without saving, calling t.Fatal on any error.

func (*Env) CloseEditor Uses

func (e *Env) CloseEditor()

CloseEditor shuts down the editor, calling t.Fatal on any error.

func (*Env) CodeAction Uses

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

CodeAction calls testDocument/codeAction for the given path, and calls t.Fatal if there are errors.

func (*Env) CodeLens Uses

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

CodeLens calls textDocument/codeLens for the given path, calling t.Fatal on any error.

func (*Env) CreateBuffer Uses

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

CreateBuffer creates a buffer in the editor, calling t.Fatal on any error.

func (*Env) DiagnosticAtRegexp Uses

func (e *Env) DiagnosticAtRegexp(name, re string) DiagnosticExpectation

DiagnosticAtRegexp expects that there is a diagnostic entry at the start position matching the regexp search string re in the buffer specified by name. Note that this currently ignores the end position.

func (*Env) DiagnosticsFor Uses

func (e *Env) DiagnosticsFor(name string) *protocol.PublishDiagnosticsParams

DiagnosticsFor returns the current diagnostics for the file. It is useful after waiting on AnyDiagnosticAtCurrentVersion, when the desired diagnostic is not simply described by DiagnosticAt.

func (e *Env) DocumentLink(name string) []protocol.DocumentLink

func (*Env) EditBuffer Uses

func (e *Env) EditBuffer(name string, edits ...fake.Edit)

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

func (*Env) ExpectNow Uses

func (e *Env) ExpectNow(expectations ...Expectation)

ExpectNow asserts that the current state of the editor matches the given expectations.

It can be used together with Env.Await to allow waiting on simple expectations, followed by more detailed expectations tested by ExpectNow. For example:

env.RegexpReplace("foo.go", "a", "x")
env.ExpectNow(env.DiagnosticAtRegexp("foo.go", "x"))

This has the advantage of not timing out if the diagnostic received for "foo.go" does not match the expectation: instead it fails early.

func (*Env) FormatBuffer Uses

func (e *Env) FormatBuffer(name string)

FormatBuffer formats the editor buffer, calling t.Fatal on any error.

func (*Env) GoToDefinition Uses

func (e *Env) GoToDefinition(name string, pos fake.Pos) (string, fake.Pos)

GoToDefinition goes to definition in the editor, calling t.Fatal on any error.

func (*Env) Hover Uses

func (e *Env) Hover(name string, pos fake.Pos) (*protocol.MarkupContent, fake.Pos)

Hover in the editor, calling t.Fatal on any error.

func (*Env) OpenFile Uses

func (e *Env) OpenFile(name string)

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

func (*Env) OpenFileWithContent Uses

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

func (*Env) OrganizeImports Uses

func (e *Env) OrganizeImports(name string)

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

func (*Env) ReadWorkspaceFile Uses

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

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

func (*Env) RegexpReplace Uses

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 Uses

func (e *Env) RegexpSearch(name, re string) fake.Pos

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 Uses

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) RunGenerate Uses

func (e *Env) RunGenerate(dir string)

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 (*Env) SaveBuffer Uses

func (e *Env) SaveBuffer(name string)

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

func (*Env) Symbol Uses

func (e *Env) Symbol(query string) []fake.SymbolInformation

Symbol returns symbols matching query

func (*Env) WriteWorkspaceFile Uses

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 Uses

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 Expectation Uses

type Expectation interface {
    // Check determines whether the state of the editor satisfies the
    // expectation, returning the results that met the condition.
    Check(State) (Verdict, interface{})
    // Description is a human-readable description of the expectation.
    Description() string

An Expectation asserts that the state of the editor at a point in time matches an expected condition. This is used for signaling in tests when certain conditions in the editor are met.

func EmptyDiagnostics Uses

func EmptyDiagnostics(name string) Expectation

EmptyDiagnostics asserts that empty diagnostics are sent for the workspace-relative path name.

func NoDiagnostics Uses

func NoDiagnostics(name string) Expectation

NoDiagnostics asserts that no diagnostics are sent for the workspace-relative path name. It should be used primarily in conjunction with a OnceMet, as it has to check that all outstanding diagnostics have already been delivered.

type LogExpectation Uses

type LogExpectation struct {
    // contains filtered or unexported fields

LogExpectation is an expectation on the log messages received by the editor from gopls.

func LogMatching Uses

func LogMatching(typ protocol.MessageType, re string) LogExpectation

LogMatching asserts that the client has received a log message of type typ matching the regexp re.

func NoErrorLogs Uses

func NoErrorLogs() LogExpectation

NoErrorLogs asserts that the client has not received any log messages of error severity.

func NoLogMatching Uses

func NoLogMatching(typ protocol.MessageType, re string) LogExpectation

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 (LogExpectation) Check Uses

func (e LogExpectation) Check(s State) (Verdict, interface{})

Check implements the Expectation interface.

func (LogExpectation) Description Uses

func (e LogExpectation) Description() string

Description implements the Expectation interface.

type Mode Uses

type Mode int

Mode is a bitmask that defines for which execution modes a test should run.

const (
    // Singleton mode uses a separate in-process gopls instance for each test,
    // and communicates over pipes to mimic the gopls sidecar execution mode,
    // which communicates over stdin/stderr.
    Singleton Mode = 1 << iota

    // Forwarded forwards connections to a shared in-process gopls instance.
    // SeparateProcess forwards connection to a shared separate gopls process.
    // NormalModes are the global default execution modes, when unmodified by
    // test flags or by individual test options.
    NormalModes = Singleton | Forwarded

type RunOption Uses

type RunOption interface {
    // contains filtered or unexported methods

A RunOption augments the behavior of the test runner.

func InGOPATH Uses

func InGOPATH() RunOption

InGOPATH configures the workspace working directory to be GOPATH, rather than a separate working directory for use with modules.

func SkipCleanup Uses

func SkipCleanup() RunOption

SkipCleanup is used only for debugging: is skips cleaning up the tests state after completion.

func WithEditorConfig Uses

func WithEditorConfig(config fake.EditorConfig) RunOption

WithEditorConfig configures the editor's LSP session.

func WithModes Uses

func WithModes(modes Mode) RunOption

WithModes configures the execution modes that the test should run in.

func WithProxy Uses

func WithProxy(txt string) RunOption

WithProxy configures a file proxy using the given txtar-encoded string.

func WithTimeout Uses

func WithTimeout(d time.Duration) RunOption

WithTimeout configures a custom timeout for this test run.

func WithoutWorkspaceFolders Uses

func WithoutWorkspaceFolders() RunOption

WithoutWorkspaceFolders prevents workspace folders from being sent as part of the sandbox's initialization. It is used to simulate opening a single file in the editor, without a workspace root. In that case, the client sends neither workspace folders nor a root URI.

type Runner Uses

type Runner struct {
    DefaultModes             Mode
    Timeout                  time.Duration
    GoplsPath                string
    PrintGoroutinesOnFailure bool
    // 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) AddCloser Uses

func (r *Runner) AddCloser(closer io.Closer)

AddCloser schedules a closer to be closed at the end of the test run. This is useful for Windows in particular, as

func (*Runner) Close Uses

func (r *Runner) Close() error

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

func (*Runner) Run Uses

func (r *Runner) Run(t *testing.T, filedata string, test func(t *testing.T, e *Env), 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 SimpleExpectation Uses

type SimpleExpectation struct {
    // contains filtered or unexported fields

SimpleExpectation holds an arbitrary check func, and implements the Expectation interface.

func CompletedWork Uses

func CompletedWork(title string, atLeast int) SimpleExpectation

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 EmptyShowMessage Uses

func EmptyShowMessage(title string) SimpleExpectation

EmptyShowMessage asserts that the editor has not received a ShowMessage.

func NoOutstandingWork Uses

func NoOutstandingWork() SimpleExpectation

NoOutstandingWork asserts that there is no work initiated using the LSP $/progress API that has not completed.

func OnceMet Uses

func OnceMet(precondition Expectation, mustMeet Expectation) *SimpleExpectation

OnceMet returns an Expectation that, once the precondition is met, asserts that mustMeet is met.

func ShowMessageRequest Uses

func ShowMessageRequest(title string) SimpleExpectation

ShowMessageRequest asserts that the editor has received a ShowMessageRequest with an action item that has the given title.

func SomeShowMessage Uses

func SomeShowMessage(title string) SimpleExpectation

SomeShowMessage asserts that the editor has received a ShowMessage.

func (SimpleExpectation) Check Uses

func (e SimpleExpectation) Check(s State) (Verdict, interface{})

Check invokes e.check.

func (SimpleExpectation) Description Uses

func (e SimpleExpectation) Description() string

Description returns e.descriptin.

type State Uses

type State struct {
    // contains filtered or unexported fields

State encapsulates the server state TODO: explain more

func (State) String Uses

func (s State) String() string

type Verdict Uses

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.
    // Unmeetable indicates that an expectation cannot be satisfied in the
    // future.

Order matters for the following constants: verdicts are sorted in order of decisiveness.

func (Verdict) String Uses

func (v Verdict) String() string

Package regtest imports 24 packages (graph). Updated 2020-07-07. Refresh now. Tools for package owners.