gopls/internal/regtest: move expectations to their own file
We've accumulated a lot of expectations, that could benefit from some
cleanup. Move them to a new file, and reorganize a bit as some of the
logical sections had gotten mixed up.
This change is purely moving code, no other changes.
Change-Id: Ib0a86a5cd686b167e555d5fae77d20d02cfd67a1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/255122
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Rebecca Stambler <rstambler@golang.org>
Trust: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/regtest/env.go b/gopls/internal/regtest/env.go
index 356fbb5..069a95d 100644
--- a/gopls/internal/regtest/env.go
+++ b/gopls/internal/regtest/env.go
@@ -7,7 +7,6 @@
import (
"context"
"fmt"
- "regexp"
"strings"
"sync"
"testing"
@@ -281,462 +280,6 @@
return finalVerdict, summary.String(), metBy
}
-// 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.
-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
-}
-
-// A Verdict is the result of checking an expectation against the current
-// editor state.
-type Verdict int
-
-// Order matters for the following constants: verdicts are sorted in order of
-// decisiveness.
-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
-)
-
-// OnceMet returns an Expectation that, once the precondition is met, asserts
-// that mustMeet is met.
-func OnceMet(precondition Expectation, mustMeet Expectation) *SimpleExpectation {
- check := func(s State) (Verdict, interface{}) {
- switch pre, _ := precondition.Check(s); pre {
- case Unmeetable:
- return Unmeetable, nil
- case Met:
- verdict, metBy := mustMeet.Check(s)
- if verdict != Met {
- return Unmeetable, metBy
- }
- return Met, metBy
- default:
- return Unmet, nil
- }
- }
- return &SimpleExpectation{
- check: check,
- description: fmt.Sprintf("once %q is met, must have %q", precondition.Description(), mustMeet.Description()),
- }
-}
-
-func (v Verdict) String() string {
- switch v {
- case Met:
- return "Met"
- case Unmet:
- return "Unmet"
- case Unmeetable:
- return "Unmeetable"
- }
- return fmt.Sprintf("unrecognized verdict %d", v)
-}
-
-// SimpleExpectation holds an arbitrary check func, and implements the Expectation interface.
-type SimpleExpectation struct {
- check func(State) (Verdict, interface{})
- description string
-}
-
-// Check invokes e.check.
-func (e SimpleExpectation) Check(s State) (Verdict, interface{}) {
- return e.check(s)
-}
-
-// Description returns e.descriptin.
-func (e SimpleExpectation) Description() string {
- return e.description
-}
-
-// NoOutstandingWork asserts that there is no work initiated using the LSP
-// $/progress API that has not completed.
-func NoOutstandingWork() SimpleExpectation {
- check := func(s State) (Verdict, interface{}) {
- if len(s.outstandingWork) == 0 {
- return Met, nil
- }
- return Unmet, nil
- }
- return SimpleExpectation{
- check: check,
- description: "no outstanding work",
- }
-}
-
-// NoShowMessage asserts that the editor has not received a ShowMessage.
-func NoShowMessage() SimpleExpectation {
- check := func(s State) (Verdict, interface{}) {
- if len(s.showMessage) == 0 {
- return Met, "no ShowMessage"
- }
- return Unmeetable, nil
- }
- return SimpleExpectation{
- check: check,
- description: "no ShowMessage received",
- }
-}
-
-// ShownMessage asserts that the editor has received a ShownMessage with the
-// given title.
-func ShownMessage(title string) SimpleExpectation {
- check := func(s State) (Verdict, interface{}) {
- for _, m := range s.showMessage {
- if strings.Contains(m.Message, title) {
- return Met, m
- }
- }
- return Unmet, nil
- }
- return SimpleExpectation{
- check: check,
- description: "received ShowMessage",
- }
-}
-
-// ShowMessageRequest asserts that the editor has received a ShowMessageRequest
-// with an action item that has the given title.
-func ShowMessageRequest(title string) SimpleExpectation {
- check := func(s State) (Verdict, interface{}) {
- if len(s.showMessageRequest) == 0 {
- return Unmet, nil
- }
- // Only check the most recent one.
- m := s.showMessageRequest[len(s.showMessageRequest)-1]
- if len(m.Actions) == 0 || len(m.Actions) > 1 {
- return Unmet, nil
- }
- if m.Actions[0].Title == title {
- return Met, m.Actions[0]
- }
- return Unmet, nil
- }
- return SimpleExpectation{
- check: check,
- description: "received ShowMessageRequest",
- }
-}
-
-// 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 CompletedWork(title string, atLeast int) SimpleExpectation {
- check := func(s State) (Verdict, interface{}) {
- if s.completedWork[title] >= atLeast {
- return Met, title
- }
- return Unmet, nil
- }
- return SimpleExpectation{
- check: check,
- description: fmt.Sprintf("completed work %q at least %d time(s)", title, atLeast),
- }
-}
-
-// LogExpectation is an expectation on the log messages received by the editor
-// from gopls.
-type LogExpectation struct {
- check func([]*protocol.LogMessageParams) (Verdict, interface{})
- description string
-}
-
-// Check implements the Expectation interface.
-func (e LogExpectation) Check(s State) (Verdict, interface{}) {
- return e.check(s.logs)
-}
-
-// Description implements the Expectation interface.
-func (e LogExpectation) Description() string {
- return e.description
-}
-
-// NoErrorLogs asserts that the client has not received any log messages of
-// error severity.
-func NoErrorLogs() LogExpectation {
- return NoLogMatching(protocol.Error, "")
-}
-
-// LogMatching asserts that the client has received a log message
-// of type typ matching the regexp re.
-func LogMatching(typ protocol.MessageType, re string, count int) LogExpectation {
- rec, err := regexp.Compile(re)
- if err != nil {
- panic(err)
- }
- check := func(msgs []*protocol.LogMessageParams) (Verdict, interface{}) {
- var found int
- for _, msg := range msgs {
- if msg.Type == typ && rec.Match([]byte(msg.Message)) {
- found++
- }
- }
- if found == count {
- return Met, nil
- }
- return Unmet, nil
- }
- return LogExpectation{
- check: check,
- description: fmt.Sprintf("log message matching %q", re),
- }
-}
-
-// 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 NoLogMatching(typ protocol.MessageType, re string) LogExpectation {
- var r *regexp.Regexp
- if re != "" {
- var err error
- r, err = regexp.Compile(re)
- if err != nil {
- panic(err)
- }
- }
- check := func(msgs []*protocol.LogMessageParams) (Verdict, interface{}) {
- for _, msg := range msgs {
- if msg.Type != typ {
- continue
- }
- if r == nil || r.Match([]byte(msg.Message)) {
- return Unmeetable, nil
- }
- }
- return Met, nil
- }
- return LogExpectation{
- check: check,
- description: fmt.Sprintf("no log message matching %q", re),
- }
-}
-
-// RegistrationExpectation is an expectation on the capability registrations
-// received by the editor from gopls.
-type RegistrationExpectation struct {
- check func([]*protocol.RegistrationParams) (Verdict, interface{})
- description string
-}
-
-// Check implements the Expectation interface.
-func (e RegistrationExpectation) Check(s State) (Verdict, interface{}) {
- return e.check(s.registrations)
-}
-
-// Description implements the Expectation interface.
-func (e RegistrationExpectation) Description() string {
- return e.description
-}
-
-// RegistrationMatching asserts that the client has received a capability
-// registration matching the given regexp.
-func RegistrationMatching(re string) RegistrationExpectation {
- rec, err := regexp.Compile(re)
- if err != nil {
- panic(err)
- }
- check := func(params []*protocol.RegistrationParams) (Verdict, interface{}) {
- for _, p := range params {
- for _, r := range p.Registrations {
- if rec.Match([]byte(r.Method)) {
- return Met, r
- }
- }
- }
- return Unmet, nil
- }
- return RegistrationExpectation{
- check: check,
- description: fmt.Sprintf("registration matching %q", re),
- }
-}
-
-// UnregistrationExpectation is an expectation on the capability
-// unregistrations received by the editor from gopls.
-type UnregistrationExpectation struct {
- check func([]*protocol.UnregistrationParams) (Verdict, interface{})
- description string
-}
-
-// Check implements the Expectation interface.
-func (e UnregistrationExpectation) Check(s State) (Verdict, interface{}) {
- return e.check(s.unregistrations)
-}
-
-// Description implements the Expectation interface.
-func (e UnregistrationExpectation) Description() string {
- return e.description
-}
-
-// UnregistrationMatching asserts that the client has received an
-// unregistration whose ID matches the given regexp.
-func UnregistrationMatching(re string) UnregistrationExpectation {
- rec, err := regexp.Compile(re)
- if err != nil {
- panic(err)
- }
- check := func(params []*protocol.UnregistrationParams) (Verdict, interface{}) {
- for _, p := range params {
- for _, r := range p.Unregisterations {
- if rec.Match([]byte(r.Method)) {
- return Met, r
- }
- }
- }
- return Unmet, nil
- }
- return UnregistrationExpectation{
- check: check,
- description: fmt.Sprintf("unregistration matching %q", re),
- }
-}
-
-// A DiagnosticExpectation is a condition that must be met by the current set
-// of diagnostics for a file.
-type DiagnosticExpectation struct {
- // IsMet determines whether the diagnostics for this file version satisfy our
- // expectation.
- isMet func(*protocol.PublishDiagnosticsParams) bool
- // Description is a human-readable description of the diagnostic expectation.
- description string
- // Path is the scratch workdir-relative path to the file being asserted on.
- path string
-}
-
-// Check implements the Expectation interface.
-func (e DiagnosticExpectation) Check(s State) (Verdict, interface{}) {
- if diags, ok := s.diagnostics[e.path]; ok && e.isMet(diags) {
- return Met, diags
- }
- return Unmet, nil
-}
-
-// Description implements the Expectation interface.
-func (e DiagnosticExpectation) Description() string {
- return fmt.Sprintf("%s: %s", e.path, e.description)
-}
-
-// EmptyDiagnostics asserts that empty diagnostics are sent for the
-// workspace-relative path name.
-func EmptyDiagnostics(name string) Expectation {
- check := func(s State) (Verdict, interface{}) {
- if diags := s.diagnostics[name]; diags != nil && len(diags.Diagnostics) == 0 {
- return Met, nil
- }
- return Unmet, nil
- }
- return SimpleExpectation{
- check: check,
- description: "empty diagnostics",
- }
-}
-
-// 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 NoDiagnostics(name string) Expectation {
- check := func(s State) (Verdict, interface{}) {
- if _, ok := s.diagnostics[name]; !ok {
- return Met, nil
- }
- return Unmet, nil
- }
- return SimpleExpectation{
- check: check,
- description: "no diagnostics",
- }
-}
-
-// AnyDiagnosticAtCurrentVersion asserts that there is a diagnostic report for
-// the current edited version of the buffer corresponding to the given
-// workdir-relative pathname.
-func (e *Env) AnyDiagnosticAtCurrentVersion(name string) DiagnosticExpectation {
- version := e.Editor.BufferVersion(name)
- isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
- return int(diags.Version) == version
- }
- return DiagnosticExpectation{
- isMet: isMet,
- description: fmt.Sprintf("any diagnostics at version %d", version),
- path: name,
- }
-}
-
-// 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 (e *Env) DiagnosticAtRegexp(name, re string) DiagnosticExpectation {
- e.T.Helper()
- pos := e.RegexpSearch(name, re)
- expectation := DiagnosticAt(name, pos.Line, pos.Column)
- expectation.description += fmt.Sprintf(" (location of %q)", re)
- return expectation
-}
-
-// DiagnosticAt asserts that there is a diagnostic entry at the position
-// specified by line and col, for the workdir-relative path name.
-func DiagnosticAt(name string, line, col int) DiagnosticExpectation {
- isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
- for _, d := range diags.Diagnostics {
- if d.Range.Start.Line == float64(line) && d.Range.Start.Character == float64(col) {
- return true
- }
- }
- return false
- }
- return DiagnosticExpectation{
- isMet: isMet,
- description: fmt.Sprintf("diagnostic at {line:%d, column:%d}", line, col),
- path: name,
- }
-}
-
-// 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 (e *Env) NoDiagnosticAtRegexp(name, re string) DiagnosticExpectation {
- e.T.Helper()
- pos := e.RegexpSearch(name, re)
- expectation := NoDiagnosticAt(name, pos.Line, pos.Column)
- expectation.description += fmt.Sprintf(" (location of %q)", re)
- return expectation
-}
-
-// 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 NoDiagnosticAt(name string, line, col int) DiagnosticExpectation {
- isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
- for _, d := range diags.Diagnostics {
- if d.Range.Start.Line == float64(line) && d.Range.Start.Character == float64(col) {
- return false
- }
- }
- return true
- }
- return DiagnosticExpectation{
- isMet: isMet,
- description: fmt.Sprintf("no diagnostic at {line:%d, column:%d}", line, col),
- path: name,
- }
-}
-
// 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.
diff --git a/gopls/internal/regtest/expectation.go b/gopls/internal/regtest/expectation.go
new file mode 100644
index 0000000..19f5ff2
--- /dev/null
+++ b/gopls/internal/regtest/expectation.go
@@ -0,0 +1,469 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package regtest
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+
+ "golang.org/x/tools/internal/lsp/protocol"
+)
+
+// 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.
+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
+}
+
+// A Verdict is the result of checking an expectation against the current
+// editor state.
+type Verdict int
+
+// Order matters for the following constants: verdicts are sorted in order of
+// decisiveness.
+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
+)
+
+func (v Verdict) String() string {
+ switch v {
+ case Met:
+ return "Met"
+ case Unmet:
+ return "Unmet"
+ case Unmeetable:
+ return "Unmeetable"
+ }
+ return fmt.Sprintf("unrecognized verdict %d", v)
+}
+
+// SimpleExpectation holds an arbitrary check func, and implements the Expectation interface.
+type SimpleExpectation struct {
+ check func(State) (Verdict, interface{})
+ description string
+}
+
+// Check invokes e.check.
+func (e SimpleExpectation) Check(s State) (Verdict, interface{}) {
+ return e.check(s)
+}
+
+// Description returns e.descriptin.
+func (e SimpleExpectation) Description() string {
+ return e.description
+}
+
+// OnceMet returns an Expectation that, once the precondition is met, asserts
+// that mustMeet is met.
+func OnceMet(precondition Expectation, mustMeet Expectation) *SimpleExpectation {
+ check := func(s State) (Verdict, interface{}) {
+ switch pre, _ := precondition.Check(s); pre {
+ case Unmeetable:
+ return Unmeetable, nil
+ case Met:
+ verdict, metBy := mustMeet.Check(s)
+ if verdict != Met {
+ return Unmeetable, metBy
+ }
+ return Met, metBy
+ default:
+ return Unmet, nil
+ }
+ }
+ return &SimpleExpectation{
+ check: check,
+ description: fmt.Sprintf("once %q is met, must have %q", precondition.Description(), mustMeet.Description()),
+ }
+}
+
+// NoOutstandingWork asserts that there is no work initiated using the LSP
+// $/progress API that has not completed.
+func NoOutstandingWork() SimpleExpectation {
+ check := func(s State) (Verdict, interface{}) {
+ if len(s.outstandingWork) == 0 {
+ return Met, nil
+ }
+ return Unmet, nil
+ }
+ return SimpleExpectation{
+ check: check,
+ description: "no outstanding work",
+ }
+}
+
+// NoShowMessage asserts that the editor has not received a ShowMessage.
+func NoShowMessage() SimpleExpectation {
+ check := func(s State) (Verdict, interface{}) {
+ if len(s.showMessage) == 0 {
+ return Met, "no ShowMessage"
+ }
+ return Unmeetable, nil
+ }
+ return SimpleExpectation{
+ check: check,
+ description: "no ShowMessage received",
+ }
+}
+
+// ShownMessage asserts that the editor has received a ShownMessage with the
+// given title.
+func ShownMessage(title string) SimpleExpectation {
+ check := func(s State) (Verdict, interface{}) {
+ for _, m := range s.showMessage {
+ if strings.Contains(m.Message, title) {
+ return Met, m
+ }
+ }
+ return Unmet, nil
+ }
+ return SimpleExpectation{
+ check: check,
+ description: "received ShowMessage",
+ }
+}
+
+// ShowMessageRequest asserts that the editor has received a ShowMessageRequest
+// with an action item that has the given title.
+func ShowMessageRequest(title string) SimpleExpectation {
+ check := func(s State) (Verdict, interface{}) {
+ if len(s.showMessageRequest) == 0 {
+ return Unmet, nil
+ }
+ // Only check the most recent one.
+ m := s.showMessageRequest[len(s.showMessageRequest)-1]
+ if len(m.Actions) == 0 || len(m.Actions) > 1 {
+ return Unmet, nil
+ }
+ if m.Actions[0].Title == title {
+ return Met, m.Actions[0]
+ }
+ return Unmet, nil
+ }
+ return SimpleExpectation{
+ check: check,
+ description: "received ShowMessageRequest",
+ }
+}
+
+// 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 CompletedWork(title string, atLeast int) SimpleExpectation {
+ check := func(s State) (Verdict, interface{}) {
+ if s.completedWork[title] >= atLeast {
+ return Met, title
+ }
+ return Unmet, nil
+ }
+ return SimpleExpectation{
+ check: check,
+ description: fmt.Sprintf("completed work %q at least %d time(s)", title, atLeast),
+ }
+}
+
+// LogExpectation is an expectation on the log messages received by the editor
+// from gopls.
+type LogExpectation struct {
+ check func([]*protocol.LogMessageParams) (Verdict, interface{})
+ description string
+}
+
+// Check implements the Expectation interface.
+func (e LogExpectation) Check(s State) (Verdict, interface{}) {
+ return e.check(s.logs)
+}
+
+// Description implements the Expectation interface.
+func (e LogExpectation) Description() string {
+ return e.description
+}
+
+// NoErrorLogs asserts that the client has not received any log messages of
+// error severity.
+func NoErrorLogs() LogExpectation {
+ return NoLogMatching(protocol.Error, "")
+}
+
+// LogMatching asserts that the client has received a log message
+// of type typ matching the regexp re.
+func LogMatching(typ protocol.MessageType, re string, count int) LogExpectation {
+ rec, err := regexp.Compile(re)
+ if err != nil {
+ panic(err)
+ }
+ check := func(msgs []*protocol.LogMessageParams) (Verdict, interface{}) {
+ var found int
+ for _, msg := range msgs {
+ if msg.Type == typ && rec.Match([]byte(msg.Message)) {
+ found++
+ }
+ }
+ if found == count {
+ return Met, nil
+ }
+ return Unmet, nil
+ }
+ return LogExpectation{
+ check: check,
+ description: fmt.Sprintf("log message matching %q", re),
+ }
+}
+
+// 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 NoLogMatching(typ protocol.MessageType, re string) LogExpectation {
+ var r *regexp.Regexp
+ if re != "" {
+ var err error
+ r, err = regexp.Compile(re)
+ if err != nil {
+ panic(err)
+ }
+ }
+ check := func(msgs []*protocol.LogMessageParams) (Verdict, interface{}) {
+ for _, msg := range msgs {
+ if msg.Type != typ {
+ continue
+ }
+ if r == nil || r.Match([]byte(msg.Message)) {
+ return Unmeetable, nil
+ }
+ }
+ return Met, nil
+ }
+ return LogExpectation{
+ check: check,
+ description: fmt.Sprintf("no log message matching %q", re),
+ }
+}
+
+// RegistrationExpectation is an expectation on the capability registrations
+// received by the editor from gopls.
+type RegistrationExpectation struct {
+ check func([]*protocol.RegistrationParams) (Verdict, interface{})
+ description string
+}
+
+// Check implements the Expectation interface.
+func (e RegistrationExpectation) Check(s State) (Verdict, interface{}) {
+ return e.check(s.registrations)
+}
+
+// Description implements the Expectation interface.
+func (e RegistrationExpectation) Description() string {
+ return e.description
+}
+
+// RegistrationMatching asserts that the client has received a capability
+// registration matching the given regexp.
+func RegistrationMatching(re string) RegistrationExpectation {
+ rec, err := regexp.Compile(re)
+ if err != nil {
+ panic(err)
+ }
+ check := func(params []*protocol.RegistrationParams) (Verdict, interface{}) {
+ for _, p := range params {
+ for _, r := range p.Registrations {
+ if rec.Match([]byte(r.Method)) {
+ return Met, r
+ }
+ }
+ }
+ return Unmet, nil
+ }
+ return RegistrationExpectation{
+ check: check,
+ description: fmt.Sprintf("registration matching %q", re),
+ }
+}
+
+// UnregistrationExpectation is an expectation on the capability
+// unregistrations received by the editor from gopls.
+type UnregistrationExpectation struct {
+ check func([]*protocol.UnregistrationParams) (Verdict, interface{})
+ description string
+}
+
+// Check implements the Expectation interface.
+func (e UnregistrationExpectation) Check(s State) (Verdict, interface{}) {
+ return e.check(s.unregistrations)
+}
+
+// Description implements the Expectation interface.
+func (e UnregistrationExpectation) Description() string {
+ return e.description
+}
+
+// UnregistrationMatching asserts that the client has received an
+// unregistration whose ID matches the given regexp.
+func UnregistrationMatching(re string) UnregistrationExpectation {
+ rec, err := regexp.Compile(re)
+ if err != nil {
+ panic(err)
+ }
+ check := func(params []*protocol.UnregistrationParams) (Verdict, interface{}) {
+ for _, p := range params {
+ for _, r := range p.Unregisterations {
+ if rec.Match([]byte(r.Method)) {
+ return Met, r
+ }
+ }
+ }
+ return Unmet, nil
+ }
+ return UnregistrationExpectation{
+ check: check,
+ description: fmt.Sprintf("unregistration matching %q", re),
+ }
+}
+
+// A DiagnosticExpectation is a condition that must be met by the current set
+// of diagnostics for a file.
+type DiagnosticExpectation struct {
+ // IsMet determines whether the diagnostics for this file version satisfy our
+ // expectation.
+ isMet func(*protocol.PublishDiagnosticsParams) bool
+ // Description is a human-readable description of the diagnostic expectation.
+ description string
+ // Path is the scratch workdir-relative path to the file being asserted on.
+ path string
+}
+
+// Check implements the Expectation interface.
+func (e DiagnosticExpectation) Check(s State) (Verdict, interface{}) {
+ if diags, ok := s.diagnostics[e.path]; ok && e.isMet(diags) {
+ return Met, diags
+ }
+ return Unmet, nil
+}
+
+// Description implements the Expectation interface.
+func (e DiagnosticExpectation) Description() string {
+ return fmt.Sprintf("%s: %s", e.path, e.description)
+}
+
+// EmptyDiagnostics asserts that empty diagnostics are sent for the
+// workspace-relative path name.
+func EmptyDiagnostics(name string) Expectation {
+ check := func(s State) (Verdict, interface{}) {
+ if diags := s.diagnostics[name]; diags != nil && len(diags.Diagnostics) == 0 {
+ return Met, nil
+ }
+ return Unmet, nil
+ }
+ return SimpleExpectation{
+ check: check,
+ description: "empty diagnostics",
+ }
+}
+
+// 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 NoDiagnostics(name string) Expectation {
+ check := func(s State) (Verdict, interface{}) {
+ if _, ok := s.diagnostics[name]; !ok {
+ return Met, nil
+ }
+ return Unmet, nil
+ }
+ return SimpleExpectation{
+ check: check,
+ description: "no diagnostics",
+ }
+}
+
+// AnyDiagnosticAtCurrentVersion asserts that there is a diagnostic report for
+// the current edited version of the buffer corresponding to the given
+// workdir-relative pathname.
+func (e *Env) AnyDiagnosticAtCurrentVersion(name string) DiagnosticExpectation {
+ version := e.Editor.BufferVersion(name)
+ isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
+ return int(diags.Version) == version
+ }
+ return DiagnosticExpectation{
+ isMet: isMet,
+ description: fmt.Sprintf("any diagnostics at version %d", version),
+ path: name,
+ }
+}
+
+// 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 (e *Env) DiagnosticAtRegexp(name, re string) DiagnosticExpectation {
+ e.T.Helper()
+ pos := e.RegexpSearch(name, re)
+ expectation := DiagnosticAt(name, pos.Line, pos.Column)
+ expectation.description += fmt.Sprintf(" (location of %q)", re)
+ return expectation
+}
+
+// DiagnosticAt asserts that there is a diagnostic entry at the position
+// specified by line and col, for the workdir-relative path name.
+func DiagnosticAt(name string, line, col int) DiagnosticExpectation {
+ isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
+ for _, d := range diags.Diagnostics {
+ if d.Range.Start.Line == float64(line) && d.Range.Start.Character == float64(col) {
+ return true
+ }
+ }
+ return false
+ }
+ return DiagnosticExpectation{
+ isMet: isMet,
+ description: fmt.Sprintf("diagnostic at {line:%d, column:%d}", line, col),
+ path: name,
+ }
+}
+
+// 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 (e *Env) NoDiagnosticAtRegexp(name, re string) DiagnosticExpectation {
+ e.T.Helper()
+ pos := e.RegexpSearch(name, re)
+ expectation := NoDiagnosticAt(name, pos.Line, pos.Column)
+ expectation.description += fmt.Sprintf(" (location of %q)", re)
+ return expectation
+}
+
+// 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 NoDiagnosticAt(name string, line, col int) DiagnosticExpectation {
+ isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
+ for _, d := range diags.Diagnostics {
+ if d.Range.Start.Line == float64(line) && d.Range.Start.Character == float64(col) {
+ return false
+ }
+ }
+ return true
+ }
+ return DiagnosticExpectation{
+ isMet: isMet,
+ description: fmt.Sprintf("no diagnostic at {line:%d, column:%d}", line, col),
+ path: name,
+ }
+}