mirror of https://github.com/golang/go.git
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>
This commit is contained in:
parent
f4cefd1cb5
commit
bf5c620a10
|
|
@ -7,7 +7,6 @@ package regtest
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
|
@ -281,462 +280,6 @@ func checkExpectations(s State, expectations []Expectation) (Verdict, string, []
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue