diff --git a/internal/lsp/cmd/serve_test.go b/internal/lsp/cmd/serve_test.go new file mode 100644 index 00000000000..ad3f8b2336b --- /dev/null +++ b/internal/lsp/cmd/serve_test.go @@ -0,0 +1,86 @@ +package cmd + +import ( + "context" + "io" + "regexp" + "testing" + "time" + + "golang.org/x/tools/internal/jsonrpc2" + "golang.org/x/tools/internal/lsp/protocol" + "golang.org/x/tools/internal/telemetry/log" +) + +type fakeServer struct { + protocol.Server + client protocol.Client +} + +func (s *fakeServer) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error { + // Our instrumentation should cause this message to be logged back to the LSP + // client. + log.Print(ctx, "ping") + return nil +} + +type fakeClient struct { + protocol.Client + + logs chan string +} + +func (c *fakeClient) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error { + c.logs <- params.Message + return nil +} + +func TestClientLogging(t *testing.T) { + server := &fakeServer{} + client := &fakeClient{logs: make(chan string)} + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Bind our fake client and server. + // sReader and sWriter read from and write to the server. cReader and cWriter + // read from and write to the client. + sReader, sWriter := io.Pipe() + cReader, cWriter := io.Pipe() + close := func() { + failOnErr := func(err error) { + if err != nil { + t.Fatal(err) + } + } + failOnErr(sReader.Close()) + failOnErr(cReader.Close()) + failOnErr(sWriter.Close()) + failOnErr(cWriter.Close()) + } + defer close() + serverStream := jsonrpc2.NewStream(sReader, cWriter) + // The returned client dispatches to the client, but it is already stored + // in the context by NewServer, so we can ignore it. + serverCtx, serverConn, _ := protocol.NewServer(ctx, serverStream, server) + serverConn.AddHandler(&handler{}) + clientStream := jsonrpc2.NewStream(cReader, sWriter) + clientCtx, clientConn, serverDispatch := protocol.NewClient(ctx, clientStream, client) + + go clientConn.Run(clientCtx) + go serverConn.Run(serverCtx) + serverDispatch.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}) + + select { + case got := <-client.logs: + want := "ping" + matched, err := regexp.MatchString(want, got) + if err != nil { + t.Fatal(err) + } + if !matched { + t.Errorf("got log %q, want a log containing %q", got, want) + } + case <-time.After(1 * time.Second): + t.Error("timeout waiting for client log") + } +}