aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2024-08-22 21:12:12 -0400
committerSam Anthony <sam@samanthony.xyz>2024-08-22 21:12:12 -0400
commitd8743bbc53c8db43151ef89d21264b531bea3d4c (patch)
treeba941b7aa904bbde985d3a76ca90028f4bf8a475
parent59cf78dbab8549761207b706f9856898cb5cd9c2 (diff)
downloadgui-d8743bbc53c8db43151ef89d21264b531bea3d4c.zip
test attachHandler
-rw-r--r--gui_test.go27
-rw-r--r--kill.go10
-rw-r--r--kill_test.go112
-rw-r--r--mux.go2
-rw-r--r--mux_test.go4
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
+ }
+}
diff --git a/kill.go b/kill.go
index 81ab8ad..d6c2b3b 100644
--- a/kill.go
+++ b/kill.go
@@ -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
+}
diff --git a/mux.go b/mux.go
index 26f220a..3f1d96a 100644
--- a/mux.go
+++ b/mux.go
@@ -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 {