diff options
| -rw-r--r-- | mux_test.go | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/mux_test.go b/mux_test.go new file mode 100644 index 0000000..9ffca83 --- /dev/null +++ b/mux_test.go @@ -0,0 +1,225 @@ +package gui + +import ( + "image" + "image/draw" + "image/jpeg" + "os" + "testing" + "time" + + "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) + root := newDummyEnv(rect) + defer func() { + root.Kill() <- true + <-root.Dead() + }() + mux := NewMux(root) + envs := []Env{mux.MakeEnv(), mux.MakeEnv(), mux.MakeEnv()} + + events := []Event{Resize{rect}, dummyEvent{"fooEvent"}, dummyEvent{"barEvent"}, dummyEvent{"bazEvent"}} + go func() { + for _, event := range events[1:] { // skip resize—it's sent automatically by the root Env + root.eventsIn <- event + } + }() + + for _, env := range envs { + for _, expect := range events { + var event Event + timer := time.NewTimer(timeout) + select { + case event = <-env.Events(): + case <-timer.C: + t.Errorf("no event received after %v", timeout) + } + if event != expect { + t.Errorf("received %v; wanted %v", event, expect) + } + } + } +} + +// Send draw function from Envs to the Mux. +func TestMuxDraw(t *testing.T) { + rect := image.Rect(120, 340, 560, 780) + root := newDummyEnv(rect) + defer func() { + root.Kill() <- true + <-root.Dead() + }() + mux := NewMux(root) + envs := []Env{mux.MakeEnv(), mux.MakeEnv(), mux.MakeEnv()} + drawFunc := func(r image.Rectangle) func(draw.Image) image.Rectangle { + return func(drw draw.Image) image.Rectangle { + cr := image.Rect(0, 0, r.Dx(), r.Dy()) + canvas := image.NewRGBA(cr) + draw.Draw(canvas, cr, image.White, image.ZP, draw.Src) + dc := gg.NewContextForRGBA(canvas) + dc.DrawEllipse(float64(r.Dx()/2), float64(r.Dy()/2), + float64(r.Dx()/2), float64(r.Dy()/2)) + dc.SetRGB(255, 120, 0) + dc.Fill() + draw.Draw(drw, r, canvas, image.ZP, draw.Src) + return r + } + } + + for _, env := range envs { + // Receive Resize event. + var event Event + timer := time.NewTimer(timeout) + select { + case event = <-env.Events(): + case <-timer.C: + t.Errorf("no event received after %v", timeout) + } + resize, ok := event.(Resize) + if !ok { + t.Errorf("got %T; wanted Resize", event) + } + + env.Draw() <- drawFunc(resize.Rectangle) + + // Receive draw function on root env. + var d func(draw.Image) image.Rectangle + timer.Reset(timeout) + select { + case d = <-root.drawOut: + case <-timer.C: + t.Errorf("no draw function received after %v", timeout) + } + + // Draw and compare images. + img := image.NewRGBA(resize.Rectangle) + r := d(img) + expectImg := image.NewRGBA(rect) + expectR := drawFunc(rect)(expectImg) + if r != expectR { + t.Errorf("draw function returned %v; wanted %v", r, expectR) + } + if !cmpImg(img, expectImg) { + expectOut, gotOut := "expect.jpg", "got.jpg" + t.Errorf("draw function did not draw correct image. Writing results to '%v' and '%v'...", expectOut, gotOut) + if err := writeImg(expectImg, expectOut); err != nil { + t.Error(err) + } + if err := writeImg(img, gotOut); err != nil { + t.Error(err) + } + } + } +} + +func cmpImg(a, b image.Image) bool { + if a.Bounds() != b.Bounds() { + return false + } + bounds := a.Bounds() + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + if a.At(x, y) != b.At(x, y) { + return false + } + } + } + return true +} + +func writeImg(img image.Image, fname string) error { + f, err := os.Create(fname) + defer f.Close() + if err != nil { + return err + } + return jpeg.Encode(f, img, nil) +} + +type dummyEnv struct { + eventsIn chan<- Event + eventsOut <-chan Event + + drawIn chan<- func(draw.Image) image.Rectangle + drawOut <-chan func(draw.Image) image.Rectangle + + kill chan<- bool + dead <-chan bool + + attachChan chan<- attachMsg +} + +func newDummyEnv(size image.Rectangle) dummyEnv { + eventsOut, eventsIn := MakeEventsChan() + drawIn := make(chan func(draw.Image) image.Rectangle) + drawOut := make(chan func(draw.Image) image.Rectangle) + kill := make(chan bool) + dead := make(chan bool) + + attached := newAttachHandler() + + go func() { + defer func() { + dead <- true + close(dead) + }() + defer close(kill) + defer close(drawOut) + defer close(drawIn) + defer close(eventsIn) + defer func() { + attached.kill <- true + <-attached.dead + }() + defer func() { + go drain(drawIn) + }() + + for { + select { + case d := <-drawIn: + drawOut <- d + case <-kill: + return + } + } + }() + + eventsIn <- Resize{size} + + return dummyEnv{eventsIn, eventsOut, drawIn, drawOut, kill, dead, attached.attach} +} + +func (de dummyEnv) Events() <-chan Event { + return de.eventsOut +} + +func (de dummyEnv) Draw() chan<- func(draw.Image) image.Rectangle { + return de.drawIn +} + +func (de dummyEnv) Kill() chan<- bool { + return de.kill +} + +func (de dummyEnv) Dead() <-chan bool { + return de.dead +} + +func (de dummyEnv) attach() chan<- attachMsg { + return de.attachChan +} + +type dummyEvent struct { + s string +} + +func (e dummyEvent) String() string { + return e.s +} |