using UnitTestSharp; using System; using System.Collections.Generic; using System.Reflection; using Azimuth; using Annulus; namespace Glass.UnitTests { public class SpotlightTests : TestFixture { public static readonly Rect ScreenRect = Rect.FromTwoPoints(Vector.Zero, new Vector(800, 600)); public static readonly Rect OwnerBounds = Rect.FromTwoPoints(new Vector(100, 100), new Vector(300, 250)); public static readonly Vector InsideTheHole = new Vector(200, 175); public static readonly Vector OutsideTheHole = new Vector(400, 300); public static Material Dim => new SolidColorMaterial { DebugName = "dim" }; public static MouseState At(Vector position) { return new MouseState { Position = position, Buttons = MouseButtonFlags.Left }; } public static void Walk(Control root) { root.UpdateInSubtree( TimeSpan.Zero, new UpdateState { ScreenRect = ScreenRect, ParentClientRect = ScreenRect, ParentBounds = ScreenRect }); } public static Spotlight OnOwnerAt(Rect ownerBounds, Rect.BorderSize ownerBorder = default) { var owner = new Panel { Material = new Material { BorderSize = ownerBorder }, Bounds = ownerBounds, }; var spotlight = new Spotlight(Dim); owner.ChildControls.Add(spotlight); Walk(owner); return spotlight; } public static Spotlight OnTheUsualOwner() { return OnOwnerAt(OwnerBounds); } public class CtorTests : TestFixture { public void EscapesTheOwnersSpaceZOrderAndClip() { var spotlight = new Spotlight(Dim); CheckTrue(spotlight.Hoisted); CheckTrue(spotlight.AlwaysOnTop); CheckFalse(spotlight.ClipToAncestors); } public void LeavesItsOwnChildrenUnclipped() { CheckFalse(new Spotlight(Dim).ClipsChildren); } public void TakesFirstRefusalOnInput() { CheckTrue(new Spotlight(Dim).Modal); } public void DimsWithTheMaterialItWasGiven() { Material dim = Dim; CheckEqual(dim, ((SpotlightMaterial)new Spotlight(dim).Material).DimMaterial); } public void OwnsNoChildControlsOfItsOwn() { CheckEqual(0, new Spotlight(Dim).ChildControls.Count); } } public class UpdateTests : TestFixture { public void Always_TakesTheOwnersBoundsAsItsOwn() { CheckEqual(OwnerBounds, OnTheUsualOwner().Bounds); } public void Always_TakesTheOwnersBoundsRatherThanItsClientRect() { Spotlight spotlight = OnOwnerAt( OwnerBounds, new Rect.BorderSize { Left = 10, Top = 20, Right = 30, Bottom = 40 }); CheckEqual(OwnerBounds, spotlight.Bounds); } public void Always_LeavesItsChildrenInTheHighlightedControlsSpace() { var owner = new Panel { Material = Material.Invisible, Bounds = OwnerBounds }; var spotlight = new Spotlight(Dim); var recorder = new ControlTests.StateRecordingControl(); spotlight.ChildControls.Add(recorder); owner.ChildControls.Add(spotlight); Walk(owner); CheckEqual(OwnerBounds, recorder.LastState.ParentClientRect); } public void ZeroAreaOwner_IsTakenAsIsForTheMaterialToDealWith() { Rect collapsed = Rect.FromPositionAndSize(new Vector(100, 100), Vector.Zero); CheckEqual(collapsed, OnOwnerAt(collapsed).Bounds); } public void ZeroAreaOwner_StillBlocksTheRestOfTheScreen() { Spotlight spotlight = OnOwnerAt( Rect.FromPositionAndSize(new Vector(100, 100), Vector.Zero)); CheckTrue(spotlight.MouseDown(At(ScreenRect.TopLeft), MouseButtonFlags.Left)); CheckTrue(spotlight.MouseDown(At(ScreenRect.BottomRight), MouseButtonFlags.Left)); CheckTrue(spotlight.MouseDown(At(InsideTheHole), MouseButtonFlags.Left)); } public void OwnerOffScreen_StillBlocksEverywhere() { Spotlight spotlight = OnOwnerAt( Rect.FromTwoPoints(new Vector(900, 700), new Vector(1000, 800))); CheckTrue(spotlight.MouseDown(At(InsideTheHole), MouseButtonFlags.Left)); CheckTrue(spotlight.MouseDown(At(OutsideTheHole), MouseButtonFlags.Left)); } } public class MouseHandlerCoverageTests : TestFixture { public static List MouseHandlerNamesOn(Type type, BindingFlags flags) { var returnMe = new List(); foreach (MethodInfo method in type.GetMethods(flags)) { ParameterInfo[] parameters = method.GetParameters(); if (method.ReturnType == typeof(bool) && parameters.Length > 0 && parameters[0].ParameterType == typeof(MouseState).MakeByRefType()) { returnMe.Add(method.Name); } } returnMe.Sort(); return returnMe; } public static List OnControl() { return MouseHandlerNamesOn(typeof(Control), BindingFlags.Public | BindingFlags.Instance); } public void TheHandlersAreTheSixControlDeclares() { CheckEqual( "Click, MouseDown, MouseMove, MouseMoveEnd, MouseMoveStart, MouseUp", string.Join(", ", OnControl())); } public void SpotlightOverridesEveryOneOfThem() { CheckEqual( OnControl(), MouseHandlerNamesOn( typeof(Spotlight), BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)); } } public class MouseDownTests : TestFixture { public void Disabled_LetsEverythingThrough() { Spotlight spotlight = OnTheUsualOwner(); spotlight.Enabled = false; CheckFalse(spotlight.MouseDown(At(OutsideTheHole), MouseButtonFlags.Left)); } public void Always_StillFiresTheSpotlightsOwnHandler() { int heard = 0; Spotlight spotlight = OnTheUsualOwner(); spotlight.OnMouseDown += delegate (in MouseState _, MouseButtonFlags __) { ++heard; }; spotlight.MouseDown(At(OutsideTheHole), MouseButtonFlags.Left); CheckEqual(1, heard); } public void InsideTheHole_LetsItThrough() { CheckFalse(OnTheUsualOwner().MouseDown(At(InsideTheHole), MouseButtonFlags.Left)); } public void OutsideTheHole_SwallowsIt() { CheckTrue(OnTheUsualOwner().MouseDown(At(OutsideTheHole), MouseButtonFlags.Left)); } } public class MouseUpTests : TestFixture { public void Always_StillFiresTheSpotlightsOwnHandler() { int heard = 0; Spotlight spotlight = OnTheUsualOwner(); spotlight.OnMouseUp += delegate (in MouseState _, MouseButtonFlags __) { ++heard; }; spotlight.MouseUp(At(OutsideTheHole), MouseButtonFlags.Left); CheckEqual(1, heard); } public void InsideTheHole_LetsItThrough() { CheckFalse(OnTheUsualOwner().MouseUp(At(InsideTheHole), MouseButtonFlags.Left)); } public void OutsideTheHole_SwallowsIt() { CheckTrue(OnTheUsualOwner().MouseUp(At(OutsideTheHole), MouseButtonFlags.Left)); } } public class MouseMoveStartTests : TestFixture { public void Always_StillFiresTheSpotlightsOwnHandler() { int heard = 0; Spotlight spotlight = OnTheUsualOwner(); spotlight.OnMouseMoveStart += delegate (in MouseState _) { ++heard; }; spotlight.MouseMoveStart(At(OutsideTheHole)); CheckEqual(1, heard); } public void InsideTheHole_LetsItThrough() { CheckFalse(OnTheUsualOwner().MouseMoveStart(At(InsideTheHole))); } public void OutsideTheHole_SwallowsIt() { CheckTrue(OnTheUsualOwner().MouseMoveStart(At(OutsideTheHole))); } } public class MouseMoveTests : TestFixture { public void Always_StillFiresTheSpotlightsOwnHandler() { int heard = 0; Spotlight spotlight = OnTheUsualOwner(); spotlight.OnMouseMove += delegate (in MouseState _) { ++heard; }; spotlight.MouseMove(At(OutsideTheHole)); CheckEqual(1, heard); } public void InsideTheHole_LetsItThrough() { CheckFalse(OnTheUsualOwner().MouseMove(At(InsideTheHole))); } public void OutsideTheHole_SwallowsIt() { CheckTrue(OnTheUsualOwner().MouseMove(At(OutsideTheHole))); } } public class MouseMoveEndTests : TestFixture { public void Always_StillFiresTheSpotlightsOwnHandler() { int heard = 0; Spotlight spotlight = OnTheUsualOwner(); spotlight.OnMouseMoveEnd += delegate (in MouseState _) { ++heard; }; spotlight.MouseMoveEnd(At(OutsideTheHole)); CheckEqual(1, heard); } public void InsideTheHole_LetsItThrough() { CheckFalse(OnTheUsualOwner().MouseMoveEnd(At(InsideTheHole))); } public void OutsideTheHole_SwallowsIt() { CheckTrue(OnTheUsualOwner().MouseMoveEnd(At(OutsideTheHole))); } } public class ClickTests : TestFixture { public void Always_StillFiresTheSpotlightsOwnHandler() { int heard = 0; Spotlight spotlight = OnTheUsualOwner(); spotlight.OnClick += delegate (in MouseState _, MouseButtonFlags __) { ++heard; }; spotlight.Click(At(OutsideTheHole), MouseButtonFlags.Left); CheckEqual(1, heard); } public void InsideTheHole_LetsItThrough() { CheckFalse(OnTheUsualOwner().Click(At(InsideTheHole), MouseButtonFlags.Left)); } public void OutsideTheHole_SwallowsIt() { CheckTrue(OnTheUsualOwner().Click(At(OutsideTheHole), MouseButtonFlags.Left)); } } public class InputRoutingTests : TestFixture { private MouseManagerTests.RecordingControl owner; private Screen screen; public override void TestSetup() { owner = new MouseManagerTests.RecordingControl("owner") { Bounds = OwnerBounds, Handles = true, }; screen = new Screen { Bounds = ScreenRect }; screen.Controls.Add(owner); } [IgnoreTest] public Spotlight Spotlight() { var spotlight = new Glass.Spotlight(Dim); owner.ChildControls.Add(spotlight); screen.Update(TimeSpan.Zero); return spotlight; } public void InsideTheHole_ReachesTheHighlightedControl() { Spotlight(); Control claimed = screen.MouseManager.MouseDown(At(InsideTheHole), MouseButtonFlags.Left); CheckEqual(owner, claimed); CheckEqual(1, owner.MouseDownCount); } public void OutsideTheHole_NeverReachesTheHighlightedControl() { Spotlight spotlight = Spotlight(); Control claimed = screen.MouseManager.MouseDown(At(OutsideTheHole), MouseButtonFlags.Left); CheckEqual(spotlight, claimed); CheckEqual(0, owner.MouseDownCount); } public void AChildOfTheSpotlight_SitsInTheTargetsSpaceAndIsStillClickable() { Spotlight spotlight = Spotlight(); // Bounds run from the hole's top left, which is what makes "just below the target" this simple. var coachMark = new MouseManagerTests.RecordingControl("coach mark") { Bounds = Rect.FromTwoPoints( new Vector(0, OwnerBounds.Height + 25), new Vector(100, OwnerBounds.Height + 75)), Handles = true, }; spotlight.ChildControls.Add(coachMark); Control claimed = screen.MouseManager.MouseDown( At(new Vector(150, 300)), MouseButtonFlags.Left); CheckEqual(coachMark, claimed); CheckEqual(0, owner.MouseDownCount); } } } }