// 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 ( "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) { 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) }() } 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 := <-ch if closed(ch) { 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) { ch := make(chan int) err := imp.ImportNValues("exportedRecv", ch, Send, count) if err != nil { t.Fatal("importSend:", err) } go func() { for i := 0; i < n; i++ { ch <- base+i } close(ch) }() } func importReceive(imp *Importer, t *testing.T, done chan bool) { ch := make(chan int) err := imp.ImportNValues("exportedSend", ch, Recv, count) if err != nil { t.Fatal("importReceive:", err) } for i := 0; i < count; i++ { v := <-ch if closed(ch) { if i != closeCount { t.Errorf("importReceive expected close at %d; got one at %d", closeCount, i) } break } if v != 23+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, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } exportSend(exp, count, t) importReceive(imp, t, nil) } func TestExportReceiveImportSend(t *testing.T) { exp, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } expDone := make(chan bool) done := make(chan bool) go func() { exportReceive(exp, t, expDone) done <- true }() <-expDone importSend(imp, count, t) <-done } func TestClosingExportSendImportReceive(t *testing.T) { exp, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } exportSend(exp, closeCount, t) importReceive(imp, t, nil) } func TestClosingImportSendExportReceive(t *testing.T) { exp, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } expDone := make(chan bool) done := make(chan bool) go func() { exportReceive(exp, t, expDone) done <- true }() <-expDone importSend(imp, closeCount, t) <-done } func TestErrorForIllegalChannel(t *testing.T) { exp, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } // 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) 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(10e9) // very long, to give even really slow machines a chance. timeout <- true }() select { case err = <-imp.Errors(): if strings.Index(err.String(), "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, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } done := make(chan bool) go func() { exportSend(exp, closeCount, t) done <- true }() <-done go importReceive(imp, t, done) exp.Drain(0) <-done } // Not a great test but it does at least invoke Sync. func TestExportSync(t *testing.T) { exp, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } done := make(chan bool) exportSend(exp, closeCount, t) 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, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } 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, 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 = <-ich if !closed(ich) { 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, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } imp, err := NewImporter("tcp", exp.Addr().String()) if err != nil { t.Fatal("new importer:", err) } 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, 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 = <-ech if !closed(ech) { t.Fatal("expected channel to be closed; got value", v) } } // This test cross-connects a pair of exporter/importer pairs. type value struct { i int source string } func TestCrossConnect(t *testing.T) { e1, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } i1, err := NewImporter("tcp", e1.Addr().String()) if err != nil { t.Fatal("new importer:", err) } e2, err := NewExporter("tcp", "127.0.0.1:0") if err != nil { t.Fatal("new exporter:", err) } i2, err := NewImporter("tcp", e2.Addr().String()) if err != nil { t.Fatal("new importer:", err) } go 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) } 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) if err != nil { t.Fatal("import of exportedReceive:", err) } r := make(chan value) err = i1.Import("exportedSend", r, Recv) 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++ } } }