Skip to content

Commit fb353ca

Browse files
committed
internal/lsp/cmd: add a test for client logging
A new test is added to verify that contextual logs are reflected back to the LSP client. In the future when we are considering servers with multiple clients, this test will be used to verify that client log exporting is scoped to the specific client session. Updates golang/go#34111. Change-Id: I29044e5355e25b81a759d064929520345230ea82 Reviewed-on: https://go-review.googlesource.com/c/tools/+/215739 Reviewed-by: Rebecca Stambler <[email protected]>
1 parent 9bae668 commit fb353ca

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed

internal/lsp/cmd/serve_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"io"
6+
"regexp"
7+
"testing"
8+
"time"
9+
10+
"golang.org/x/tools/internal/jsonrpc2"
11+
"golang.org/x/tools/internal/lsp/protocol"
12+
"golang.org/x/tools/internal/telemetry/log"
13+
)
14+
15+
type fakeServer struct {
16+
protocol.Server
17+
client protocol.Client
18+
}
19+
20+
func (s *fakeServer) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
21+
// Our instrumentation should cause this message to be logged back to the LSP
22+
// client.
23+
log.Print(ctx, "ping")
24+
return nil
25+
}
26+
27+
type fakeClient struct {
28+
protocol.Client
29+
30+
logs chan string
31+
}
32+
33+
func (c *fakeClient) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error {
34+
c.logs <- params.Message
35+
return nil
36+
}
37+
38+
func TestClientLogging(t *testing.T) {
39+
server := &fakeServer{}
40+
client := &fakeClient{logs: make(chan string)}
41+
ctx, cancel := context.WithCancel(context.Background())
42+
defer cancel()
43+
44+
// Bind our fake client and server.
45+
// sReader and sWriter read from and write to the server. cReader and cWriter
46+
// read from and write to the client.
47+
sReader, sWriter := io.Pipe()
48+
cReader, cWriter := io.Pipe()
49+
close := func() {
50+
failOnErr := func(err error) {
51+
if err != nil {
52+
t.Fatal(err)
53+
}
54+
}
55+
failOnErr(sReader.Close())
56+
failOnErr(cReader.Close())
57+
failOnErr(sWriter.Close())
58+
failOnErr(cWriter.Close())
59+
}
60+
defer close()
61+
serverStream := jsonrpc2.NewStream(sReader, cWriter)
62+
// The returned client dispatches to the client, but it is already stored
63+
// in the context by NewServer, so we can ignore it.
64+
serverCtx, serverConn, _ := protocol.NewServer(ctx, serverStream, server)
65+
serverConn.AddHandler(&handler{})
66+
clientStream := jsonrpc2.NewStream(cReader, sWriter)
67+
clientCtx, clientConn, serverDispatch := protocol.NewClient(ctx, clientStream, client)
68+
69+
go clientConn.Run(clientCtx)
70+
go serverConn.Run(serverCtx)
71+
serverDispatch.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{})
72+
73+
select {
74+
case got := <-client.logs:
75+
want := "ping"
76+
matched, err := regexp.MatchString(want, got)
77+
if err != nil {
78+
t.Fatal(err)
79+
}
80+
if !matched {
81+
t.Errorf("got log %q, want a log containing %q", got, want)
82+
}
83+
case <-time.After(1 * time.Second):
84+
t.Error("timeout waiting for client log")
85+
}
86+
}

0 commit comments

Comments
 (0)