mirror of
https://github.com/golang/go
synced 2024-11-18 15:14:44 -07:00
9b4b92067d
closerList was already unnecessarily abstract, and tracking connections will allow us to also wait for all connections to finish. Share functionality by embedding this type in PipeServer, TCPServer. Change-Id: Ib2cb2157c1477f904bc278aa91902f5c633afe13 Reviewed-on: https://go-review.googlesource.com/c/tools/+/238547 Reviewed-by: Ian Cottrell <iancottrell@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
120 lines
3.2 KiB
Go
120 lines
3.2 KiB
Go
// 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 servertest provides utilities for running tests against a remote LSP
|
|
// server.
|
|
package servertest
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
|
)
|
|
|
|
// Connector is the interface used to connect to a server.
|
|
type Connector interface {
|
|
Connect(context.Context) jsonrpc2.Conn
|
|
}
|
|
|
|
// TCPServer is a helper for executing tests against a remote jsonrpc2
|
|
// connection. Once initialized, its Addr field may be used to connect a
|
|
// jsonrpc2 client.
|
|
type TCPServer struct {
|
|
*connList
|
|
|
|
Addr string
|
|
|
|
ln net.Listener
|
|
framer jsonrpc2.Framer
|
|
}
|
|
|
|
// NewTCPServer returns a new test server listening on local tcp port and
|
|
// serving incoming jsonrpc2 streams using the provided stream server. It
|
|
// panics on any error.
|
|
func NewTCPServer(ctx context.Context, server jsonrpc2.StreamServer, framer jsonrpc2.Framer) *TCPServer {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
panic(fmt.Sprintf("servertest: failed to listen: %v", err))
|
|
}
|
|
if framer == nil {
|
|
framer = jsonrpc2.NewHeaderStream
|
|
}
|
|
go jsonrpc2.Serve(ctx, ln, server, 0)
|
|
return &TCPServer{Addr: ln.Addr().String(), ln: ln, framer: framer, connList: &connList{}}
|
|
}
|
|
|
|
// Connect dials the test server and returns a jsonrpc2 Connection that is
|
|
// ready for use.
|
|
func (s *TCPServer) Connect(ctx context.Context) jsonrpc2.Conn {
|
|
netConn, err := net.Dial("tcp", s.Addr)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("servertest: failed to connect to test instance: %v", err))
|
|
}
|
|
conn := jsonrpc2.NewConn(s.framer(netConn))
|
|
s.add(conn)
|
|
return conn
|
|
}
|
|
|
|
// PipeServer is a test server that handles connections over io.Pipes.
|
|
type PipeServer struct {
|
|
*connList
|
|
server jsonrpc2.StreamServer
|
|
framer jsonrpc2.Framer
|
|
}
|
|
|
|
// NewPipeServer returns a test server that can be connected to via io.Pipes.
|
|
func NewPipeServer(ctx context.Context, server jsonrpc2.StreamServer, framer jsonrpc2.Framer) *PipeServer {
|
|
if framer == nil {
|
|
framer = jsonrpc2.NewRawStream
|
|
}
|
|
return &PipeServer{server: server, framer: framer, connList: &connList{}}
|
|
}
|
|
|
|
// Connect creates new io.Pipes and binds them to the underlying StreamServer.
|
|
func (s *PipeServer) Connect(ctx context.Context) jsonrpc2.Conn {
|
|
sPipe, cPipe := net.Pipe()
|
|
serverStream := s.framer(sPipe)
|
|
serverConn := jsonrpc2.NewConn(serverStream)
|
|
s.add(serverConn)
|
|
go s.server.ServeStream(ctx, serverConn)
|
|
|
|
clientStream := s.framer(cPipe)
|
|
clientConn := jsonrpc2.NewConn(clientStream)
|
|
s.add(clientConn)
|
|
return clientConn
|
|
}
|
|
|
|
// connList tracks closers to run when a testserver is closed. This is a
|
|
// convenience, so that callers don't have to worry about closing each
|
|
// connection.
|
|
type connList struct {
|
|
mu sync.Mutex
|
|
conns []jsonrpc2.Conn
|
|
}
|
|
|
|
func (l *connList) add(conn jsonrpc2.Conn) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.conns = append(l.conns, conn)
|
|
}
|
|
|
|
func (l *connList) Close() error {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
var errmsgs []string
|
|
for _, conn := range l.conns {
|
|
if err := conn.Close(); err != nil {
|
|
errmsgs = append(errmsgs, err.Error())
|
|
}
|
|
}
|
|
if len(errmsgs) > 0 {
|
|
return fmt.Errorf("closing errors:\n%s", strings.Join(errmsgs, "\n"))
|
|
}
|
|
return nil
|
|
}
|