blob: 840716e92dea172def9f4d685ac4482e5b63b970 [file] [log] [blame]
// Copyright 2015 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 revdial
import (
"bufio"
"bytes"
"io"
"io/ioutil"
"net"
"sync"
"testing"
"time"
"golang.org/x/net/nettest"
)
func TestDialer(t *testing.T) {
pr, pw := io.Pipe()
var out bytes.Buffer
d := NewDialer(bufio.NewReadWriter(
bufio.NewReader(pr),
bufio.NewWriter(&out),
), ioutil.NopCloser(nil))
c, err := d.Dial()
if err != nil {
t.Fatal(err)
}
if c.(*conn).id != 1 {
t.Fatalf("first id = %d; want 1", c.(*conn).id)
}
c.Close() // to verify incoming write frames don't block
c, err = d.Dial()
if err != nil {
t.Fatal(err)
}
if c.(*conn).id != 2 {
t.Fatalf("second id = %d; want 2", c.(*conn).id)
}
if g, w := len(d.conns), 1; g != w {
t.Errorf("size of conns map after dial+close+dial = %v; want %v", g, w)
}
go func() {
// Write "b" and then "ar", and read it as "bar"
pw.Write([]byte{byte(frameWrite), 0, 0, 0, 2, 0, 1, 'b'})
pw.Write([]byte{byte(frameWrite), 0, 0, 0, 1, 0, 1, 'x'}) // verify doesn't block first conn
pw.Write([]byte{byte(frameWrite), 0, 0, 0, 2, 0, 2, 'a', 'r'})
}()
buf := make([]byte, 3)
if n, err := io.ReadFull(c, buf); err != nil {
t.Fatalf("ReadFul = %v (%q), %v", n, buf[:n], err)
}
if string(buf) != "bar" {
t.Fatalf("read = %q; want bar", buf)
}
if _, err := io.WriteString(c, "hello, world"); err != nil {
t.Fatal(err)
}
got := out.String()
want := "N\x00\x00\x00\x01\x00\x00" +
"C\x00\x00\x00\x01\x00\x00" +
"N\x00\x00\x00\x02\x00\x00" +
"W\x00\x00\x00\x02\x00\fhello, world"
if got != want {
t.Errorf("Written on wire differs.\nWrote: %q\n Want: %q", got, want)
}
}
func TestListener(t *testing.T) {
pr, pw := io.Pipe()
var out bytes.Buffer
ln := NewListener(bufio.NewReadWriter(
bufio.NewReader(pr),
bufio.NewWriter(&out),
))
go io.WriteString(pw, "N\x00\x00\x00\x42\x00\x00")
c, err := ln.Accept()
if err != nil {
t.Fatal(err)
}
if g, w := c.(*conn).id, uint32(0x42); g != w {
t.Errorf("conn id = %d; want %d", g, w)
}
go func() {
io.WriteString(pw, "W\x00\x00\x00\x42\x00\x03"+"foo")
io.WriteString(pw, "W\x00\x00\x00\x42\x00\x03"+"bar")
io.WriteString(pw, "C\x00\x00\x00\x42\x00\x00")
}()
slurp, err := ioutil.ReadAll(c)
if g, w := string(slurp), "foobar"; g != w {
t.Errorf("Read %q; want %q", g, w)
}
if err != nil {
t.Errorf("Read = %v", err)
}
ln.Close()
if _, err := ln.Accept(); err == nil {
t.Fatalf("Accept after Closed returned nil error; want error")
}
io.WriteString(c, "first write")
io.WriteString(c, "second write")
c.Close()
got := out.String()
want := "W\x00\x00\x00B\x00\vfirst write" +
"W\x00\x00\x00B\x00\fsecond write" +
"C\x00\x00\x00B\x00\x00"
if got != want {
t.Errorf("Wrote: %q\n Want: %q", got, want)
}
}
func TestInterop(t *testing.T) {
var na, nb net.Conn
if true {
tln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer tln.Close()
na, err = net.Dial("tcp", tln.Addr().String())
if err != nil {
t.Fatal(err)
}
nb, err = tln.Accept()
if err != nil {
t.Fatal(err)
}
} else {
// TODO(bradfitz): also run this way
na, nb = net.Pipe()
}
defer na.Close()
defer nb.Close()
ln := NewListener(bufio.NewReadWriter(
bufio.NewReader(na),
bufio.NewWriter(na),
))
defer ln.Close()
d := NewDialer(bufio.NewReadWriter(
bufio.NewReader(nb),
bufio.NewWriter(nb),
), ioutil.NopCloser(nil))
defer d.Close()
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c, err := d.Dial()
if err != nil {
t.Errorf("Dial: %v", err)
return
}
defer c.Close()
sc, err := ln.Accept()
if err != nil {
t.Errorf("Accept: %v", err)
return
}
defer sc.Close()
const cmsg = "Some client message"
const smsg = "Some server message"
io.WriteString(c, cmsg) // TODO(bradfitz): why the 3/500 failure rate when these are "go io.WriteString"?
io.WriteString(sc, smsg)
buf := make([]byte, len(cmsg))
if n, err := io.ReadFull(c, buf); err != nil {
t.Errorf("reading from client conn: (%d %q, %v)", n, buf[:n], err)
return
}
if string(buf) != smsg {
t.Errorf("client read %q; want %q", buf, smsg)
}
if _, err := io.ReadFull(sc, buf); err != nil {
t.Errorf("reading from server conn: %v", err)
return
}
if string(buf) != cmsg {
t.Errorf("server read %q; want %q", buf, cmsg)
}
}()
}
wg.Wait()
}
// Verify that the server (e.g. the buildlet dialing the coordinator)
// going away unblocks all connections active back to it.
func TestServerEOFKillsConns(t *testing.T) {
pr, pw := io.Pipe()
var out bytes.Buffer
d := NewDialer(bufio.NewReadWriter(
bufio.NewReader(pr),
bufio.NewWriter(&out),
), ioutil.NopCloser(nil))
c, err := d.Dial()
if err != nil {
t.Fatal(err)
}
readErr := make(chan error, 1)
go func() {
_, err := c.Read([]byte{0})
readErr <- err
}()
pw.Close()
select {
case err := <-readErr:
if err == nil {
t.Fatal("got nil read error; want non-nil")
}
case <-time.After(2 * time.Second):
t.Error("timeout waiting for Read")
}
select {
case <-d.Done():
case <-time.After(2 * time.Second):
t.Error("timeout waiting for Done channel")
}
}
func TestConnAgainstNetTest(t *testing.T) {
if testing.Short() {
t.Skipf("testing in short mode")
}
t.Logf("warning: the revdial's SetWriteDeadline support is not complete; some tests involving write deadlines known to be flaky")
nettest.TestConn(t, func() (c1, c2 net.Conn, stop func(), err error) {
tln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
return
}
cc, err := net.Dial("tcp", tln.Addr().String())
if err != nil {
t.Fatal(err)
return
}
sc, err := tln.Accept()
if err != nil {
t.Fatal(err)
return
}
rd := NewDialer(bufio.NewReadWriter(
bufio.NewReader(sc),
bufio.NewWriter(sc),
), ioutil.NopCloser(nil))
rl := NewListener(bufio.NewReadWriter(
bufio.NewReader(cc),
bufio.NewWriter(cc),
))
c1c := make(chan interface{}, 1)
c2c := make(chan interface{}, 1)
go func() {
c, err := rd.Dial()
if err != nil {
c1c <- err
return
}
c1c <- c
}()
go func() {
c, err := rl.Accept()
if err != nil {
c2c <- err
return
}
c2c <- c
}()
switch v := (<-c1c).(type) {
case net.Conn:
c1 = v
case error:
t.Fatalf("revdial.Dial: %v", v)
}
switch v := (<-c2c).(type) {
case net.Conn:
c2 = v
case error:
t.Fatalf("revdial.Accept: %v", v)
}
stop = func() {
tln.Close()
cc.Close()
sc.Close()
}
return
})
}