using System; using Azimuth; using Blacklight.Core.Cameras; namespace Blacklight.Core.UnitTests.Cameras { public class InputRouterTests : UnitTestSharp.TestFixture { public class RecordingConsumer : IInputConsumer { public bool Consumes; public int PointerDownCount = 0; public int PointerMoveCount = 0; public int PointerUpCount = 0; public int WheelCount = 0; public int KeyDownCount = 0; public int KeyUpCount = 0; public PointerInput LastPointerInput; public int LastWheelTicks = 0; public Vector LastWheelPixelPos = Vector.Zero; public Key LastKey; public RecordingConsumer(bool consumes) { Consumes = consumes; } public bool PointerDown(PointerInput input) { PointerDownCount++; LastPointerInput = input; return Consumes; } public bool PointerMove(PointerInput input) { PointerMoveCount++; LastPointerInput = input; return Consumes; } public bool PointerUp(PointerInput input) { PointerUpCount++; LastPointerInput = input; return Consumes; } public bool Wheel(int ticks, Vector pixelPos) { WheelCount++; LastWheelTicks = ticks; LastWheelPixelPos = pixelPos; return Consumes; } public bool KeyDown(Key key) { KeyDownCount++; LastKey = key; return Consumes; } public bool KeyUp(Key key) { KeyUpCount++; LastKey = key; return Consumes; } } public class MutatingConsumer : IInputConsumer { private readonly InputRouter router; private readonly Action mutate; public MutatingConsumer(InputRouter router, Action mutate) { this.router = router; this.mutate = mutate; } private bool Mutate() { mutate(router, this); return false; } public bool PointerDown(PointerInput input) { return Mutate(); } public bool PointerMove(PointerInput input) { return Mutate(); } public bool PointerUp(PointerInput input) { return Mutate(); } public bool Wheel(int ticks, Vector pixelPos) { return Mutate(); } public bool KeyDown(Key key) { return Mutate(); } public bool KeyUp(Key key) { return Mutate(); } } public static readonly Action UnbindItself = (router, self) => router.Remove(self); public static readonly Action UnbindEveryone = (router, self) => router.Clear(); public static readonly Action PromoteTheLowestToHighest = (router, self) => { IInputConsumer lowest = router.Consumers[0]; router.Remove(lowest); router.AddHighestPriority(lowest); }; public static (InputRouter, RecordingConsumer, RecordingConsumer) MakeRouter(bool topConsumes) { var bottom = new RecordingConsumer(true); var top = new RecordingConsumer(topConsumes); var router = new InputRouter(); router.AddLowestPriority(bottom); router.AddHighestPriority(top); return (router, top, bottom); } /// /// Two declining consumers with a third above them that mangles the order while handling the sample. /// public static (InputRouter, RecordingConsumer, RecordingConsumer) MakeMutatingOnTop( Action mutation) { var bottom = new RecordingConsumer(false); var middle = new RecordingConsumer(false); var router = new InputRouter(); router.AddLowestPriority(bottom); router.AddHighestPriority(middle); router.AddHighestPriority(new MutatingConsumer(router, mutation)); return (router, middle, bottom); } public static PointerInput SomeInput => new PointerInput { PixelPos = new Vector(12, 34), Buttons = PointerButtons.Right }; public class CtorTests : UnitTestSharp.TestFixture { public void StartsWithNoConsumers() { CheckEqual(0, new InputRouter().Consumers.Count); } } public class AddHighestPriorityTests : UnitTestSharp.TestFixture { public void OnAnEmptyRouter_MakesItTheOnlyConsumer() { var consumer = new RecordingConsumer(false); var router = new InputRouter(); router.AddHighestPriority(consumer); CheckEqual(1, router.Consumers.Count); CheckEqual(consumer, router.Consumers[0]); } public void PutsTheConsumerAheadOfTheEarlierOnes() { var first = new RecordingConsumer(false); var second = new RecordingConsumer(false); var router = new InputRouter(); router.AddHighestPriority(first); router.AddHighestPriority(second); CheckEqual(2, router.Consumers.Count); CheckEqual(first, router.Consumers[0]); CheckEqual(second, router.Consumers[1]); } public void IsOfferedTheSampleBeforeTheEarlierOnes() { var earlier = new RecordingConsumer(false); var added = new RecordingConsumer(true); var router = new InputRouter(); router.AddHighestPriority(earlier); router.AddHighestPriority(added); Check(router.PointerDown(SomeInput)); CheckEqual(0, earlier.PointerDownCount); } } public class AddLowestPriorityTests : UnitTestSharp.TestFixture { public void OnAnEmptyRouter_MakesItTheOnlyConsumer() { var consumer = new RecordingConsumer(false); var router = new InputRouter(); router.AddLowestPriority(consumer); CheckEqual(1, router.Consumers.Count); CheckEqual(consumer, router.Consumers[0]); } public void PutsTheConsumerBehindTheEarlierOnes() { var first = new RecordingConsumer(false); var second = new RecordingConsumer(false); var router = new InputRouter(); router.AddLowestPriority(first); router.AddLowestPriority(second); CheckEqual(2, router.Consumers.Count); CheckEqual(second, router.Consumers[0]); CheckEqual(first, router.Consumers[1]); } public void IsOnlyOfferedWhatTheEarlierOnesDeclined() { var earlier = new RecordingConsumer(true); var added = new RecordingConsumer(true); var router = new InputRouter(); router.AddHighestPriority(earlier); router.AddLowestPriority(added); Check(router.PointerDown(SomeInput)); CheckEqual(0, added.PointerDownCount); } } public class RemoveTests : UnitTestSharp.TestFixture { public void ReturnsTrueWhenTheConsumerWasBound() { var consumer = new RecordingConsumer(false); var router = new InputRouter(); router.AddHighestPriority(consumer); Check(router.Remove(consumer)); CheckEqual(0, router.Consumers.Count); } public void ReturnsFalseWhenTheConsumerWasNeverBound() { var router = new InputRouter(); router.AddHighestPriority(new RecordingConsumer(false)); CheckFalse(router.Remove(new RecordingConsumer(false))); CheckEqual(1, router.Consumers.Count); } public void FromTheMiddle_LeavesTheRestInOrder() { var bottom = new RecordingConsumer(false); var middle = new RecordingConsumer(false); var top = new RecordingConsumer(false); var router = new InputRouter(); router.AddHighestPriority(bottom); router.AddHighestPriority(middle); router.AddHighestPriority(top); Check(router.Remove(middle)); CheckEqual(2, router.Consumers.Count); CheckEqual(bottom, router.Consumers[0]); CheckEqual(top, router.Consumers[1]); } public void FromTheBottom_LeavesTheRestInOrder() { var bottom = new RecordingConsumer(false); var top = new RecordingConsumer(false); var router = new InputRouter(); router.AddHighestPriority(bottom); router.AddHighestPriority(top); Check(router.Remove(bottom)); CheckEqual(1, router.Consumers.Count); CheckEqual(top, router.Consumers[0]); } public void FromTheTop_LeavesTheRestInOrder() { var bottom = new RecordingConsumer(false); var top = new RecordingConsumer(false); var router = new InputRouter(); router.AddHighestPriority(bottom); router.AddHighestPriority(top); Check(router.Remove(top)); CheckEqual(1, router.Consumers.Count); CheckEqual(bottom, router.Consumers[0]); } public void FromAnEmptyRouter_ReturnsFalse() { CheckFalse(new InputRouter().Remove(new RecordingConsumer(false))); } } public class ClearTests : UnitTestSharp.TestFixture { public void LeavesNoConsumers() { var (router, _, _) = MakeRouter(topConsumes: true); router.Clear(); CheckEqual(0, router.Consumers.Count); } public void OnAnEmptyRouterLeavesItEmpty() { var router = new InputRouter(); router.Clear(); CheckEqual(0, router.Consumers.Count); } } public class PointerDownTests : UnitTestSharp.TestFixture { public void NoConsumers_ReturnsFalse() { CheckFalse(new InputRouter().PointerDown(SomeInput)); } public void HighestPriorityTakingIt_StopsBeforeTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: true); Check(router.PointerDown(SomeInput)); CheckEqual(0, bottom.PointerDownCount); } public void HighestPriorityDecliningIt_FallsThroughToTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: false); Check(router.PointerDown(SomeInput)); CheckEqual(1, bottom.PointerDownCount); } public void EveryConsumerDecliningIt_ReturnsFalse() { var router = new InputRouter(); router.AddHighestPriority(new RecordingConsumer(false)); router.AddHighestPriority(new RecordingConsumer(false)); CheckFalse(router.PointerDown(SomeInput)); } public void PassesTheInputThrough() { var (router, top, _) = MakeRouter(topConsumes: true); router.PointerDown(SomeInput); CheckEqual(new Vector(12, 34), top.LastPointerInput.PixelPos); CheckEqual(PointerButtons.Right, top.LastPointerInput.Buttons); } public void ConsumerUnbindingItselfMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(UnbindItself); CheckFalse(router.PointerDown(SomeInput)); CheckEqual(1, middle.PointerDownCount); CheckEqual(1, bottom.PointerDownCount); } public void ConsumerUnbindingEveryoneMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(UnbindEveryone); CheckFalse(router.PointerDown(SomeInput)); CheckEqual(1, middle.PointerDownCount); CheckEqual(1, bottom.PointerDownCount); } public void ConsumerReorderingMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(PromoteTheLowestToHighest); CheckFalse(router.PointerDown(SomeInput)); CheckEqual(1, middle.PointerDownCount); CheckEqual(1, bottom.PointerDownCount); } } public class PointerMoveTests : UnitTestSharp.TestFixture { public void NoConsumers_ReturnsFalse() { CheckFalse(new InputRouter().PointerMove(SomeInput)); } public void HighestPriorityTakingIt_StopsBeforeTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: true); Check(router.PointerMove(SomeInput)); CheckEqual(0, bottom.PointerMoveCount); } public void HighestPriorityDecliningIt_FallsThroughToTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: false); Check(router.PointerMove(SomeInput)); CheckEqual(1, bottom.PointerMoveCount); } public void EveryConsumerDecliningIt_ReturnsFalse() { var router = new InputRouter(); router.AddHighestPriority(new RecordingConsumer(false)); router.AddHighestPriority(new RecordingConsumer(false)); CheckFalse(router.PointerMove(SomeInput)); } public void PassesTheInputThrough() { var (router, top, _) = MakeRouter(topConsumes: true); router.PointerMove(SomeInput); CheckEqual(new Vector(12, 34), top.LastPointerInput.PixelPos); CheckEqual(PointerButtons.Right, top.LastPointerInput.Buttons); } public void ConsumerReorderingMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(PromoteTheLowestToHighest); CheckFalse(router.PointerMove(SomeInput)); CheckEqual(1, middle.PointerMoveCount); CheckEqual(1, bottom.PointerMoveCount); } } public class PointerUpTests : UnitTestSharp.TestFixture { public void NoConsumers_ReturnsFalse() { CheckFalse(new InputRouter().PointerUp(SomeInput)); } public void HighestPriorityTakingIt_StopsBeforeTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: true); Check(router.PointerUp(SomeInput)); CheckEqual(0, bottom.PointerUpCount); } public void HighestPriorityDecliningIt_FallsThroughToTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: false); Check(router.PointerUp(SomeInput)); CheckEqual(1, bottom.PointerUpCount); } public void EveryConsumerDecliningIt_ReturnsFalse() { var router = new InputRouter(); router.AddHighestPriority(new RecordingConsumer(false)); router.AddHighestPriority(new RecordingConsumer(false)); CheckFalse(router.PointerUp(SomeInput)); } public void PassesTheInputThrough() { var (router, top, _) = MakeRouter(topConsumes: true); router.PointerUp(SomeInput); CheckEqual(new Vector(12, 34), top.LastPointerInput.PixelPos); CheckEqual(PointerButtons.Right, top.LastPointerInput.Buttons); } public void ConsumerReorderingMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(PromoteTheLowestToHighest); CheckFalse(router.PointerUp(SomeInput)); CheckEqual(1, middle.PointerUpCount); CheckEqual(1, bottom.PointerUpCount); } } public class WheelTests : UnitTestSharp.TestFixture { public void NoConsumers_ReturnsFalse() { CheckFalse(new InputRouter().Wheel(120, new Vector(1, 2))); } public void HighestPriorityTakingIt_StopsBeforeTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: true); Check(router.Wheel(120, new Vector(1, 2))); CheckEqual(0, bottom.WheelCount); } public void HighestPriorityDecliningIt_FallsThroughToTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: false); Check(router.Wheel(120, new Vector(1, 2))); CheckEqual(1, bottom.WheelCount); } public void EveryConsumerDecliningIt_ReturnsFalse() { var router = new InputRouter(); router.AddHighestPriority(new RecordingConsumer(false)); router.AddHighestPriority(new RecordingConsumer(false)); CheckFalse(router.Wheel(120, new Vector(1, 2))); } public void PassesTicksAndPositionThrough() { var (router, top, _) = MakeRouter(topConsumes: true); router.Wheel(-240, new Vector(1, 2)); CheckEqual(-240, top.LastWheelTicks); CheckEqual(new Vector(1, 2), top.LastWheelPixelPos); } public void ConsumerReorderingMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(PromoteTheLowestToHighest); CheckFalse(router.Wheel(120, new Vector(1, 2))); CheckEqual(1, middle.WheelCount); CheckEqual(1, bottom.WheelCount); } } public class KeyDownTests : UnitTestSharp.TestFixture { public void NoConsumers_ReturnsFalse() { CheckFalse(new InputRouter().KeyDown(Key.PageUp)); } public void HighestPriorityTakingIt_StopsBeforeTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: true); Check(router.KeyDown(Key.PageUp)); CheckEqual(0, bottom.KeyDownCount); } public void HighestPriorityDecliningIt_FallsThroughToTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: false); Check(router.KeyDown(Key.PageUp)); CheckEqual(1, bottom.KeyDownCount); } public void EveryConsumerDecliningIt_ReturnsFalse() { var router = new InputRouter(); router.AddHighestPriority(new RecordingConsumer(false)); router.AddHighestPriority(new RecordingConsumer(false)); CheckFalse(router.KeyDown(Key.PageUp)); } public void PassesTheKeyThrough() { var (router, top, _) = MakeRouter(topConsumes: true); router.KeyDown(Key.Escape); CheckEqual(Key.Escape, top.LastKey); } public void ConsumerReorderingMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(PromoteTheLowestToHighest); CheckFalse(router.KeyDown(Key.PageUp)); CheckEqual(1, middle.KeyDownCount); CheckEqual(1, bottom.KeyDownCount); } } public class KeyUpTests : UnitTestSharp.TestFixture { public void NoConsumers_ReturnsFalse() { CheckFalse(new InputRouter().KeyUp(Key.PageUp)); } public void HighestPriorityTakingIt_StopsBeforeTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: true); Check(router.KeyUp(Key.PageUp)); CheckEqual(0, bottom.KeyUpCount); } public void HighestPriorityDecliningIt_FallsThroughToTheLowerOne() { var (router, _, bottom) = MakeRouter(topConsumes: false); Check(router.KeyUp(Key.PageUp)); CheckEqual(1, bottom.KeyUpCount); } public void EveryConsumerDecliningIt_ReturnsFalse() { var router = new InputRouter(); router.AddHighestPriority(new RecordingConsumer(false)); router.AddHighestPriority(new RecordingConsumer(false)); CheckFalse(router.KeyUp(Key.PageUp)); } public void PassesTheKeyThrough() { var (router, top, _) = MakeRouter(topConsumes: true); router.KeyUp(Key.Escape); CheckEqual(Key.Escape, top.LastKey); } public void ConsumerReorderingMidDispatch_StillOffersToEveryoneBelowOnce() { var (router, middle, bottom) = MakeMutatingOnTop(PromoteTheLowestToHighest); CheckFalse(router.KeyUp(Key.PageUp)); CheckEqual(1, middle.KeyUpCount); CheckEqual(1, bottom.KeyUpCount); } } } }