lib/connections: Always run a simple connection test

Establish a connection over both TCP and QUIC and transmit a simple
message over it. Presumably this should weed out panics and crap from
the QUIC package at test time...
This commit is contained in:
Jakob Borg 2021-08-03 13:05:41 +02:00
parent abdf024517
commit 739bbe2ad5

View File

@ -7,10 +7,12 @@
package connections package connections
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"net" "net"
@ -296,7 +298,7 @@ func TestNextDialRegistryCleanup(t *testing.T) {
} }
} }
func BenchmarkConnections(pb *testing.B) { func BenchmarkConnections(b *testing.B) {
addrs := []string{ addrs := []string{
"tcp://127.0.0.1:0", "tcp://127.0.0.1:0",
"quic://127.0.0.1:0", "quic://127.0.0.1:0",
@ -317,9 +319,13 @@ func BenchmarkConnections(pb *testing.B) {
} }
for _, addr := range addrs { for _, addr := range addrs {
for _, sz := range sizes { for _, sz := range sizes {
data := make([]byte, sz)
if _, err := rand.Read(data); err != nil {
b.Fatal(err)
}
for _, direction := range []string{"cs", "sc"} { for _, direction := range []string{"cs", "sc"} {
proto := strings.SplitN(addr, ":", 2)[0] proto := strings.SplitN(addr, ":", 2)[0]
pb.Run(fmt.Sprintf("%s_%d_%s", proto, sz, direction), func(b *testing.B) { b.Run(fmt.Sprintf("%s_%d_%s", proto, sz, direction), func(b *testing.B) {
if proto == "relay" && !haveRelay { if proto == "relay" && !haveRelay {
b.Skip("could not connect to relay") b.Skip("could not connect to relay")
} }
@ -327,61 +333,81 @@ func BenchmarkConnections(pb *testing.B) {
if direction == "sc" { if direction == "sc" {
server, client = client, server server, client = client, server
} }
data := make([]byte, sz)
if _, err := rand.Read(data); err != nil {
b.Fatal(err)
}
total := 0 total := 0
wg := sync.NewWaitGroup()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
wg := sync.NewWaitGroup()
wg.Add(2) wg.Add(2)
errC := make(chan error, 2)
go func() { go func() {
if err := sendMsg(client, data); err != nil { wg.Wait()
b.Fatal(err) close(errC)
}()
go func() {
if _, err := client.Write(data); err != nil {
errC <- err
return
} }
wg.Done() wg.Done()
}() }()
go func() { go func() {
if err := recvMsg(server, data); err != nil { if _, err := io.ReadFull(server, data); err != nil {
b.Fatal(err) errC <- err
return
} }
total += sz total += sz
wg.Done() wg.Done()
}() }()
wg.Wait() err := <-errC
if err != nil {
b.Fatal(err)
}
} }
b.ReportAllocs() b.ReportAllocs()
b.SetBytes(int64(total / b.N)) b.SetBytes(int64(total / b.N))
}) })
}) })
} }
} }
} }
} }
func sendMsg(c internalConn, buf []byte) error { func TestConnectionEstablishment(t *testing.T) {
n, err := c.Write(buf) addrs := []string{
if n != len(buf) || err != nil { "tcp://127.0.0.1:0",
return err "quic://127.0.0.1:0",
}
return nil
} }
func recvMsg(c internalConn, buf []byte) error { send := make([]byte, 128<<10)
for read := 0; read != len(buf); { if _, err := rand.Read(send); err != nil {
n, err := c.Read(buf) t.Fatal(err)
read += n
if err != nil {
return err
}
}
return nil
} }
func withConnectionPair(b *testing.B, connUri string, h func(client, server internalConn)) { for _, addr := range addrs {
proto := strings.SplitN(addr, ":", 2)[0]
t.Run(proto, func(t *testing.T) {
withConnectionPair(t, addr, func(client, server internalConn) {
if _, err := client.Write(send); err != nil {
t.Fatal(err)
}
recv := make([]byte, len(send))
if _, err := io.ReadFull(server, recv); err != nil {
t.Fatal(err)
}
if !bytes.Equal(recv, send) {
t.Fatal("data mismatch")
}
})
})
}
}
func withConnectionPair(b interface{ Fatal(...interface{}) }, connUri string, h func(client, server internalConn)) {
// Root of the service tree. // Root of the service tree.
supervisor := suture.New("main", suture.Spec{ supervisor := suture.New("main", suture.Spec{
PassThroughPanics: true, PassThroughPanics: true,
@ -449,19 +475,22 @@ func withConnectionPair(b *testing.B, connUri string, h func(client, server inte
} }
} }
data := []byte("hello")
// Quic does not start a stream until some data is sent through, so send something for the AcceptStream // Quic does not start a stream until some data is sent through, so send something for the AcceptStream
// to fire on the other side. // to fire on the other side.
if err := sendMsg(clientConn, data); err != nil { send := []byte("hello")
if _, err := clientConn.Write(send); err != nil {
b.Fatal(err) b.Fatal(err)
} }
serverConn := <-conns serverConn := <-conns
if err := recvMsg(serverConn, data); err != nil { recv := make([]byte, len(send))
if _, err := io.ReadFull(serverConn, recv); err != nil {
b.Fatal(err) b.Fatal(err)
} }
if !bytes.Equal(recv, send) {
b.Fatal("data mismatch")
}
h(clientConn, serverConn) h(clientConn, serverConn)
@ -469,7 +498,7 @@ func withConnectionPair(b *testing.B, connUri string, h func(client, server inte
_ = serverConn.Close() _ = serverConn.Close()
} }
func mustGetCert(b *testing.B) tls.Certificate { func mustGetCert(b interface{ Fatal(...interface{}) }) tls.Certificate {
f1, err := ioutil.TempFile("", "") f1, err := ioutil.TempFile("", "")
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)