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  }