2020-02-06 17:50:37 -07:00
|
|
|
// 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 provides an environment for writing regression tests.
|
|
|
|
package regtest
|
|
|
|
|
|
|
|
import (
|
2020-02-10 09:34:13 -07:00
|
|
|
"bytes"
|
2020-02-06 17:50:37 -07:00
|
|
|
"context"
|
|
|
|
"fmt"
|
2020-04-01 19:08:59 -06:00
|
|
|
"io"
|
2020-02-10 09:34:13 -07:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2020-02-06 17:50:37 -07:00
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2020-03-04 11:29:23 -07:00
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
2020-02-06 17:50:37 -07:00
|
|
|
"golang.org/x/tools/internal/jsonrpc2/servertest"
|
|
|
|
"golang.org/x/tools/internal/lsp/cache"
|
2020-02-19 08:18:21 -07:00
|
|
|
"golang.org/x/tools/internal/lsp/debug"
|
2020-02-06 17:50:37 -07:00
|
|
|
"golang.org/x/tools/internal/lsp/fake"
|
|
|
|
"golang.org/x/tools/internal/lsp/lsprpc"
|
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
|
|
)
|
|
|
|
|
|
|
|
// EnvMode is a bitmask that defines in which execution environments a test
|
|
|
|
// should run.
|
|
|
|
type EnvMode int
|
|
|
|
|
|
|
|
const (
|
2020-02-10 09:34:13 -07:00
|
|
|
// Singleton mode uses a separate cache for each test.
|
2020-02-06 17:50:37 -07:00
|
|
|
Singleton EnvMode = 1 << iota
|
2020-04-01 19:08:59 -06:00
|
|
|
|
2020-02-10 09:34:13 -07:00
|
|
|
// Forwarded forwards connections to an in-process gopls instance.
|
2020-02-06 17:50:37 -07:00
|
|
|
Forwarded
|
2020-02-10 09:34:13 -07:00
|
|
|
// SeparateProcess runs a separate gopls process, and forwards connections to
|
|
|
|
// it.
|
|
|
|
SeparateProcess
|
|
|
|
// NormalModes runs tests in all modes.
|
2020-02-27 10:31:02 -07:00
|
|
|
NormalModes = Singleton | Forwarded
|
2020-02-06 17:50:37 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
type Runner struct {
|
2020-02-09 12:44:03 -07:00
|
|
|
defaultModes EnvMode
|
|
|
|
timeout time.Duration
|
2020-02-10 09:34:13 -07:00
|
|
|
goplsPath string
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
ts *servertest.TCPServer
|
|
|
|
socketDir string
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewTestRunner creates a Runner with its shared state initialized, ready to
|
|
|
|
// run tests.
|
2020-02-10 09:34:13 -07:00
|
|
|
func NewTestRunner(modes EnvMode, testTimeout time.Duration, goplsPath string) *Runner {
|
2020-02-06 17:50:37 -07:00
|
|
|
return &Runner{
|
2020-02-09 12:44:03 -07:00
|
|
|
defaultModes: modes,
|
|
|
|
timeout: testTimeout,
|
2020-02-10 09:34:13 -07:00
|
|
|
goplsPath: goplsPath,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modes returns the bitmask of environment modes this runner is configured to
|
|
|
|
// test.
|
|
|
|
func (r *Runner) Modes() EnvMode {
|
|
|
|
return r.defaultModes
|
|
|
|
}
|
|
|
|
|
|
|
|
// getTestServer gets the test server instance to connect to, or creates one if
|
|
|
|
// it doesn't exist.
|
|
|
|
func (r *Runner) getTestServer() *servertest.TCPServer {
|
|
|
|
r.mu.Lock()
|
|
|
|
defer r.mu.Unlock()
|
|
|
|
if r.ts == nil {
|
2020-02-28 08:30:03 -07:00
|
|
|
ctx := context.Background()
|
|
|
|
ctx = debug.WithInstance(ctx, "", "")
|
2020-03-09 11:22:56 -06:00
|
|
|
ss := lsprpc.NewStreamServer(cache.New(ctx, nil))
|
2020-02-10 09:34:13 -07:00
|
|
|
r.ts = servertest.NewTCPServer(context.Background(), ss)
|
|
|
|
}
|
|
|
|
return r.ts
|
|
|
|
}
|
|
|
|
|
|
|
|
// runTestAsGoplsEnvvar triggers TestMain to run gopls instead of running
|
|
|
|
// tests. It's a trick to allow tests to find a binary to use to start a gopls
|
|
|
|
// subprocess.
|
|
|
|
const runTestAsGoplsEnvvar = "_GOPLS_TEST_BINARY_RUN_AS_GOPLS"
|
|
|
|
|
|
|
|
func (r *Runner) getRemoteSocket(t *testing.T) string {
|
|
|
|
t.Helper()
|
|
|
|
r.mu.Lock()
|
|
|
|
defer r.mu.Unlock()
|
|
|
|
const daemonFile = "gopls-test-daemon"
|
|
|
|
if r.socketDir != "" {
|
|
|
|
return filepath.Join(r.socketDir, daemonFile)
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
2020-02-10 09:34:13 -07:00
|
|
|
|
|
|
|
if r.goplsPath == "" {
|
|
|
|
t.Fatal("cannot run tests with a separate process unless a path to a gopls binary is configured")
|
|
|
|
}
|
|
|
|
var err error
|
|
|
|
r.socketDir, err = ioutil.TempDir("", "gopls-regtests")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("creating tempdir: %v", err)
|
|
|
|
}
|
|
|
|
socket := filepath.Join(r.socketDir, daemonFile)
|
2020-02-25 09:00:37 -07:00
|
|
|
args := []string{"serve", "-listen", "unix;" + socket, "-listen.timeout", "10s"}
|
2020-02-10 09:34:13 -07:00
|
|
|
cmd := exec.Command(r.goplsPath, args...)
|
|
|
|
cmd.Env = append(os.Environ(), runTestAsGoplsEnvvar+"=true")
|
|
|
|
var stderr bytes.Buffer
|
|
|
|
cmd.Stderr = &stderr
|
|
|
|
go func() {
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
panic(fmt.Sprintf("error running external gopls: %v\nstderr:\n%s", err, stderr.String()))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return socket
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close cleans up resource that have been allocated to this workspace.
|
|
|
|
func (r *Runner) Close() error {
|
2020-02-10 09:34:13 -07:00
|
|
|
r.mu.Lock()
|
|
|
|
defer r.mu.Unlock()
|
|
|
|
if r.ts != nil {
|
|
|
|
r.ts.Close()
|
|
|
|
}
|
|
|
|
if r.socketDir != "" {
|
|
|
|
os.RemoveAll(r.socketDir)
|
|
|
|
}
|
|
|
|
return nil
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
2020-02-09 12:44:03 -07:00
|
|
|
// 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.
|
2020-03-23 15:26:05 -06:00
|
|
|
func (r *Runner) Run(t *testing.T, filedata string, test func(e *Env)) {
|
2020-02-06 17:50:37 -07:00
|
|
|
t.Helper()
|
2020-02-09 12:44:03 -07:00
|
|
|
r.RunInMode(r.defaultModes, t, filedata, test)
|
|
|
|
}
|
2020-02-06 17:50:37 -07:00
|
|
|
|
2020-02-09 12:44:03 -07:00
|
|
|
// RunInMode runs the test in the execution modes specified by the modes bitmask.
|
2020-03-23 15:26:05 -06:00
|
|
|
func (r *Runner) RunInMode(modes EnvMode, t *testing.T, filedata string, test func(e *Env)) {
|
2020-02-09 12:44:03 -07:00
|
|
|
t.Helper()
|
2020-02-06 17:50:37 -07:00
|
|
|
tests := []struct {
|
2020-04-01 19:08:59 -06:00
|
|
|
name string
|
|
|
|
mode EnvMode
|
|
|
|
getServer func(context.Context, *testing.T) jsonrpc2.StreamServer
|
2020-02-06 17:50:37 -07:00
|
|
|
}{
|
2020-04-01 19:08:59 -06:00
|
|
|
{"singleton", Singleton, singletonEnv},
|
2020-02-06 17:50:37 -07:00
|
|
|
{"forwarded", Forwarded, r.forwardedEnv},
|
2020-02-10 09:34:13 -07:00
|
|
|
{"separate_process", SeparateProcess, r.separateProcessEnv},
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
tc := tc
|
2020-02-09 12:44:03 -07:00
|
|
|
if modes&tc.mode == 0 {
|
2020-02-06 17:50:37 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
t.Helper()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
|
|
|
|
defer cancel()
|
2020-04-01 19:08:59 -06:00
|
|
|
ctx = debug.WithInstance(ctx, "", "")
|
|
|
|
|
2020-02-06 17:50:37 -07:00
|
|
|
ws, err := fake.NewWorkspace("lsprpc", []byte(filedata))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer ws.Close()
|
2020-04-01 19:08:59 -06:00
|
|
|
ss := tc.getServer(ctx, t)
|
|
|
|
ls := &loggingServer{delegate: ss}
|
|
|
|
ts := servertest.NewPipeServer(ctx, ls)
|
|
|
|
defer func() {
|
|
|
|
ts.Close()
|
|
|
|
}()
|
2020-02-06 17:50:37 -07:00
|
|
|
env := NewEnv(ctx, t, ws, ts)
|
2020-02-18 18:53:06 -07:00
|
|
|
defer func() {
|
2020-04-01 19:08:59 -06:00
|
|
|
if t.Failed() {
|
|
|
|
ls.printBuffers(t.Name(), os.Stderr)
|
|
|
|
}
|
2020-02-18 18:53:06 -07:00
|
|
|
if err := env.E.Shutdown(ctx); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}()
|
2020-03-23 15:26:05 -06:00
|
|
|
test(env)
|
2020-02-06 17:50:37 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-01 19:08:59 -06:00
|
|
|
type loggingServer struct {
|
|
|
|
delegate jsonrpc2.StreamServer
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
buffers []*bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *loggingServer) ServeStream(ctx context.Context, stream jsonrpc2.Stream) error {
|
|
|
|
s.mu.Lock()
|
|
|
|
var buf bytes.Buffer
|
|
|
|
s.buffers = append(s.buffers, &buf)
|
|
|
|
s.mu.Unlock()
|
|
|
|
logStream := protocol.LoggingStream(stream, &buf)
|
|
|
|
return s.delegate.ServeStream(ctx, logStream)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *loggingServer) printBuffers(testname string, w io.Writer) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
for i, buf := range s.buffers {
|
|
|
|
fmt.Fprintf(os.Stderr, "#### Start Gopls Test Logs %d of %d for %q\n", i+1, len(s.buffers), testname)
|
|
|
|
io.Copy(w, buf)
|
|
|
|
fmt.Fprintf(os.Stderr, "#### End Gopls Test Logs %d of %d for %q\n", i+1, len(s.buffers), testname)
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-01 19:08:59 -06:00
|
|
|
func singletonEnv(ctx context.Context, t *testing.T) jsonrpc2.StreamServer {
|
|
|
|
return lsprpc.NewStreamServer(cache.New(ctx, nil))
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
2020-04-01 19:08:59 -06:00
|
|
|
func (r *Runner) forwardedEnv(ctx context.Context, t *testing.T) jsonrpc2.StreamServer {
|
2020-02-10 09:34:13 -07:00
|
|
|
ts := r.getTestServer()
|
2020-04-01 19:08:59 -06:00
|
|
|
return lsprpc.NewForwarder("tcp", ts.Addr)
|
2020-02-10 09:34:13 -07:00
|
|
|
}
|
|
|
|
|
2020-04-01 19:08:59 -06:00
|
|
|
func (r *Runner) separateProcessEnv(ctx context.Context, t *testing.T) jsonrpc2.StreamServer {
|
2020-02-18 10:47:19 -07:00
|
|
|
// TODO(rfindley): can we use the autostart behavior here, instead of
|
|
|
|
// pre-starting the remote?
|
2020-04-01 19:08:59 -06:00
|
|
|
socket := r.getRemoteSocket(t)
|
|
|
|
return lsprpc.NewForwarder("unix", socket)
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
type Env struct {
|
2020-03-23 15:26:05 -06:00
|
|
|
T *testing.T
|
|
|
|
Ctx context.Context
|
2020-02-06 17:50:37 -07:00
|
|
|
|
2020-03-04 11:29:23 -07:00
|
|
|
// Most tests should not need to access the workspace, editor, server, or
|
|
|
|
// connection, but they are available if needed.
|
2020-02-06 17:50:37 -07:00
|
|
|
W *fake.Workspace
|
|
|
|
E *fake.Editor
|
2020-02-09 11:38:49 -07:00
|
|
|
Server servertest.Connector
|
2020-03-04 11:29:23 -07:00
|
|
|
Conn *jsonrpc2.Conn
|
2020-02-06 17:50:37 -07:00
|
|
|
|
|
|
|
// mu guards the fields below, for the purpose of checking conditions on
|
|
|
|
// every change to diagnostics.
|
|
|
|
mu sync.Mutex
|
|
|
|
// For simplicity, each waiter gets a unique ID.
|
|
|
|
nextWaiterID int
|
|
|
|
lastDiagnostics map[string]*protocol.PublishDiagnosticsParams
|
|
|
|
waiters map[int]*diagnosticCondition
|
|
|
|
}
|
|
|
|
|
|
|
|
// A diagnosticCondition is satisfied when all expectations are simultaneously
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
// met. At that point, the 'met' channel is closed. On any failure, err is set
|
|
|
|
// and the failed channel is closed.
|
2020-02-06 17:50:37 -07:00
|
|
|
type diagnosticCondition struct {
|
|
|
|
expectations []DiagnosticExpectation
|
|
|
|
met chan struct{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewEnv creates a new test environment using the given workspace and gopls
|
|
|
|
// server.
|
2020-02-09 11:38:49 -07:00
|
|
|
func NewEnv(ctx context.Context, t *testing.T, ws *fake.Workspace, ts servertest.Connector) *Env {
|
2020-02-06 17:50:37 -07:00
|
|
|
t.Helper()
|
|
|
|
conn := ts.Connect(ctx)
|
|
|
|
editor, err := fake.NewConnectedEditor(ctx, ws, conn)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
env := &Env{
|
2020-03-23 15:26:05 -06:00
|
|
|
T: t,
|
|
|
|
Ctx: ctx,
|
2020-02-06 17:50:37 -07:00
|
|
|
W: ws,
|
|
|
|
E: editor,
|
|
|
|
Server: ts,
|
2020-03-04 11:29:23 -07:00
|
|
|
Conn: conn,
|
2020-02-06 17:50:37 -07:00
|
|
|
lastDiagnostics: make(map[string]*protocol.PublishDiagnosticsParams),
|
|
|
|
waiters: make(map[int]*diagnosticCondition),
|
|
|
|
}
|
|
|
|
env.E.Client().OnDiagnostics(env.onDiagnostics)
|
|
|
|
return env
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Env) onDiagnostics(_ context.Context, d *protocol.PublishDiagnosticsParams) error {
|
|
|
|
e.mu.Lock()
|
|
|
|
defer e.mu.Unlock()
|
|
|
|
|
|
|
|
pth := e.W.URIToPath(d.URI)
|
|
|
|
e.lastDiagnostics[pth] = d
|
|
|
|
|
|
|
|
for id, condition := range e.waiters {
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
if meetsExpectations(e.lastDiagnostics, condition.expectations) {
|
2020-02-06 17:50:37 -07:00
|
|
|
delete(e.waiters, id)
|
|
|
|
close(condition.met)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
// ExpectDiagnostics asserts that the current diagnostics in the editor match
|
|
|
|
// the given expectations. It is intended to be used together with Env.Await to
|
|
|
|
// allow waiting on simpler diagnostic expectations (for example,
|
|
|
|
// AnyDiagnosticsACurrenttVersion), followed by more detailed expectations
|
|
|
|
// tested by ExpectDiagnostics.
|
|
|
|
//
|
|
|
|
// For example:
|
|
|
|
// env.RegexpReplace("foo.go", "a", "x")
|
|
|
|
// env.Await(env.AnyDiagnosticAtCurrentVersion("foo.go"))
|
|
|
|
// env.ExpectDiagnostics(env.DiagnosticAtRegexp("foo.go", "x"))
|
|
|
|
//
|
|
|
|
// This has the advantage of not timing out if the diagnostic received for
|
|
|
|
// "foo.go" does not match the expectation: instead it fails early.
|
|
|
|
func (e *Env) ExpectDiagnostics(expectations ...DiagnosticExpectation) {
|
|
|
|
e.T.Helper()
|
|
|
|
e.mu.Lock()
|
|
|
|
defer e.mu.Unlock()
|
|
|
|
if !meetsExpectations(e.lastDiagnostics, expectations) {
|
|
|
|
e.T.Fatalf("diagnostic are unmet:\n%s\nlast diagnostics:\n%s", summarizeExpectations(expectations), formatDiagnostics(e.lastDiagnostics))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func meetsExpectations(m map[string]*protocol.PublishDiagnosticsParams, expectations []DiagnosticExpectation) bool {
|
2020-02-06 17:50:37 -07:00
|
|
|
for _, e := range expectations {
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
diags, ok := m[e.Path]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !e.IsMet(diags) {
|
2020-02-06 17:50:37 -07:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// A DiagnosticExpectation is a condition that must be met by the current set
|
|
|
|
// of diagnostics.
|
|
|
|
type DiagnosticExpectation struct {
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
// 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.
|
2020-02-06 17:50:37 -07:00
|
|
|
Description string
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
// Path is the workspace-relative path to the file being asserted on.
|
|
|
|
Path string
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// EmptyDiagnostics asserts that diagnostics are empty for the
|
|
|
|
// workspace-relative path name.
|
|
|
|
func EmptyDiagnostics(name string) DiagnosticExpectation {
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
|
|
|
|
return len(diags.Diagnostics) == 0
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
return DiagnosticExpectation{
|
|
|
|
IsMet: isMet,
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
Description: "empty diagnostics",
|
|
|
|
Path: name,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AnyDiagnosticAtCurrentVersion asserts that there is a diagnostic report for
|
|
|
|
// the current edited version of the buffer corresponding to the given
|
|
|
|
// workspace-relative pathname.
|
|
|
|
func (e *Env) AnyDiagnosticAtCurrentVersion(name string) DiagnosticExpectation {
|
|
|
|
version := e.E.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,
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-28 12:56:20 -07:00
|
|
|
// 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 {
|
|
|
|
pos := e.RegexpSearch(name, re)
|
|
|
|
expectation := DiagnosticAt(name, pos.Line, pos.Column)
|
|
|
|
expectation.Description += fmt.Sprintf(" (location of %q)", re)
|
|
|
|
return expectation
|
|
|
|
}
|
|
|
|
|
2020-02-06 17:50:37 -07:00
|
|
|
// DiagnosticAt asserts that there is a diagnostic entry at the position
|
|
|
|
// specified by line and col, for the workspace-relative path name.
|
|
|
|
func DiagnosticAt(name string, line, col int) DiagnosticExpectation {
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
|
|
|
|
for _, d := range diags.Diagnostics {
|
2020-02-06 17:50:37 -07:00
|
|
|
if d.Range.Start.Line == float64(line) && d.Range.Start.Character == float64(col) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return DiagnosticExpectation{
|
|
|
|
IsMet: isMet,
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
Description: fmt.Sprintf("diagnostic at {line:%d, column:%d}", line, col),
|
|
|
|
Path: name,
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
// Await waits for all diagnostic expectations to simultaneously be met. It
|
|
|
|
// should only be called from the main test goroutine.
|
2020-02-06 17:50:37 -07:00
|
|
|
func (e *Env) Await(expectations ...DiagnosticExpectation) {
|
|
|
|
// NOTE: in the future this mechanism extend beyond just diagnostics, for
|
|
|
|
// example by modifying IsMet to be a func(*Env) boo. However, that would
|
|
|
|
// require careful checking of conditions around every state change, so for
|
|
|
|
// now we just limit the scope to diagnostic conditions.
|
|
|
|
|
2020-03-23 15:26:05 -06:00
|
|
|
e.T.Helper()
|
2020-02-06 17:50:37 -07:00
|
|
|
e.mu.Lock()
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
// Before adding the waiter, we check if the condition is currently met or
|
|
|
|
// failed to avoid a race where the condition was realized before Await was
|
|
|
|
// called.
|
|
|
|
if meetsExpectations(e.lastDiagnostics, expectations) {
|
2020-02-06 17:50:37 -07:00
|
|
|
e.mu.Unlock()
|
|
|
|
return
|
|
|
|
}
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
cond := &diagnosticCondition{
|
2020-02-06 17:50:37 -07:00
|
|
|
expectations: expectations,
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
met: make(chan struct{}),
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
e.waiters[e.nextWaiterID] = cond
|
2020-02-06 17:50:37 -07:00
|
|
|
e.nextWaiterID++
|
|
|
|
e.mu.Unlock()
|
|
|
|
|
|
|
|
select {
|
2020-03-23 15:26:05 -06:00
|
|
|
case <-e.Ctx.Done():
|
2020-02-06 17:50:37 -07:00
|
|
|
// Debugging an unmet expectation can be tricky, so we put some effort into
|
|
|
|
// nicely formatting the failure.
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
summary := summarizeExpectations(expectations)
|
2020-02-06 17:50:37 -07:00
|
|
|
e.mu.Lock()
|
|
|
|
diagString := formatDiagnostics(e.lastDiagnostics)
|
|
|
|
e.mu.Unlock()
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
e.T.Fatalf("waiting on:\n\t%s\nerr: %v\ndiagnostics:\n%s", summary, e.Ctx.Err(), diagString)
|
|
|
|
case <-cond.met:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func summarizeExpectations(expectations []DiagnosticExpectation) string {
|
|
|
|
var descs []string
|
|
|
|
for _, e := range expectations {
|
|
|
|
descs = append(descs, fmt.Sprintf("%s: %s", e.Path, e.Description))
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
return strings.Join(descs, "\n\t")
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func formatDiagnostics(diags map[string]*protocol.PublishDiagnosticsParams) string {
|
|
|
|
var b strings.Builder
|
|
|
|
for name, params := range diags {
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
b.WriteString(fmt.Sprintf("\t%s (version %d):\n", name, int(params.Version)))
|
2020-02-06 17:50:37 -07:00
|
|
|
for _, d := range params.Diagnostics {
|
internal/lsp/regtest: add functions to make diagnostic assertions easier
One of the tricky things about asserting on conditions in regtests is
the asynchronous nature of LSP. For example, as the LSP client we cannot
be sure when we've received all diagnostics for a given file.
Currently, regtests are implemented by awaiting specific diagnostic
expectations. This means that if gopls generates diagnostics that do
not match those expectations, we can only time out the test.
Ideally, we would want to know that gopls is done generating all diagnostics
for the current file state. This is not possible without knowing the
status of diagnostics for. Barring this, we would want to know that
diagnostics are done for the current file version. Unfortunately, that
also is not possible, because a new version of file B can affect
diagnostics in file A.
So in lieu of this information, this CL exposes a few tools that can be
used to improve the experience of writing new regtests.
- A new expectation is added: AnyDiagnosticAtCurrentVersion, that is
satisfied if any diagnostics have been received for the current
buffer version.
- ExpectDiagnostics is added to Env, to help check whether the current
diagnostics matches expectations.
Updates golang/go#38113
Change-Id: I48d2c3db87c13ac3ab424d01d9444cbc285af9e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/226842
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-04-01 12:56:48 -06:00
|
|
|
b.WriteString(fmt.Sprintf("\t\t(%d, %d): %s\n", int(d.Range.Start.Line), int(d.Range.Start.Character), d.Message))
|
2020-02-06 17:50:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return b.String()
|
|
|
|
}
|