diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2024-08-22 21:12:12 -0400 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2024-08-22 21:12:12 -0400 |
| commit | d8743bbc53c8db43151ef89d21264b531bea3d4c (patch) | |
| tree | ba941b7aa904bbde985d3a76ca90028f4bf8a475 | |
| parent | 59cf78dbab8549761207b706f9856898cb5cd9c2 (diff) | |
| download | gui-d8743bbc53c8db43151ef89d21264b531bea3d4c.zip | |
test attachHandler
| -rw-r--r-- | gui_test.go | 27 | ||||
| -rw-r--r-- | kill.go | 10 | ||||
| -rw-r--r-- | kill_test.go | 112 | ||||
| -rw-r--r-- | mux.go | 2 | ||||
| -rw-r--r-- | mux_test.go | 4 |
5 files changed, 148 insertions, 7 deletions
diff --git a/gui_test.go b/gui_test.go new file mode 100644 index 0000000..584544a --- /dev/null +++ b/gui_test.go @@ -0,0 +1,27 @@ +package gui + +import "time" + +const timeout = 1 * time.Second + +// trySend returns true if v can be sent to c within timeout, or false otherwise. +func trySend[T any](c chan<- T, v T, timeout time.Duration) bool { + timer := time.NewTimer(timeout) + select { + case c <- v: + return true + case <-timer.C: + return false + } +} + +// tryRecv returns the value received from c, or false if no value is received within timeout. +func tryRecv[T any](c <-chan T, timeout time.Duration) (*T, bool) { + timer := time.NewTimer(timeout) + select { + case v := <-c: + return &v, true + case <-timer.C: + return nil, false + } +} @@ -28,9 +28,9 @@ type attachable interface { // If attachHandler is killed while a victim is attached, it kills the victim. // When killed, the victim must detach itself before dying. type attachHandler struct { - attach chan<- attachable - kill chan<- bool - dead <-chan bool + attachChan chan<- attachable + kill chan<- bool + dead <-chan bool } func newAttachHandler() attachHandler { @@ -65,3 +65,7 @@ func newAttachHandler() attachHandler { return attachHandler{attach, kill, dead} } + +func (ah attachHandler) attach() chan<- attachable { + return ah.attachChan +} diff --git a/kill_test.go b/kill_test.go new file mode 100644 index 0000000..d11e54c --- /dev/null +++ b/kill_test.go @@ -0,0 +1,112 @@ +package gui + +import ( + "fmt" + "testing" +) + +// Kill the attachHandler with no victim attached. +func TestAttachHandlerKill(t *testing.T) { + handler := newAttachHandler() + if !trySend(handler.kill, true, timeout) { + t.Errorf("kill attachHandler timed out after %v", timeout) + } + if _, ok := tryRecv(handler.dead, timeout); !ok { + t.Errorf("no dead signal from attachHandler after %v", timeout) + } +} + +// Kill the attachHandler with a victim attached. +func TestAttachHandlerAttachKill(t *testing.T) { + handler := newAttachHandler() + victim, err := newDummyAttachable(handler) + if err != nil { + t.Error(err) + } + + // Kill attachHandler. + if !trySend(handler.kill, true, timeout) { + t.Errorf("failed to kill attachHandler after %v", timeout) + } + if _, ok := tryRecv(handler.dead, timeout); !ok { + t.Errorf("attachHandler not dead after %v", timeout) + } + + // victim.Dead() should now be closed. + if _, notClosed := <-victim.Dead(); notClosed { + t.Errorf("victim not dead after killing attachHandler") + } +} + +// Detach the victim and attach another in its place. +func TestAttachHandlerReattach(t *testing.T) { + handler := newAttachHandler() + + // Attach first victim. + victim1, err := newDummyAttachable(handler) + if err != nil { + t.Error(err) + } + + // Try to attach second victim while first still attached—should fail. + if _, err := newDummyAttachable(handler); err == nil { + t.Errorf("attachHandler accepted another victim while the first was still attached.") + } + + // Detach first victim. + if !trySend(victim1.Kill(), true, timeout) { + t.Errorf("failed to kill first victim after %v", timeout) + } + if _, ok := tryRecv(victim1.Dead(), timeout); !ok { + t.Errorf("first victim failed to die after %v", timeout) + } + + // Attach second victim. + if _, err := newDummyAttachable(handler); err != nil { + t.Error(err) + } + + handler.kill <- true + <-handler.dead +} + +type dummyAttachable struct { + kill chan<- bool + dead <-chan bool + detachChan <-chan bool +} + +// newDummyAttachable returns a dummyAttachable that is attached to parent, +// or error if the parent does not accept the attach. +func newDummyAttachable(parent killer) (attachable, error) { + kill := make(chan bool) + dead := make(chan bool) + detachChan := make(chan bool) + + go func() { + <-kill + close(kill) + detachChan <- true + close(detachChan) + dead <- true + close(dead) + }() + + da := dummyAttachable{kill, dead, detachChan} + if !trySend(parent.attach(), attachable(da), timeout) { + return da, fmt.Errorf("failed to attach after %v", timeout) + } + return da, nil +} + +func (da dummyAttachable) Kill() chan<- bool { + return da.kill +} + +func (da dummyAttachable) Dead() <-chan bool { + return da.dead +} + +func (da dummyAttachable) detach() <-chan bool { + return da.detachChan +} @@ -129,7 +129,7 @@ func (mux Mux) MakeEnv() Env { eventsIn: eventsIn, eventsOut: eventsOut, draw: drawChan, - attachChan: attached.attach, + attachChan: attached.attach(), kill: kill, dead: dead, detachFromMux: detachFromMux, diff --git a/mux_test.go b/mux_test.go index 95fe4ae..b159e99 100644 --- a/mux_test.go +++ b/mux_test.go @@ -11,8 +11,6 @@ import ( "github.com/fogleman/gg" ) -const timeout = 1 * time.Second - // Send Events from the Mux to the Envs.. func TestMuxEvent(t *testing.T) { rect := image.Rect(12, 34, 56, 78) @@ -193,7 +191,7 @@ func newDummyEnv(size image.Rectangle) dummyEnv { eventsIn <- Resize{size} - return dummyEnv{eventsIn, eventsOut, drawIn, drawOut, kill, dead, attached.attach} + return dummyEnv{eventsIn, eventsOut, drawIn, drawOut, kill, dead, attached.attach()} } func (de dummyEnv) Events() <-chan Event { |