ipld-eth-server/vendor/github.com/aristanetworks/goarista/lanz/client_test.go
Matt K 293dd2e848 Add vendor dir (#16) (#4)
* Add vendor dir so builds dont require dep

* Pin specific version go-eth version
2018-01-29 13:44:18 -06:00

290 lines
5.9 KiB
Go

// Copyright (c) 2016 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.
package lanz_test
import (
"bytes"
"encoding/binary"
"errors"
"io"
"net"
"testing"
"time"
"github.com/aristanetworks/goarista/lanz"
pb "github.com/aristanetworks/goarista/lanz/proto"
"github.com/aristanetworks/goarista/test"
"github.com/aristanetworks/glog"
"github.com/golang/protobuf/proto"
)
var testProtoBuf = &pb.LanzRecord{
ErrorRecord: &pb.ErrorRecord{
Timestamp: proto.Uint64(146591697107549),
ErrorMessage: proto.String("Error"),
},
}
type testConnector struct {
reader *bytes.Reader
open bool
refusing bool
connect chan bool
block chan bool
}
func (c *testConnector) Read(p []byte) (int, error) {
if !c.open {
return 0, errors.New("closed")
}
if c.reader.Len() == 0 {
<-c.block
return 0, io.EOF
}
return c.reader.Read(p)
}
func (c *testConnector) Close() error {
if !c.open {
return nil
}
c.open = false
close(c.block)
return nil
}
func (c *testConnector) Connect() error {
var err error
if c.refusing {
err = errors.New("refused")
} else {
c.block = make(chan bool)
c.open = true
}
if c.connect != nil {
c.connect <- true
}
return err
}
func launchClient(ch chan<- *pb.LanzRecord, conn lanz.ConnectReadCloser) (lanz.Client, chan bool) {
done := make(chan bool)
c := lanz.New(lanz.WithConnector(conn), lanz.WithBackoff(1*time.Millisecond))
go func() {
c.Run(ch)
done <- true
}()
return c, done
}
func pbsToStream(pbs []*pb.LanzRecord) []byte {
var r []byte
for _, p := range pbs {
b, err := proto.Marshal(p)
if err != nil {
glog.Fatalf("Can't marshal pb: %v", err)
}
bLen := uint64(len(b))
s := make([]byte, binary.MaxVarintLen64)
sLen := binary.PutUvarint(s, bLen)
r = append(r, s[:sLen]...)
r = append(r, b...)
}
return r
}
func testStream() []byte {
return pbsToStream([]*pb.LanzRecord{testProtoBuf})
}
// This function tests that basic workflow works.
func TestSuccessPath(t *testing.T) {
pbs := []*pb.LanzRecord{
{
ConfigRecord: &pb.ConfigRecord{
Timestamp: proto.Uint64(146591697107544),
LanzVersion: proto.Uint32(1),
NumOfPorts: proto.Uint32(146),
SegmentSize: proto.Uint32(512),
MaxQueueSize: proto.Uint32(524288000),
PortConfigRecord: []*pb.ConfigRecord_PortConfigRecord{
{
IntfName: proto.String("Cpu"),
SwitchId: proto.Uint32(2048),
PortId: proto.Uint32(4096),
InternalPort: proto.Bool(false),
HighThreshold: proto.Uint32(50000),
LowThreshold: proto.Uint32(25000),
},
},
GlobalUsageReportingEnabled: proto.Bool(true),
},
},
{
CongestionRecord: &pb.CongestionRecord{
Timestamp: proto.Uint64(146591697107546),
IntfName: proto.String("Cpu"),
SwitchId: proto.Uint32(2048),
PortId: proto.Uint32(4096),
QueueSize: proto.Uint32(30000),
},
},
{
ErrorRecord: &pb.ErrorRecord{
Timestamp: proto.Uint64(146591697107549),
ErrorMessage: proto.String("Error"),
},
},
}
conn := &testConnector{reader: bytes.NewReader(pbsToStream(pbs))}
ch := make(chan *pb.LanzRecord)
c, done := launchClient(ch, conn)
for i, p := range pbs {
r, ok := <-ch
if !ok {
t.Fatalf("Unexpected closed channel")
}
if !test.DeepEqual(p, r) {
t.Fatalf("Test case %d: expected %v, but got %v", i, p, r)
}
}
c.Stop()
<-done
if conn.open {
t.Fatalf("Connection still open after stopping")
}
}
// This function tests that the client keeps retrying on connection error.
func TestRetryOnConnectionError(t *testing.T) {
conn := &testConnector{
refusing: true,
connect: make(chan bool),
}
ch := make(chan *pb.LanzRecord)
c, done := launchClient(ch, conn)
connects := 3
stopped := false
for !stopped {
select {
case <-conn.connect:
connects--
if connects == 0 {
c.Stop()
}
case <-done:
stopped = true
}
}
}
// This function tests that the client will reconnect if the connection gets closed.
func TestRetryOnClose(t *testing.T) {
conn := &testConnector{
reader: bytes.NewReader(testStream()),
connect: make(chan bool),
}
ch := make(chan *pb.LanzRecord)
c, done := launchClient(ch, conn)
<-conn.connect
<-ch
conn.Close()
<-conn.connect
conn.Close()
<-conn.connect
c.Stop()
<-done
if conn.open {
t.Fatalf("Connection still open after stopping")
}
}
// This function tests that the client will reconnect if it receives truncated input.
func TestRetryOnTrucatedInput(t *testing.T) {
conn := &testConnector{
reader: bytes.NewReader(testStream()[:5]),
connect: make(chan bool),
}
ch := make(chan *pb.LanzRecord)
c, done := launchClient(ch, conn)
<-conn.connect
conn.block <- false
<-conn.connect
c.Stop()
<-done
if conn.open {
t.Fatalf("Connection still open after stopping")
}
}
// This function tests that the client will reconnect if it receives malformed input.
func TestRetryOnMalformedInput(t *testing.T) {
stream := testStream()
uLen := binary.PutUvarint(stream, 3)
conn := &testConnector{
reader: bytes.NewReader(stream[:uLen+3]),
connect: make(chan bool),
}
ch := make(chan *pb.LanzRecord)
c, done := launchClient(ch, conn)
<-conn.connect
<-conn.connect
c.Stop()
<-done
if conn.open {
t.Fatalf("Connection still open after stopping")
}
}
// This function tests the default connector.
func TestDefaultConnector(t *testing.T) {
stream := testStream()
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Can't listen: %v", err)
}
go func() {
conn, err := l.Accept()
if err != nil {
t.Fatalf("Can't accept: %v", err)
}
conn.Write(stream)
conn.Close()
}()
ch := make(chan *pb.LanzRecord)
done := make(chan bool)
c := lanz.New(lanz.WithAddr(l.Addr().String()), lanz.WithBackoff(1*time.Millisecond))
go func() {
c.Run(ch)
done <- true
}()
p := <-ch
c.Stop()
<-done
if !test.DeepEqual(p, testProtoBuf) {
t.Fatalf("Expected protobuf %v, but got %v", testProtoBuf, p)
}
}