501699af16
From-SVN: r184819
448 lines
9.8 KiB
Go
448 lines
9.8 KiB
Go
// Copyright 2010 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 netchan
|
|
|
|
import (
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const count = 10 // number of items in most tests
|
|
const closeCount = 5 // number of items when sender closes early
|
|
|
|
const base = 23
|
|
|
|
func exportSend(exp *Exporter, n int, t *testing.T, done chan bool) {
|
|
ch := make(chan int)
|
|
err := exp.Export("exportedSend", ch, Send)
|
|
if err != nil {
|
|
t.Fatal("exportSend:", err)
|
|
}
|
|
go func() {
|
|
for i := 0; i < n; i++ {
|
|
ch <- base + i
|
|
}
|
|
close(ch)
|
|
if done != nil {
|
|
done <- true
|
|
}
|
|
}()
|
|
}
|
|
|
|
func exportReceive(exp *Exporter, t *testing.T, expDone chan bool) {
|
|
ch := make(chan int)
|
|
err := exp.Export("exportedRecv", ch, Recv)
|
|
expDone <- true
|
|
if err != nil {
|
|
t.Fatal("exportReceive:", err)
|
|
}
|
|
for i := 0; i < count; i++ {
|
|
v, ok := <-ch
|
|
if !ok {
|
|
if i != closeCount {
|
|
t.Errorf("exportReceive expected close at %d; got one at %d", closeCount, i)
|
|
}
|
|
break
|
|
}
|
|
if v != base+i {
|
|
t.Errorf("export Receive: bad value: expected %d+%d=%d; got %d", base, i, base+i, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func importSend(imp *Importer, n int, t *testing.T, done chan bool) {
|
|
ch := make(chan int)
|
|
err := imp.ImportNValues("exportedRecv", ch, Send, 3, -1)
|
|
if err != nil {
|
|
t.Fatal("importSend:", err)
|
|
}
|
|
go func() {
|
|
for i := 0; i < n; i++ {
|
|
ch <- base + i
|
|
}
|
|
close(ch)
|
|
if done != nil {
|
|
done <- true
|
|
}
|
|
}()
|
|
}
|
|
|
|
func importReceive(imp *Importer, t *testing.T, done chan bool) {
|
|
ch := make(chan int)
|
|
err := imp.ImportNValues("exportedSend", ch, Recv, 3, count)
|
|
if err != nil {
|
|
t.Fatal("importReceive:", err)
|
|
}
|
|
for i := 0; i < count; i++ {
|
|
v, ok := <-ch
|
|
if !ok {
|
|
if i != closeCount {
|
|
t.Errorf("importReceive expected close at %d; got one at %d", closeCount, i)
|
|
}
|
|
break
|
|
}
|
|
if v != base+i {
|
|
t.Errorf("importReceive: bad value: expected %d+%d=%d; got %+d", base, i, base+i, v)
|
|
}
|
|
}
|
|
if done != nil {
|
|
done <- true
|
|
}
|
|
}
|
|
|
|
func TestExportSendImportReceive(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
exportSend(exp, count, t, nil)
|
|
importReceive(imp, t, nil)
|
|
}
|
|
|
|
func TestExportReceiveImportSend(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
expDone := make(chan bool)
|
|
done := make(chan bool)
|
|
go func() {
|
|
exportReceive(exp, t, expDone)
|
|
done <- true
|
|
}()
|
|
<-expDone
|
|
importSend(imp, count, t, nil)
|
|
<-done
|
|
}
|
|
|
|
func TestClosingExportSendImportReceive(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
exportSend(exp, closeCount, t, nil)
|
|
importReceive(imp, t, nil)
|
|
}
|
|
|
|
func TestClosingImportSendExportReceive(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
expDone := make(chan bool)
|
|
done := make(chan bool)
|
|
go func() {
|
|
exportReceive(exp, t, expDone)
|
|
done <- true
|
|
}()
|
|
<-expDone
|
|
importSend(imp, closeCount, t, nil)
|
|
<-done
|
|
}
|
|
|
|
func TestErrorForIllegalChannel(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
// Now export a channel.
|
|
ch := make(chan int, 1)
|
|
err := exp.Export("aChannel", ch, Send)
|
|
if err != nil {
|
|
t.Fatal("export:", err)
|
|
}
|
|
ch <- 1234
|
|
close(ch)
|
|
// Now try to import a different channel.
|
|
ch = make(chan int)
|
|
err = imp.Import("notAChannel", ch, Recv, 1)
|
|
if err != nil {
|
|
t.Fatal("import:", err)
|
|
}
|
|
// Expect an error now. Start a timeout.
|
|
timeout := make(chan bool, 1) // buffered so closure will not hang around.
|
|
go func() {
|
|
time.Sleep(10 * time.Second) // very long, to give even really slow machines a chance.
|
|
timeout <- true
|
|
}()
|
|
select {
|
|
case err = <-imp.Errors():
|
|
if strings.Index(err.Error(), "no such channel") < 0 {
|
|
t.Error("wrong error for nonexistent channel:", err)
|
|
}
|
|
case <-timeout:
|
|
t.Error("import of nonexistent channel did not receive an error")
|
|
}
|
|
}
|
|
|
|
// Not a great test but it does at least invoke Drain.
|
|
func TestExportDrain(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
done := make(chan bool)
|
|
go func() {
|
|
exportSend(exp, closeCount, t, nil)
|
|
done <- true
|
|
}()
|
|
<-done
|
|
go importReceive(imp, t, done)
|
|
exp.Drain(0)
|
|
<-done
|
|
}
|
|
|
|
// Not a great test but it does at least invoke Drain.
|
|
func TestImportDrain(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
expDone := make(chan bool)
|
|
go exportReceive(exp, t, expDone)
|
|
<-expDone
|
|
importSend(imp, closeCount, t, nil)
|
|
imp.Drain(0)
|
|
}
|
|
|
|
// Not a great test but it does at least invoke Sync.
|
|
func TestExportSync(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
done := make(chan bool)
|
|
exportSend(exp, closeCount, t, nil)
|
|
go importReceive(imp, t, done)
|
|
exp.Sync(0)
|
|
<-done
|
|
}
|
|
|
|
// Test hanging up the send side of an export.
|
|
// TODO: test hanging up the receive side of an export.
|
|
func TestExportHangup(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
ech := make(chan int)
|
|
err := exp.Export("exportedSend", ech, Send)
|
|
if err != nil {
|
|
t.Fatal("export:", err)
|
|
}
|
|
// Prepare to receive two values. We'll actually deliver only one.
|
|
ich := make(chan int)
|
|
err = imp.ImportNValues("exportedSend", ich, Recv, 1, 2)
|
|
if err != nil {
|
|
t.Fatal("import exportedSend:", err)
|
|
}
|
|
// Send one value, receive it.
|
|
const Value = 1234
|
|
ech <- Value
|
|
v := <-ich
|
|
if v != Value {
|
|
t.Fatal("expected", Value, "got", v)
|
|
}
|
|
// Now hang up the channel. Importer should see it close.
|
|
exp.Hangup("exportedSend")
|
|
v, ok := <-ich
|
|
if ok {
|
|
t.Fatal("expected channel to be closed; got value", v)
|
|
}
|
|
}
|
|
|
|
// Test hanging up the send side of an import.
|
|
// TODO: test hanging up the receive side of an import.
|
|
func TestImportHangup(t *testing.T) {
|
|
exp, imp := pair(t)
|
|
ech := make(chan int)
|
|
err := exp.Export("exportedRecv", ech, Recv)
|
|
if err != nil {
|
|
t.Fatal("export:", err)
|
|
}
|
|
// Prepare to Send two values. We'll actually deliver only one.
|
|
ich := make(chan int)
|
|
err = imp.ImportNValues("exportedRecv", ich, Send, 1, 2)
|
|
if err != nil {
|
|
t.Fatal("import exportedRecv:", err)
|
|
}
|
|
// Send one value, receive it.
|
|
const Value = 1234
|
|
ich <- Value
|
|
v := <-ech
|
|
if v != Value {
|
|
t.Fatal("expected", Value, "got", v)
|
|
}
|
|
// Now hang up the channel. Exporter should see it close.
|
|
imp.Hangup("exportedRecv")
|
|
v, ok := <-ech
|
|
if ok {
|
|
t.Fatal("expected channel to be closed; got value", v)
|
|
}
|
|
}
|
|
|
|
// loop back exportedRecv to exportedSend,
|
|
// but receive a value from ctlch before starting the loop.
|
|
func exportLoopback(exp *Exporter, t *testing.T) {
|
|
inch := make(chan int)
|
|
if err := exp.Export("exportedRecv", inch, Recv); err != nil {
|
|
t.Fatal("exportRecv")
|
|
}
|
|
|
|
outch := make(chan int)
|
|
if err := exp.Export("exportedSend", outch, Send); err != nil {
|
|
t.Fatal("exportSend")
|
|
}
|
|
|
|
ctlch := make(chan int)
|
|
if err := exp.Export("exportedCtl", ctlch, Recv); err != nil {
|
|
t.Fatal("exportRecv")
|
|
}
|
|
|
|
go func() {
|
|
<-ctlch
|
|
for i := 0; i < count; i++ {
|
|
x := <-inch
|
|
if x != base+i {
|
|
t.Errorf("exportLoopback expected %d; got %d", i, x)
|
|
}
|
|
outch <- x
|
|
}
|
|
}()
|
|
}
|
|
|
|
// This test checks that channel operations can proceed
|
|
// even when other concurrent operations are blocked.
|
|
func TestIndependentSends(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Logf("disabled test during -short")
|
|
return
|
|
}
|
|
exp, imp := pair(t)
|
|
|
|
exportLoopback(exp, t)
|
|
|
|
importSend(imp, count, t, nil)
|
|
done := make(chan bool)
|
|
go importReceive(imp, t, done)
|
|
|
|
// wait for export side to try to deliver some values.
|
|
time.Sleep(250 * time.Millisecond)
|
|
|
|
ctlch := make(chan int)
|
|
if err := imp.ImportNValues("exportedCtl", ctlch, Send, 1, 1); err != nil {
|
|
t.Fatal("importSend:", err)
|
|
}
|
|
ctlch <- 0
|
|
|
|
<-done
|
|
}
|
|
|
|
// This test cross-connects a pair of exporter/importer pairs.
|
|
type value struct {
|
|
I int
|
|
Source string
|
|
}
|
|
|
|
func TestCrossConnect(t *testing.T) {
|
|
e1, i1 := pair(t)
|
|
e2, i2 := pair(t)
|
|
|
|
crossExport(e1, e2, t)
|
|
crossImport(i1, i2, t)
|
|
}
|
|
|
|
// Export side of cross-traffic.
|
|
func crossExport(e1, e2 *Exporter, t *testing.T) {
|
|
s := make(chan value)
|
|
err := e1.Export("exportedSend", s, Send)
|
|
if err != nil {
|
|
t.Fatal("exportSend:", err)
|
|
}
|
|
|
|
r := make(chan value)
|
|
err = e2.Export("exportedReceive", r, Recv)
|
|
if err != nil {
|
|
t.Fatal("exportReceive:", err)
|
|
}
|
|
|
|
go crossLoop("export", s, r, t)
|
|
}
|
|
|
|
// Import side of cross-traffic.
|
|
func crossImport(i1, i2 *Importer, t *testing.T) {
|
|
s := make(chan value)
|
|
err := i2.Import("exportedReceive", s, Send, 2)
|
|
if err != nil {
|
|
t.Fatal("import of exportedReceive:", err)
|
|
}
|
|
|
|
r := make(chan value)
|
|
err = i1.Import("exportedSend", r, Recv, 2)
|
|
if err != nil {
|
|
t.Fatal("import of exported Send:", err)
|
|
}
|
|
|
|
crossLoop("import", s, r, t)
|
|
}
|
|
|
|
// Cross-traffic: send and receive 'count' numbers.
|
|
func crossLoop(name string, s, r chan value, t *testing.T) {
|
|
for si, ri := 0, 0; si < count && ri < count; {
|
|
select {
|
|
case s <- value{si, name}:
|
|
si++
|
|
case v := <-r:
|
|
if v.I != ri {
|
|
t.Errorf("loop: bad value: expected %d, hello; got %+v", ri, v)
|
|
}
|
|
ri++
|
|
}
|
|
}
|
|
}
|
|
|
|
const flowCount = 100
|
|
|
|
// test flow control from exporter to importer.
|
|
func TestExportFlowControl(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Logf("disabled test during -short")
|
|
return
|
|
}
|
|
exp, imp := pair(t)
|
|
|
|
sendDone := make(chan bool, 1)
|
|
exportSend(exp, flowCount, t, sendDone)
|
|
|
|
ch := make(chan int)
|
|
err := imp.ImportNValues("exportedSend", ch, Recv, 20, -1)
|
|
if err != nil {
|
|
t.Fatal("importReceive:", err)
|
|
}
|
|
|
|
testFlow(sendDone, ch, flowCount, t)
|
|
}
|
|
|
|
// test flow control from importer to exporter.
|
|
func TestImportFlowControl(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Logf("disabled test during -short")
|
|
return
|
|
}
|
|
exp, imp := pair(t)
|
|
|
|
ch := make(chan int)
|
|
err := exp.Export("exportedRecv", ch, Recv)
|
|
if err != nil {
|
|
t.Fatal("importReceive:", err)
|
|
}
|
|
|
|
sendDone := make(chan bool, 1)
|
|
importSend(imp, flowCount, t, sendDone)
|
|
testFlow(sendDone, ch, flowCount, t)
|
|
}
|
|
|
|
func testFlow(sendDone chan bool, ch <-chan int, N int, t *testing.T) {
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
sendDone <- false
|
|
}()
|
|
|
|
if <-sendDone {
|
|
t.Fatal("send did not block")
|
|
}
|
|
n := 0
|
|
for i := range ch {
|
|
t.Log("after blocking, got value ", i)
|
|
n++
|
|
}
|
|
if n != N {
|
|
t.Fatalf("expected %d values; got %d", N, n)
|
|
}
|
|
}
|
|
|
|
func pair(t *testing.T) (*Exporter, *Importer) {
|
|
c0, c1 := net.Pipe()
|
|
exp := NewExporter()
|
|
go exp.ServeConn(c0)
|
|
imp := NewImporter(c1)
|
|
return exp, imp
|
|
}
|