golang.org/x/tools/gopls@v0.15.3/internal/lsprpc/binder_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package lsprpc_test 6 7 import ( 8 "context" 9 "regexp" 10 "strings" 11 "testing" 12 "time" 13 14 "golang.org/x/tools/gopls/internal/protocol" 15 jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" 16 17 . "golang.org/x/tools/gopls/internal/lsprpc" 18 ) 19 20 // ServerBinder binds incoming connections to a new server. 21 type ServerBinder struct { 22 newServer ServerFunc 23 } 24 25 func NewServerBinder(newServer ServerFunc) *ServerBinder { 26 return &ServerBinder{newServer: newServer} 27 } 28 29 // streamServer used to have this method, but it was never used. 30 // TODO(adonovan): figure out whether we need any of this machinery 31 // and, if not, delete it. In the meantime, it's better that it sit 32 // in the test package with all the other mothballed machinery 33 // than in the production code where it would couple streamServer 34 // and ServerBinder. 35 /* 36 func (s *streamServer) Binder() *ServerBinder { 37 newServer := func(ctx context.Context, client protocol.ClientCloser) protocol.Server { 38 session := cache.NewSession(ctx, s.cache) 39 svr := s.serverForTest 40 if svr == nil { 41 options := settings.DefaultOptions(s.optionsOverrides) 42 svr = server.New(session, client, options) 43 if instance := debug.GetInstance(ctx); instance != nil { 44 instance.AddService(svr, session) 45 } 46 } 47 return svr 48 } 49 return NewServerBinder(newServer) 50 } 51 */ 52 53 func (b *ServerBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { 54 client := protocol.ClientDispatcherV2(conn) 55 server := b.newServer(ctx, client) 56 serverHandler := protocol.ServerHandlerV2(server) 57 // Wrap the server handler to inject the client into each request context, so 58 // that log events are reflected back to the client. 59 wrapped := jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { 60 ctx = protocol.WithClient(ctx, client) 61 return serverHandler.Handle(ctx, req) 62 }) 63 preempter := &Canceler{ 64 Conn: conn, 65 } 66 return jsonrpc2_v2.ConnectionOptions{ 67 Handler: wrapped, 68 Preempter: preempter, 69 } 70 } 71 72 type TestEnv struct { 73 Conns []*jsonrpc2_v2.Connection 74 Servers []*jsonrpc2_v2.Server 75 } 76 77 func (e *TestEnv) Shutdown(t *testing.T) { 78 for _, s := range e.Servers { 79 s.Shutdown() 80 } 81 for _, c := range e.Conns { 82 if err := c.Close(); err != nil { 83 t.Error(err) 84 } 85 } 86 for _, s := range e.Servers { 87 if err := s.Wait(); err != nil { 88 t.Error(err) 89 } 90 } 91 } 92 93 func (e *TestEnv) serve(ctx context.Context, t *testing.T, server jsonrpc2_v2.Binder) (jsonrpc2_v2.Listener, *jsonrpc2_v2.Server) { 94 l, err := jsonrpc2_v2.NetPipeListener(ctx) 95 if err != nil { 96 t.Fatal(err) 97 } 98 s := jsonrpc2_v2.NewServer(ctx, l, server) 99 e.Servers = append(e.Servers, s) 100 return l, s 101 } 102 103 func (e *TestEnv) dial(ctx context.Context, t *testing.T, dialer jsonrpc2_v2.Dialer, client jsonrpc2_v2.Binder, forwarded bool) *jsonrpc2_v2.Connection { 104 if forwarded { 105 l, _ := e.serve(ctx, t, NewForwardBinder(dialer)) 106 dialer = l.Dialer() 107 } 108 conn, err := jsonrpc2_v2.Dial(ctx, dialer, client) 109 if err != nil { 110 t.Fatal(err) 111 } 112 e.Conns = append(e.Conns, conn) 113 return conn 114 } 115 116 func staticClientBinder(client protocol.Client) jsonrpc2_v2.Binder { 117 f := func(context.Context, protocol.Server) protocol.Client { return client } 118 return NewClientBinder(f) 119 } 120 121 func staticServerBinder(server protocol.Server) jsonrpc2_v2.Binder { 122 f := func(ctx context.Context, client protocol.ClientCloser) protocol.Server { 123 return server 124 } 125 return NewServerBinder(f) 126 } 127 128 func TestClientLoggingV2(t *testing.T) { 129 ctx := context.Background() 130 131 for name, forwarded := range map[string]bool{ 132 "forwarded": true, 133 "standalone": false, 134 } { 135 t.Run(name, func(t *testing.T) { 136 client := FakeClient{Logs: make(chan string, 10)} 137 env := new(TestEnv) 138 defer env.Shutdown(t) 139 l, _ := env.serve(ctx, t, staticServerBinder(PingServer{})) 140 conn := env.dial(ctx, t, l.Dialer(), staticClientBinder(client), forwarded) 141 142 if err := protocol.ServerDispatcherV2(conn).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}); err != nil { 143 t.Errorf("DidOpen: %v", err) 144 } 145 select { 146 case got := <-client.Logs: 147 want := "ping" 148 matched, err := regexp.MatchString(want, got) 149 if err != nil { 150 t.Fatal(err) 151 } 152 if !matched { 153 t.Errorf("got log %q, want a log containing %q", got, want) 154 } 155 case <-time.After(1 * time.Second): 156 t.Error("timeout waiting for client log") 157 } 158 }) 159 } 160 } 161 162 func TestRequestCancellationV2(t *testing.T) { 163 ctx := context.Background() 164 165 for name, forwarded := range map[string]bool{ 166 "forwarded": true, 167 "standalone": false, 168 } { 169 t.Run(name, func(t *testing.T) { 170 server := WaitableServer{ 171 Started: make(chan struct{}), 172 Completed: make(chan error), 173 } 174 env := new(TestEnv) 175 defer env.Shutdown(t) 176 l, _ := env.serve(ctx, t, staticServerBinder(server)) 177 client := FakeClient{Logs: make(chan string, 10)} 178 conn := env.dial(ctx, t, l.Dialer(), staticClientBinder(client), forwarded) 179 180 sd := protocol.ServerDispatcherV2(conn) 181 ctx, cancel := context.WithCancel(ctx) 182 183 result := make(chan error) 184 go func() { 185 _, err := sd.Hover(ctx, &protocol.HoverParams{}) 186 result <- err 187 }() 188 // Wait for the Hover request to start. 189 <-server.Started 190 cancel() 191 if err := <-result; err == nil { 192 t.Error("nil error for cancelled Hover(), want non-nil") 193 } 194 if err := <-server.Completed; err == nil || !strings.Contains(err.Error(), "cancelled hover") { 195 t.Errorf("Hover(): unexpected server-side error %v", err) 196 } 197 }) 198 } 199 }