blob: 78af34649fa50abfa874123c2cc6c81dc71b3560 [file] [log] [blame]
// Copyright 2018 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.
// +build fuchsia
package dispatch_test
import (
"sync"
"syscall/zx"
"syscall/zx/dispatch"
"testing"
)
func dispatcherTest(t *testing.T, name string, f func(t *testing.T, d *dispatch.Dispatcher)) {
t.Run(name, func(t *testing.T) {
d, err := dispatch.NewDispatcher()
if err != nil {
t.Fatal("creating dispatcher: ", err)
}
ch := make(chan struct{})
go func() {
defer close(ch)
d.Serve()
}()
defer func() {
d.Close()
<-ch
}()
f(t, d)
})
}
func TestCloseDispatcher(t *testing.T) {
d, err := dispatch.NewDispatcher()
if err != nil {
t.Fatal("creating dispatcher: ", err)
}
var wg sync.WaitGroup
const serveCalls = 5
for i := 0; i < serveCalls; i++ {
wg.Add(1)
go func() {
d.Serve()
wg.Done()
}()
}
d.WaitForNServeLoops(serveCalls)
d.Close()
wg.Wait()
}
func TestShutdownOne(t *testing.T) {
d, err := dispatch.NewDispatcher()
if err != nil {
t.Fatal("creating dispatcher: ", err)
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
d.Serve()
wg.Done()
}()
d.WaitForNServeLoops(1)
if err := d.ShutdownOne(); err != nil {
t.Fatalf("ShutdownOne(): %s", err)
}
wg.Wait()
}
func TestWait(t *testing.T) {
t.Parallel()
dispatcherTest(t, "WaitOnce", func(t *testing.T, d *dispatch.Dispatcher) {
h0, h1, err := zx.NewChannel(0)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = h0.Close()
_ = h1.Close()
}()
done := make(chan struct{})
handler := func(error, *zx.PacketSignal) dispatch.WaitResult {
close(done)
return dispatch.WaitFinished
}
if _, err := d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, handler); err != nil {
t.Fatal(err)
}
if err := h1.Close(); err != nil {
t.Fatal(err)
}
<-done
})
dispatcherTest(t, "WaitMultipleTimes", func(t *testing.T, d *dispatch.Dispatcher) {
h0, h1, err := zx.NewChannel(0)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = h0.Close()
_ = h1.Close()
}()
done := make(chan struct{})
handler := func(error, *zx.PacketSignal) dispatch.WaitResult {
var b [zx.ChannelMaxMessageBytes]byte
var h [zx.ChannelMaxMessageHandles]zx.Handle
bn, hn, err := h0.Read(b[:], h[:], 0)
if err != nil {
t.Fatal("error reading: ", err)
}
if bn != 1 || hn != 0 {
t.Fatalf("unexpected read %d bytes %d handles", bn, hn)
}
if b[0] == 1 {
return dispatch.WaitAgain
}
close(done)
return dispatch.WaitFinished
}
if _, err := d.BeginWait(zx.Handle(h0), zx.SignalChannelReadable, handler); err != nil {
t.Fatal(err)
}
for i := 0; i < 5; i++ {
if err := h1.Write([]byte{1}, nil, 0); err != nil {
t.Fatal(err)
}
}
if err := h1.Write([]byte{0}, nil, 0); err != nil {
t.Fatal(err)
}
<-done
})
dispatcherTest(t, "CancelWait", func(t *testing.T, d *dispatch.Dispatcher) {
h0, h1, err := zx.NewChannel(0)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = h0.Close()
_ = h1.Close()
}()
handler := func(err error, sigs *zx.PacketSignal) dispatch.WaitResult {
t.Fatalf("unexpected call to handler err=%v sigs=%+v", err, sigs)
return dispatch.WaitFinished
}
id, err := d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, handler)
if err != nil {
t.Fatal(err)
}
if err := d.CancelWait(id); err != nil {
t.Fatal(err)
}
if err := h1.Close(); err != nil {
t.Fatal(err)
}
})
dispatcherTest(t, "Shutdown", func(t *testing.T, d *dispatch.Dispatcher) {
h0, h1, err := zx.NewChannel(0)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = h0.Close()
_ = h1.Close()
}()
d.Close()
if _, err := d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, nil); err == nil {
t.Fatal("unexpected success for BeginWait on shut down dispatcher")
}
})
dispatcherTest(t, "MultipleGoroutines", func(t *testing.T, d *dispatch.Dispatcher) {
// Spin up 5 goroutines all serving the dispatcher.
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
d.Serve()
}()
}
defer func() {
d.Close()
wg.Wait()
}()
done := make(chan struct{}, 20)
defer close(done)
var ends []zx.Channel
for i := 0; i < cap(done); i++ {
h0, h1, err := zx.NewChannel(0)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = h0.Close()
_ = h1.Close()
}()
handler := func(error, *zx.PacketSignal) dispatch.WaitResult {
done <- struct{}{}
return dispatch.WaitFinished
}
if _, err := d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, handler); err != nil {
t.Fatal(err)
}
ends = append(ends, h1)
}
for _, e := range ends {
if err := e.Close(); err != nil {
t.Fatal(err)
}
}
for i := 0; i < 20; i++ {
<-done
}
})
}