regtest

package
v0.11.0-pre.3 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2022 License: BSD-3-Clause Imports: 37 Imported by: 0

Documentation

Overview

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.

Test package setup

The regression 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 regtest.Main function, which must be invoked by TestMain. Therefore, a minimal regtest testing package looks like this:

package lsptests

import (
	"fmt"
	"testing"

	"golang.org/x/tools/gopls/internal/hooks"
	. "golang.org/x/tools/gopls/internal/lsp/regtest"
)

func TestMain(m *testing.M) {
	Main(m, hooks.Options)
}

Writing a simple regression test

To run a regression test use the regtest.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 handle 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 Regtest Execution

The regtest 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 regression 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", fake.Edit{
	Text: `package c`,
})

These wrappers thread through Env.Ctx, and call t.Fatal on any errors.

Expressing expectations

The general pattern for a regression 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 regression tests we must be able to say "do this, and then eventually gopls should do that". To achieve this, the regtest 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 fundamental 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 regtest 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_REGTEST_TIMEOUT=5s, regression tests will time out after 5 seconds.

Tips & Tricks

Here are some tips and tricks for working with regression tests:

  1. Set the environment variable GOPLS_REGTEST_TIMEOUT=5s during development.
  2. Run tests with -short. This will only run regression 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(lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad), 1, false)
)

Functions

func Main

func Main(m *testing.M, hook func(*source.Options))

Main sets up and tears down the shared regtest 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) DiagnosticsFor

func (a *Awaiter) 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.

TODO(rfindley): this method is inherently racy. Replace usages of this method with the atomic OnceMet(..., ReadDiagnostics) pattern.

func (*Awaiter) Hooks

func (a *Awaiter) Hooks() fake.ClientHooks

type DiagnosticExpectation

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

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 NoDiagnosticWithMessage

func NoDiagnosticWithMessage(name, msg string) DiagnosticExpectation

NoDiagnosticWithMessage asserts that there is no diagnostic entry with the given message.

This should only be used in combination with OnceMet for a given condition, otherwise it may always succeed.

func (DiagnosticExpectation) Check

func (e DiagnosticExpectation) Check(s State) Verdict

Check implements the Expectation interface.

func (DiagnosticExpectation) Description

func (e DiagnosticExpectation) Description() string

Description implements the Expectation interface.

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

func (e *Env) AcceptCompletion(path string, pos fake.Pos, item protocol.CompletionItem)

AcceptCompletion accepts a completion for the given item at the given position.

func (*Env) AfterChange added in v0.11.0

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.

It awaits the completion of all anticipated work before checking the given expectations.

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

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

func (*Env) ChangeConfiguration

func (e *Env) ChangeConfiguration(newConfig fake.EditorConfig)

ChangeConfiguration updates the editor config, calling t.Fatal on any error.

func (*Env) ChangeFilesOnDisk

func (e *Env) ChangeFilesOnDisk(events []fake.FileEvent)

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) 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(path string, diagnostics []protocol.Diagnostic) []protocol.CodeAction

CodeAction calls testDocument/codeAction for the given path, and calls t.Fatal if there are 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(path string, pos fake.Pos) *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) DiagnosticAtRegexp

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

func (e *Env) DiagnosticAtRegexpFromSource(name, re, source string) DiagnosticExpectation

DiagnosticAtRegexpFromSource expects a diagnostic at the first position matching re, from the given source.

func (*Env) DiagnosticAtRegexpWithMessage

func (e *Env) DiagnosticAtRegexpWithMessage(name, re, msg string) DiagnosticExpectation

DiagnosticAtRegexpWithMessage is like DiagnosticAtRegexp, but it also checks for the content of the diagnostic message,

func (*Env) DocumentHighlight

func (e *Env) DocumentHighlight(name string, pos fake.Pos) []protocol.DocumentHighlight
func (e *Env) DocumentLink(name string) []protocol.DocumentLink

func (*Env) DoneDiagnosingChanges added in v0.11.0

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

func (*Env) EditBuffer

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

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

func (e *Env) GoSumDiagnostic(name, module string) Expectation

GoSumDiagnostic asserts that a "go.sum is out of sync" diagnostic for the given module (as formatted in a go.mod file, e.g. "example.com v1.0.0") is present.

func (*Env) GoToDefinition

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. It returns the path and position of the resulting jump.

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

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

func (e *Env) Implementations(path string, pos fake.Pos) []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) NoDiagnosticAtRegexp

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

NoDiagnosticAtRegexp expects that there is no 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. This should only be used in combination with OnceMet for a given condition, otherwise it may always succeed.

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(path string, pos fake.Pos) []protocol.Location

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

func (*Env) RegexpRange

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

