blob: 9c1975b007139ecd9ce78bc43322ef0dc6a913b6 [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/atomic"
"syscall/zx"
. "syscall/zx/dispatch"
"testing"
"time"
)
func dispatcherTest(t *testing.T, name string, f func(d *Dispatcher) error) {
t.Run(name, func(t *testing.T) {
d, err := NewDispatcher()
if err != nil {
t.Fatal("creating dispatcher: ", err)
}
go d.Serve()
if err := f(d); err != nil {
t.Fatal("executing test: ", err)
}
d.Close()
})
}
func TestWait(t *testing.T) {
t.Parallel()
dispatcherTest(t, "WaitOnce", func(d *Dispatcher) error {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return err
}
defer h0.Close()
done := make(chan struct{}, 1)
defer close(done)
handler := func(d *Dispatcher, s zx.Status, p *zx.PacketSignal) WaitResult {
done <- struct{}{}
return WaitFinished
}
_, err = d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, 0, handler)
if err != nil {
return err
}
h1.Close()
select {
case <-done:
return nil
case <-time.After(5 * time.Second):
t.Fatal("timed out, handler never executed")
}
return nil
})
dispatcherTest(t, "WaitMultipleTimes", func(d *Dispatcher) error {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return err
}
defer h0.Close()
defer h1.Close()
done := make(chan struct{}, 1)
defer close(done)
handler := func(d *Dispatcher, s zx.Status, p *zx.PacketSignal) 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 WaitAgain
}
done <- struct{}{}
return WaitFinished
}
_, err = d.BeginWait(zx.Handle(h0), zx.SignalChannelReadable, 0, handler)
if err != nil {
return err
}
for i := 0; i < 5; i++ {
if err := h1.Write([]byte{1}, nil, 0); err != nil {
return err
}
}
if err := h1.Write([]byte{0}, nil, 0); err != nil {
return err
}
select {
case <-done:
return nil
case <-time.After(5 * time.Second):
t.Fatal("timed out, handler never executed")
}
return nil
})
dispatcherTest(t, "CancelWaitHandleShutdown", func(d *Dispatcher) error {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return err
}
defer h0.Close()
defer h1.Close()
var i uint32
handler := func(d *Dispatcher, s zx.Status, p *zx.PacketSignal) WaitResult {
atomic.AddUint32(&i, 1)
return WaitFinished
}
id, err := d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, HandleShutdown, handler)
if err != nil {
return err
}
if err := d.CancelWait(id); err != nil {
return err
}
d.Close()
if i != 0 {
t.Fatal("handler ran unexpectedly")
}
return nil
})
dispatcherTest(t, "Shutdown", func(d *Dispatcher) error {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return err
}
defer h0.Close()
defer h1.Close()
handler := func(d *Dispatcher, s zx.Status, p *zx.PacketSignal) WaitResult {
return WaitFinished
}
d.Close()
_, err = d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, 0, handler)
if err == nil {
t.Fatal("unexpected success for BeginWait on shut down dispatcher")
}
return nil
})
dispatcherTest(t, "HandleShutdown", func(d *Dispatcher) error {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return err
}
defer h0.Close()
defer h1.Close()
done := make(chan struct{}, 1)
defer close(done)
handler := func(d *Dispatcher, s zx.Status, p *zx.PacketSignal) WaitResult {
done <- struct{}{}
return WaitFinished
}
_, err = d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, HandleShutdown, handler)
if err != nil {
return err
}
d.Close()
select {
case <-done:
return nil
case <-time.After(5 * time.Second):
t.Fatal("timed out, handler never executed")
}
return nil
})
dispatcherTest(t, "MultipleGoroutines", func(d *Dispatcher) error {
// Spin up 5 goroutines all serving the dispatcher.
for i := 0; i < 5; i++ {
go d.Serve()
}
done := make(chan struct{}, 20)
defer close(done)
var ends []zx.Channel
for i := 0; i < 20; i++ {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return err
}
defer h0.Close()
handler := func(d *Dispatcher, s zx.Status, p *zx.PacketSignal) WaitResult {
done <- struct{}{}
return WaitFinished
}
_, err = d.BeginWait(zx.Handle(h0), zx.SignalChannelPeerClosed, 0, handler)
if err != nil {
return err
}
ends = append(ends, h1)
}
for _, e := range ends {
e.Close()
}
for i := 0; i < 20; i++ {
select {
case <-done:
continue
case <-time.After(3 * time.Second):
t.Fatal("timed out, some handler never executed")
}
}
return nil
})
}