From b2f177667031d7e14cef40f59172d46e7adc881f Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Wed, 6 May 2020 22:21:54 -0400 Subject: [PATCH] io/router: fix enter/leave event tests and add more This commit fixes a bug which concealed test failures when the actual event sequence was empty but the expected events were not. Additionally, this commit adds the following tests: - a test for when the active input area disappears while it is still being "hovered". - a test for when there are two nested input areas that are being hovered Signed-off-by: Chris Waldon --- io/router/pointer_test.go | 195 +++++++++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 4 deletions(-) diff --git a/io/router/pointer_test.go b/io/router/pointer_test.go index abeaf593..4b7f07ea 100644 --- a/io/router/pointer_test.go +++ b/io/router/pointer_test.go @@ -136,6 +136,8 @@ func TestPointerEnterLeave(t *testing.T) { handler2 := new(int) var ops op.Ops + var stack op.StackOp + stack.Push(&ops) // Handler 1 area: (0, 0) - (100, 100) pointer.Rect(image.Rectangle{ Max: image.Point{ @@ -144,7 +146,10 @@ func TestPointerEnterLeave(t *testing.T) { }, }).Add(&ops) pointer.InputOp{Key: handler1}.Add(&ops) + stack.Pop() + // Handler 2 area: (50, 50) - (100, 100) (areas intersect). + stack.Push(&ops) pointer.Rect(image.Rectangle{ Min: image.Point{ X: 50, @@ -156,6 +161,7 @@ func TestPointerEnterLeave(t *testing.T) { }, }).Add(&ops) pointer.InputOp{Key: handler2}.Add(&ops) + stack.Pop() var r Router r.Frame(&ops) @@ -170,7 +176,9 @@ func TestPointerEnterLeave(t *testing.T) { }, ) // First event for a handler is always a Cancel. - assertEventSequence(t, r.Events(handler1), pointer.Cancel, pointer.Enter, pointer.Move) + // Only handler2 should receive the enter/move events because it is on top + // and handler1 is not an ancestor in the hit tree. + assertEventSequence(t, r.Events(handler1), pointer.Cancel) assertEventSequence(t, r.Events(handler2), pointer.Cancel, pointer.Enter, pointer.Move) // Leave the second area by moving into the first. @@ -183,7 +191,8 @@ func TestPointerEnterLeave(t *testing.T) { }, }, ) - assertEventSequence(t, r.Events(handler1), pointer.Move) + // The cursor leaves handler2 and enters handler1. + assertEventSequence(t, r.Events(handler1), pointer.Enter, pointer.Move) assertEventSequence(t, r.Events(handler2), pointer.Leave) // Move, but stay within the same hit area. @@ -236,13 +245,192 @@ func TestPointerEnterLeave(t *testing.T) { }, }, ) - assertEventSequence(t, r.Events(handler1), pointer.Enter, pointer.Release) + assertEventSequence(t, r.Events(handler1), pointer.Enter) + // The second handler gets the release event because the press started inside it. + assertEventSequence(t, r.Events(handler2), pointer.Leave, pointer.Release) + +} + +func TestPointerEnterLeaveNested(t *testing.T) { + handler1 := new(int) + handler2 := new(int) + var ops op.Ops + + // Handler 1 area: (0, 0) - (100, 100) + pointer.Rect(image.Rectangle{ + Max: image.Point{ + X: 100, + Y: 100, + }, + }).Add(&ops) + pointer.InputOp{Key: handler1}.Add(&ops) + + // Handler 2 area: (25, 25) - (75, 75) (nested within first). + pointer.Rect(image.Rectangle{ + Min: image.Point{ + X: 25, + Y: 25, + }, + Max: image.Point{ + X: 75, + Y: 75, + }, + }).Add(&ops) + pointer.InputOp{Key: handler2}.Add(&ops) + + var r Router + r.Frame(&ops) + // Hit both handlers. + r.Add( + pointer.Event{ + Type: pointer.Move, + Position: f32.Point{ + X: 50, + Y: 50, + }, + }, + ) + // First event for a handler is always a Cancel. + // Both handlers should receive the Enter and Move events because handler2 is a child of handler1. + assertEventSequence(t, r.Events(handler1), pointer.Cancel, pointer.Enter, pointer.Move) + assertEventSequence(t, r.Events(handler2), pointer.Cancel, pointer.Enter, pointer.Move) + + // Leave the second area by moving into the first. + r.Add( + pointer.Event{ + Type: pointer.Move, + Position: f32.Point{ + X: 20, + Y: 20, + }, + }, + ) + assertEventSequence(t, r.Events(handler1), pointer.Move) assertEventSequence(t, r.Events(handler2), pointer.Leave) + + // Move, but stay within the same hit area. + r.Add( + pointer.Event{ + Type: pointer.Move, + Position: f32.Point{ + X: 10, + Y: 10, + }, + }, + ) + assertEventSequence(t, r.Events(handler1), pointer.Move) + assertEventSequence(t, r.Events(handler2)) + + // Move outside of both inputs. + r.Add( + pointer.Event{ + Type: pointer.Move, + Position: f32.Point{ + X: 200, + Y: 200, + }, + }, + ) + assertEventSequence(t, r.Events(handler1), pointer.Leave) + assertEventSequence(t, r.Events(handler2)) + + // Check that a Press event generates Enter Events. + r.Add( + pointer.Event{ + Type: pointer.Press, + Position: f32.Point{ + X: 50, + Y: 50, + }, + }, + ) + assertEventSequence(t, r.Events(handler1), pointer.Enter, pointer.Press) + assertEventSequence(t, r.Events(handler2), pointer.Enter, pointer.Press) + + // Check that a Release event generates Enter/Leave Events. + r.Add( + pointer.Event{ + Type: pointer.Release, + Position: f32.Point{ + // Move out of the second hit area and into the first. + X: 20, + Y: 20, + }, + }, + ) + assertEventSequence(t, r.Events(handler1), pointer.Release) + assertEventSequence(t, r.Events(handler2), pointer.Leave, pointer.Release) +} + +func TestPointerActiveInputDisappears(t *testing.T) { + handler1 := new(int) + // Save this logic so we can redo it later. + renderHandler1 := func(ops *op.Ops) { + var stack op.StackOp + stack.Push(ops) + // Handler 1 area: (0, 0) - (100, 100) + pointer.Rect(image.Rectangle{ + Max: image.Point{ + X: 100, + Y: 100, + }, + }).Add(ops) + pointer.InputOp{Key: handler1}.Add(ops) + stack.Pop() + } + + var ops op.Ops + var r Router + + renderHandler1(&ops) + + // Draw handler. + ops.Reset() + renderHandler1(&ops) + r.Frame(&ops) + r.Add( + pointer.Event{ + Type: pointer.Move, + Position: f32.Point{ + X: 25, + Y: 25, + }, + }, + ) + assertEventSequence(t, r.Events(handler1), pointer.Cancel, pointer.Enter, pointer.Move) + + // Re-render with handler missing. + ops.Reset() + r.Frame(&ops) + r.Add( + pointer.Event{ + Type: pointer.Move, + Position: f32.Point{ + X: 25, + Y: 25, + }, + }, + ) + assertEventSequence(t, r.Events(handler1), pointer.Cancel) +} + +// toTypes converts a sequence of event.Event to their pointer.Types. It assumes +// that all input events are of underlying type pointer.Event, and thus will +// panic if some are not. +func toTypes(events []event.Event) []pointer.Type { + out := make([]pointer.Type, len(events)) + for i, event := range events { + out[i] = event.(pointer.Event).Type + } + return out } // assertEventSequence ensures that the provided actualEvents match the expected event types // in the provided order func assertEventSequence(t *testing.T, actualEvents []event.Event, expected ...pointer.Type) { + if len(actualEvents) != len(expected) { + t.Errorf("expected %v events, got %v", expected, toTypes(actualEvents)) + } for i, event := range actualEvents { pointerEvent, ok := event.(pointer.Event) if !ok { @@ -250,7 +438,6 @@ func assertEventSequence(t *testing.T, actualEvents []event.Event, expected ...p continue } if len(expected) <= i { - t.Errorf("actualEvents is longer than expected, has len %d, expected len %d", len(actualEvents), len(expected)) continue } if pointerEvent.Type != expected[i] {