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 (see gopls/doc/daemon.md 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 golang.org/x/tools/internal/lsp/fake package.
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.
Index ¶
- Variables
- type DiagnosticExpectation
- type EditorConfig
- type Env
- func (e *Env) AnyDiagnosticAtCurrentVersion(name string) DiagnosticExpectation
- func (e *Env) ApplyQuickFixes(path string, diagnostics []protocol.Diagnostic)
- func (e *Env) Await(expectations ...Expectation)
- func (e *Env) ChangeEnv(overlay map[string]string)
- func (e *Env) ChangeFilesOnDisk(events []fake.FileEvent)
- func (e *Env) CheckForFileChanges()
- func (e *Env) CloseBuffer(name string)
- func (e *Env) CloseEditor()
- func (e *Env) CodeAction(path string) []protocol.CodeAction
- func (e *Env) CodeLens(path string) []protocol.CodeLens
- func (e *Env) Completion(path string, pos fake.Pos) *protocol.CompletionList
- func (e *Env) CreateBuffer(name string, content string)
- func (e *Env) DiagnosticAtRegexp(name, re string) DiagnosticExpectation
- func (e *Env) DiagnosticsFor(name string) *protocol.PublishDiagnosticsParams
- func (e *Env) DocumentLink(name string) []protocol.DocumentLink
- func (e *Env) EditBuffer(name string, edits ...fake.Edit)
- func (e *Env) ExecuteCodeLensCommand(path string, cmd *source.Command)
- func (e *Env) FormatBuffer(name string)
- func (e *Env) GoToDefinition(name string, pos fake.Pos) (string, fake.Pos)
- func (e *Env) Hover(name string, pos fake.Pos) (*protocol.MarkupContent, fake.Pos)
- func (e *Env) NoDiagnosticAtRegexp(name, re string) DiagnosticExpectation
- func (e *Env) OpenFile(name string)
- func (e *Env) OpenFileWithContent(name, content string)
- func (e *Env) OrganizeImports(name string)
- func (e *Env) ReadWorkspaceFile(name string) string
- func (e *Env) References(path string, pos fake.Pos) []protocol.Location
- func (e *Env) RegexpRange(name, re string) (fake.Pos, fake.Pos)
- func (e *Env) RegexpReplace(name, regexpStr, replace string)
- func (e *Env) RegexpSearch(name, re string) fake.Pos
- func (e *Env) RemoveWorkspaceFile(name string)
- func (e *Env) RunGenerate(dir string)
- func (e *Env) SaveBuffer(name string)
- func (e *Env) Symbol(query string) []fake.SymbolInformation
- func (e *Env) WriteWorkspaceFile(name, content string)
- func (e *Env) WriteWorkspaceFiles(files map[string]string)
- type Expectation
- type LogExpectation
- type Mode
- type RegistrationExpectation
- type RunOption
- func InExistingDir(dir string) RunOption
- func InGOPATH() RunOption
- func SkipHooks(skip bool) RunOption
- func SkipLogs() RunOption
- func WithDebugAddress(addr string) RunOption
- func WithGOPROXY(goproxy string) RunOption
- func WithLimitWorkspaceScope() RunOption
- func WithModes(modes Mode) RunOption
- func WithProxyFiles(txt string) RunOption
- func WithRootPath(path string) RunOption
- func WithTimeout(d time.Duration) RunOption
- func WithoutWorkspaceFolders() RunOption
- type Runner
- type SimpleExpectation
- func CompletedWork(title string, atLeast int) SimpleExpectation
- func NoOutstandingWork() SimpleExpectation
- func NoShowMessage() SimpleExpectation
- func OnceMet(precondition Expectation, mustMeet Expectation) *SimpleExpectation
- func ReadDiagnostics(fileName string, into *protocol.PublishDiagnosticsParams) *SimpleExpectation
- func ShowMessageRequest(title string) SimpleExpectation
- func ShownMessage(title string) SimpleExpectation
- type State
- type TestFunc
- type UnregistrationExpectation
- type Verdict
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(lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad), 1) )
Functions ¶
This section is empty.
Types ¶
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 NoDiagnosticAt ¶ added in v0.5.1
func NoDiagnosticAt(name string, line, col int) DiagnosticExpectation
NoDiagnosticAt asserts that there is no diagnostic entry at the position specified by line and col, for the workdir-relative path name. 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 EditorConfig ¶ added in v0.5.1
type EditorConfig fake.EditorConfig
EditorConfig is a RunOption option that configured the regtest editor.
type Env ¶
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 ¶
func NewEnv(ctx context.Context, t *testing.T, sandbox *fake.Sandbox, ts servertest.Connector, editorConfig fake.EditorConfig, withHooks bool) *Env
NewEnv creates a new test environment using the given scratch environment and gopls server.
func (*Env) AnyDiagnosticAtCurrentVersion ¶
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 ¶
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)
Await waits for all expectations to simultaneously be met. It should only be called from the main test goroutine.
func (*Env) ChangeEnv ¶
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) ChangeFilesOnDisk ¶
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 ¶
CloseBuffer closes an editor buffer without saving, calling t.Fatal on any error.
func (*Env) CloseEditor ¶
func (e *Env) CloseEditor()
CloseEditor shuts down the editor, calling t.Fatal on any error.
func (*Env) CodeAction ¶
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 ¶
CodeLens calls textDocument/codeLens for the given path, calling t.Fatal on any error.
func (*Env) Completion ¶ added in v0.5.1
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) 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) DiagnosticsFor ¶
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 (*Env) DocumentLink ¶
func (e *Env) DocumentLink(name string) []protocol.DocumentLink
func (*Env) EditBuffer ¶
EditBuffer applies edits to an editor buffer, calling t.Fatal on any error.
func (*Env) ExecuteCodeLensCommand ¶ added in v0.5.1
ExecuteCodeLensCommand executes the command for the code lens matching the given command name.
func (*Env) FormatBuffer ¶
FormatBuffer formats the editor buffer, calling t.Fatal on any error.
func (*Env) GoToDefinition ¶
GoToDefinition goes to definition in the editor, calling t.Fatal on any error.
func (*Env) NoDiagnosticAtRegexp ¶ added in v0.5.1
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) OpenFileWithContent ¶
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 calls textDocument/references for the given path at the given position.
func (*Env) RegexpRange ¶ added in v0.5.1
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 ¶
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) RunGenerate ¶
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 ¶
SaveBuffer saves an editor buffer, calling t.Fatal on any error.
func (*Env) Symbol ¶
func (e *Env) Symbol(query string) []fake.SymbolInformation
Symbol returns symbols matching query
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 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 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.
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) LogExpectation
LogMatching asserts that the client has received a log message of type typ matching the regexp re.
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.
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. Forwarded // SeparateProcess forwards connection to a shared separate gopls process. SeparateProcess // NormalModes are the global default execution modes, when unmodified by // test flags or by individual test options. NormalModes = Singleton | Forwarded )
type RegistrationExpectation ¶
type RegistrationExpectation struct {
// contains filtered or unexported fields
}
RegistrationExpectation is an expectation on the capability registrations received by the editor from gopls.
func RegistrationMatching ¶
func RegistrationMatching(re string) RegistrationExpectation
RegistrationMatching asserts that the client has received a capability registration matching the given regexp.
func (RegistrationExpectation) Check ¶
func (e RegistrationExpectation) Check(s State) Verdict
Check implements the Expectation interface.
func (RegistrationExpectation) Description ¶
func (e RegistrationExpectation) Description() string
Description implements the Expectation interface.
type RunOption ¶
type RunOption interface {
// contains filtered or unexported methods
}
A RunOption augments the behavior of the test runner.
func InExistingDir ¶
InExistingDir runs the test in a pre-existing directory. If set, no initial files may be passed to the runner. It is intended for long-running stress tests.
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 SkipHooks ¶
SkipHooks allows for disabling the test runner's client hooks that are used for instrumenting expectations (tracking diagnostics, logs, work done, etc.). It is intended for performance-sensitive stress tests or benchmarks.
func SkipLogs ¶
func SkipLogs() RunOption
SkipLogs skips the buffering of logs during test execution. It is intended for long-running stress tests.
func WithDebugAddress ¶
WithDebugAddress configures a debug server bound to addr. This option is currently only supported when executing in Singleton mode. It is intended to be used for long-running stress tests.
func WithGOPROXY ¶
WithGOPROXY configures the test environment to have an explicit proxy value. This is intended for stress tests -- to ensure their isolation, regtests should instead use WithProxyFiles.
func WithLimitWorkspaceScope ¶
func WithLimitWorkspaceScope() RunOption
WithLimitWorkspaceScope sets the LimitWorkspaceScope configuration.
func WithProxyFiles ¶
WithProxyFiles configures a file proxy using the given txtar-encoded string.
func WithRootPath ¶
WithRootPath specifies the rootURI of the workspace folder opened in the editor. By default, the sandbox opens the top-level directory, but some tests need to check other cases.
func WithTimeout ¶
WithTimeout configures a custom timeout for this test run.
func WithoutWorkspaceFolders ¶
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 ¶
type Runner struct { DefaultModes Mode Timeout time.Duration GoplsPath string PrintGoroutinesOnFailure bool TempDir string SkipCleanup 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 ¶
AddCloser schedules a closer to be closed at the end of the test run. This is useful for Windows in particular, as
type SimpleExpectation ¶
type SimpleExpectation struct {
// contains filtered or unexported fields
}
SimpleExpectation holds an arbitrary check func, and implements the Expectation interface.
func CompletedWork ¶
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 NoOutstandingWork ¶
func NoOutstandingWork() SimpleExpectation
NoOutstandingWork asserts that there is no work initiated using the LSP $/progress API that has not completed.
func NoShowMessage ¶
func NoShowMessage() SimpleExpectation
NoShowMessage asserts that the editor has not received a ShowMessage.
func OnceMet ¶
func OnceMet(precondition Expectation, mustMeet Expectation) *SimpleExpectation
OnceMet returns an Expectation that, once the precondition is met, asserts that mustMeet is met.
func ReadDiagnostics ¶ added in v0.5.1
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 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 ¶ added in v0.5.1
func ShownMessage(title string) SimpleExpectation
ShownMessage asserts that the editor has received a ShownMessage with the given title.
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.descriptin.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State encapsulates the server state TODO: explain more
type UnregistrationExpectation ¶
type UnregistrationExpectation struct {
// contains filtered or unexported fields
}
UnregistrationExpectation is an expectation on the capability unregistrations received by the editor from gopls.
func UnregistrationMatching ¶
func UnregistrationMatching(re string) UnregistrationExpectation
UnregistrationMatching asserts that the client has received an unregistration whose ID matches the given regexp.
func (UnregistrationExpectation) Check ¶
func (e UnregistrationExpectation) Check(s State) Verdict
Check implements the Expectation interface.
func (UnregistrationExpectation) Description ¶
func (e UnregistrationExpectation) Description() string
Description implements the Expectation interface.
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.