RegexpRange returns the range 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) 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) 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

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(path string, pos fake.Pos, 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 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) 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) 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) SetBufferContent

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

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

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

Symbol returns symbols matching query

func (*Env) WorkspaceSymbol

func (e *Env) WorkspaceSymbol(sym string) []protocol.SymbolInformation

WorkspaceSymbol calls workspace/symbol

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 interface {
	// Check determines whether the state of the editor satisfies the
	// expectation, returning the results that met the condition.
	Check(State) Verdict
	// 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

func EmptyDiagnostics(name string) Expectation

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

func EmptyOrNoDiagnostics

func EmptyOrNoDiagnostics(name string) Expectation

EmptyOrNoDiagnostics asserts that either no diagnostics are sent for the workspace-relative path name, or empty diagnostics are sent. TODO(rFindley): this subtlety shouldn't be necessary. Gopls should always send at least one diagnostic set for open files.

func NoDiagnostics

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.

func NoOutstandingDiagnostics

func NoOutstandingDiagnostics() Expectation

NoOutstandingDiagnostics asserts that the workspace has no outstanding diagnostic messages.

type LogExpectation

type LogExpectation struct {
	// contains filtered or unexported fields
}

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

func LogMatching

func LogMatching(typ protocol.MessageType, re string, count int, atLeast bool) LogExpectation

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 cound matching logs.

func NoErrorLogs

func NoErrorLogs() LogExpectation

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

func NoLogMatching

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

func (e LogExpectation) Check(s State) Verdict

Check implements the Expectation interface.

func (LogExpectation) Description

func (e LogExpectation) Description() string

Description implements the Expectation interface.

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 with experimental options 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 Experimental | Experimental | N | N | Y

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

	// Experimental enables all of the experimental configurations that are
	// being developed, and runs gopls in sidecar mode.
	//
	// It uses a separate cache for each test, to exercise races that may only
	// appear with cache misses.
	Experimental
)

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 regtestRunner
}

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 InGOPATH

func InGOPATH() RunOption

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

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

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
	OptionsHook              func(*source.Options) // if set, use these options when creating gopls sessions
	// 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 is a RunOption that 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 SimpleExpectation

type SimpleExpectation struct {
	// contains filtered or unexported fields
}

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

func AllOf added in v0.11.0

func AllOf(allOf ...Expectation) *SimpleExpectation

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) *SimpleExpectation

AnyOf returns an expectation that is satisfied when any of the given expectations is met.

func CompletedProgress added in v0.11.0

func CompletedProgress(token protocol.ProgressToken, into *WorkStatus) SimpleExpectation

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

func FileWatchMatching(re string) SimpleExpectation

FileWatchMatching expects that a file registration matches re.

func NoFileWatchMatching

func NoFileWatchMatching(re string) SimpleExpectation

NoFileWatchMatching expects that no file registration matches re.

func NoOutstandingWork

func NoOutstandingWork() SimpleExpectation

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

func NoShownMessage

func NoShownMessage(subString string) SimpleExpectation

NoShownMessage asserts that the editor has not received a ShowMessage.

func OnceMet

func OnceMet(precondition Expectation, mustMeets ...Expectation) *SimpleExpectation

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

func OutstandingWork

func OutstandingWork(title, msg string) SimpleExpectation

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 ReadDiagnostics

func ReadDiagnostics(fileName string, into *protocol.PublishDiagnosticsParams) *SimpleExpectation

ReadDiagnostics is an 'expectation' that is used to read diagnostics atomically. It is intended to be used with 'OnceMet'.

func RegistrationMatching deprecated

func RegistrationMatching(re string) SimpleExpectation

RegistrationMatching asserts that the client has received a capability registration matching the given regexp.

TODO(rfindley): remove this once TestWatchReplaceTargets has been revisited.

Deprecated: use (No)FileWatchMatching

func ShowMessageRequest

func ShowMessageRequest(title string) SimpleExpectation

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

func ShownMessage

func ShownMessage(containing string) SimpleExpectation

ShownMessage asserts that the editor has received a ShowMessageRequest containing the given substring.

func StartedWork

func StartedWork(title string, atLeast uint64) SimpleExpectation

StartedWork expect a work item to have been started >= atLeast times.

See CompletedWork.

func UnregistrationMatching

func UnregistrationMatching(re string) SimpleExpectation

UnregistrationMatching asserts that the client has received an unregistration whose ID matches the given regexp.

func (SimpleExpectation) Check

func (e SimpleExpectation) Check(s State) Verdict

Check invokes e.check.

func (SimpleExpectation) Description

func (e SimpleExpectation) Description() string

Description returns e.description.

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 added in v0.11.0

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

Jump to

Keyboard shortcuts

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