using System.Runtime.CompilerServices; using Annulus; using Azimuth; using UnitTestSharp; namespace Glass.UnitTests { public class ButtonBaseTests : TestFixture { // The smallest concrete ButtonBase: a settable material set and nothing else, so the base's machine is // exercised without Button or Checkbox in the picture. public class TestButtonBase : ButtonBase { public ButtonMaterial MaterialSet { get; set; } protected override ButtonMaterial CurrentButtonMaterial => MaterialSet; } public class MaterialTests : TestFixture { public record TestMaterial : Material { public TestMaterial([CallerMemberName] string debugName = null) : base(debugName) { } } static TestMaterial Normal = new TestMaterial(); static TestMaterial Hover = new TestMaterial(); static TestMaterial Pressed = new TestMaterial(); static TestMaterial HoverAndPressed = new TestMaterial(); static TestMaterial Disabled = new TestMaterial(); TestButtonBase button; public override void TestSetup() { button = new TestButtonBase(); button.MaterialSet = new ButtonMaterial { NominalMaterial = Normal, HoverMaterial = Hover, PressedMaterial = Pressed, HoverPressedMaterial = HoverAndPressed, DisabledMaterial = Disabled, }; } public void ReturnsNominalByDefault() { CheckEqual(Normal, button.Material); } public void ReturnsHoverWhenHovered() { button.HoveredOver = true; CheckEqual(Hover, button.Material); } public void ReturnsPressedWhenPressed() { button.ButtonsDown = MouseButtonFlags.Left; CheckEqual(Pressed, button.Material); } public void ReturnsHoverPressedWhenHoveredAndPressed() { button.ButtonsDown = MouseButtonFlags.Left; button.HoveredOver = true; CheckEqual(HoverAndPressed, button.Material); } public void ReturnsDisabledWhenDisabledEvenWhilePressedAndHovered() { button.ButtonsDown = MouseButtonFlags.Left; button.HoveredOver = true; button.Enabled = false; CheckEqual(Disabled, button.Material); } } public class PressedTests : TestFixture { public void FalseWhenNoButtonsDown() { var button = new TestButtonBase(); button.ButtonsDown = MouseButtonFlags.None; CheckFalse(button.Pressed); } public void TrueWhenAButtonIsDown() { var button = new TestButtonBase(); button.ButtonsDown = MouseButtonFlags.Middle; Check(button.Pressed); } public void TrueForAnOutOfRangeButtonFlag() { var button = new TestButtonBase(); button.ButtonsDown = MouseButtonFlags.End; Check(button.Pressed); } } public class MouseDownTests : TestFixture { public void CallsCallbackWithCorrectArguments() { var button = new TestButtonBase(); var state = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; bool called = false; button.OnMouseDown += (in MouseState stateIn, MouseButtonFlags buttonIn) => { var expected = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; CheckEqual(expected, stateIn); CheckEqual(MouseButtonFlags.Left, buttonIn); called = true; }; bool locked = button.MouseDown(state, MouseButtonFlags.Left); Check(called); Check(locked); } public void RecordsMouseButton() { var button = new TestButtonBase(); var state = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; button.MouseDown(state, MouseButtonFlags.Left); CheckEqual(MouseButtonFlags.Left, button.ButtonsDown); } public void LatchesEvenWithNoAttachedCallback() { var button = new TestButtonBase(); var state = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; bool locked = button.MouseDown(state, MouseButtonFlags.Left); Check(locked); } public void DoesNotOverwriteOldMouseButtons() { var button = new TestButtonBase(); var state = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; button.ButtonsDown = MouseButtonFlags.Middle | MouseButtonFlags.Right; button.MouseDown(state, MouseButtonFlags.Left); CheckEqual(MouseButtonFlags.Left | MouseButtonFlags.Middle | MouseButtonFlags.Right, button.ButtonsDown); } } public class MouseUpTests : TestFixture { public void CallsCallbackWithCorrectArguments() { var button = new TestButtonBase(); var state = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; bool called = false; button.OnMouseUp += (in MouseState stateIn, MouseButtonFlags buttonIn) => { var expected = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; CheckEqual(expected, stateIn); CheckEqual(MouseButtonFlags.Left, buttonIn); called = true; }; bool locked = button.MouseUp(state, MouseButtonFlags.Left); Check(called); Check(locked); } public void ReturnsFalseIfNoAttachedCallback() { var button = new TestButtonBase(); var state = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Right | MouseButtonFlags.Middle, }; bool locked = button.MouseUp(state, MouseButtonFlags.Left); CheckFalse(locked); } public void UnsetsSetMouseButton() { var button = new TestButtonBase(); var state = new MouseState(); button.ButtonsDown = MouseButtonFlags.Middle | MouseButtonFlags.Right; button.MouseUp(state, MouseButtonFlags.Right); CheckEqual(MouseButtonFlags.Middle, button.ButtonsDown); } } public class MouseMoveTests : TestFixture { public void SetsHoveredWhenInside() { var button = new TestButtonBase(); button.Bounds = Rect.FromTwoPoints(new Vector(0, 0), new Vector(100, 100)); button.MouseMove(new MouseState()); Check(button.HoveredOver); } public void ClearsHoveredWhenOutside() { var button = new TestButtonBase(); button.HoveredOver = true; button.Bounds = Rect.FromTwoPoints(new Vector(10, 10), new Vector(100, 100)); button.MouseMove(new MouseState()); CheckFalse(button.HoveredOver); } public void CallsCallbackWhenInside() { var button = new TestButtonBase(); bool called = false; button.OnMouseMove += (in MouseState stateIn) => { CheckEqual(Vector.Zero, stateIn.Position); CheckEqual(MouseButtonFlags.Middle | MouseButtonFlags.Right, stateIn.Buttons); called = true; }; button.Bounds = Rect.FromTwoPoints(new Vector(0, 0), new Vector(100, 100)); button.MouseMove(new MouseState { Buttons = MouseButtonFlags.Middle | MouseButtonFlags.Right }); Check(called); } public void CallsCallbackWhenOutside() { var button = new TestButtonBase(); bool called = false; button.OnMouseMove += (in MouseState stateIn) => { CheckEqual(Vector.Zero, stateIn.Position); CheckEqual(MouseButtonFlags.Middle | MouseButtonFlags.Right, stateIn.Buttons); called = true; }; button.Bounds = Rect.FromTwoPoints(new Vector(10, 10), new Vector(100, 100)); button.MouseMove(new MouseState { Buttons = MouseButtonFlags.Middle | MouseButtonFlags.Right }); Check(called); } public void LatchesWhenThereIsACallback() { var button = new TestButtonBase(); button.OnMouseMove += (in MouseState stateIn) => { }; Check(button.MouseMove(new MouseState())); } public void LatchesWhenThereIsNoCallback() { var button = new TestButtonBase(); CheckTrue(button.MouseMove(new MouseState())); } } public class MouseMoveStartTests : TestFixture { public void SetsHoveredWhenInside() { var button = new TestButtonBase(); button.Bounds = Rect.FromTwoPoints(new Vector(0, 0), new Vector(100, 100)); button.MouseMoveStart(new MouseState()); Check(button.HoveredOver); } public void ClearsHoveredWhenOutside() { var button = new TestButtonBase(); button.HoveredOver = true; button.Bounds = Rect.FromTwoPoints(new Vector(10, 10), new Vector(100, 100)); button.MouseMoveStart(new MouseState()); CheckFalse(button.HoveredOver); } public void CallsCallbackWhenInside() { var button = new TestButtonBase(); bool called = false; button.OnMouseMoveStart += (in MouseState stateIn) => { CheckEqual(Vector.Zero, stateIn.Position); CheckEqual(MouseButtonFlags.Middle | MouseButtonFlags.Right, stateIn.Buttons); called = true; }; button.Bounds = Rect.FromTwoPoints(new Vector(0, 0), new Vector(100, 100)); button.MouseMoveStart(new MouseState { Buttons = MouseButtonFlags.Middle | MouseButtonFlags.Right }); Check(called); } public void CallsCallbackWhenOutside() { var button = new TestButtonBase(); bool called = false; button.OnMouseMoveStart += (in MouseState stateIn) => { CheckEqual(Vector.Zero, stateIn.Position); CheckEqual(MouseButtonFlags.Middle | MouseButtonFlags.Right, stateIn.Buttons); called = true; }; button.Bounds = Rect.FromTwoPoints(new Vector(10, 10), new Vector(100, 100)); button.MouseMoveStart(new MouseState { Buttons = MouseButtonFlags.Middle | MouseButtonFlags.Right }); Check(called); } public void LatchesWhenThereIsACallback() { var button = new TestButtonBase(); button.OnMouseMoveStart += (in MouseState stateIn) => { }; Check(button.MouseMoveStart(new MouseState())); } public void LatchesWhenThereIsNoCallback() { var button = new TestButtonBase(); CheckTrue(button.MouseMoveStart(new MouseState())); } } public class MouseMoveEndTests : TestFixture { public void ClearsHoveredWhenInside() { var button = new TestButtonBase(); button.HoveredOver = true; button.Bounds = Rect.FromTwoPoints(new Vector(0, 0), new Vector(100, 100)); button.MouseMoveEnd(new MouseState()); CheckFalse(button.HoveredOver); } public void ClearsHoveredWhenOutside() { var button = new TestButtonBase(); button.HoveredOver = true; button.Bounds = Rect.FromTwoPoints(new Vector(10, 10), new Vector(100, 100)); button.MouseMoveEnd(new MouseState()); CheckFalse(button.HoveredOver); } public void CallsCallbackWhenInside() { var button = new TestButtonBase(); bool called = false; button.OnMouseMoveEnd += (in MouseState stateIn) => { CheckEqual(Vector.Zero, stateIn.Position); CheckEqual(MouseButtonFlags.Middle | MouseButtonFlags.Right, stateIn.Buttons); called = true; }; button.Bounds = Rect.FromTwoPoints(new Vector(0, 0), new Vector(100, 100)); button.MouseMoveEnd(new MouseState { Buttons = MouseButtonFlags.Middle | MouseButtonFlags.Right }); Check(called); } public void CallsCallbackWhenOutside() { var button = new TestButtonBase(); bool called = false; button.OnMouseMoveEnd += (in MouseState stateIn) => { CheckEqual(Vector.Zero, stateIn.Position); CheckEqual(MouseButtonFlags.Middle | MouseButtonFlags.Right, stateIn.Buttons); called = true; }; button.Bounds = Rect.FromTwoPoints(new Vector(10, 10), new Vector(100, 100)); button.MouseMoveEnd(new MouseState { Buttons = MouseButtonFlags.Middle | MouseButtonFlags.Right }); Check(called); } public void LatchesWhenThereIsACallback() { var button = new TestButtonBase(); button.OnMouseMoveEnd += (in MouseState stateIn) => { }; Check(button.MouseMoveEnd(new MouseState())); } public void DoesNotLatchWhenThereIsNoCallback() { var button = new TestButtonBase(); CheckFalse(button.MouseMoveEnd(new MouseState())); } } public class CancelMouseInputTests : TestFixture { public void Always_ClearsPressedAndHoveredState() { var button = new TestButtonBase(); button.ButtonsDown = MouseButtonFlags.Left; button.HoveredOver = true; button.CancelMouseInput(); CheckEqual(MouseButtonFlags.None, button.ButtonsDown); CheckFalse(button.HoveredOver); } public void Always_LeavesItEnabled() { var button = new TestButtonBase(); button.CancelMouseInput(); Check(button.Enabled); } public void Always_ChangesTheMouseInputVersion() { var button = new TestButtonBase(); int before = button.MouseInputVersion; button.CancelMouseInput(); CheckNotEqual(before, button.MouseInputVersion); } } public class DisablingClearsStateTests : TestFixture { public void ClearsPressedAndHovered() { var button = new TestButtonBase(); button.ButtonsDown = MouseButtonFlags.Left; button.HoveredOver = true; button.Enabled = false; CheckEqual(MouseButtonFlags.None, button.ButtonsDown); CheckFalse(button.HoveredOver); } public void ReenablingDoesNotRestoreState() { var button = new TestButtonBase(); button.ButtonsDown = MouseButtonFlags.Left; button.HoveredOver = true; button.Enabled = false; button.Enabled = true; CheckEqual(MouseButtonFlags.None, button.ButtonsDown); CheckFalse(button.HoveredOver); } } public class DisabledInputTests : TestFixture { private TestButtonBase button; private bool callbackCalled; public override void TestSetup() { callbackCalled = false; button = new TestButtonBase(); button.Enabled = false; button.OnMouseDown += (in MouseState _, MouseButtonFlags __) => callbackCalled = true; button.OnMouseUp += (in MouseState _, MouseButtonFlags __) => callbackCalled = true; button.OnMouseMoveStart += (in MouseState _) => callbackCalled = true; button.OnMouseMove += (in MouseState _) => callbackCalled = true; button.OnMouseMoveEnd += (in MouseState _) => callbackCalled = true; button.OnClick += (in MouseState _, MouseButtonFlags __) => callbackCalled = true; } public delegate bool MouseCall(TestButtonBase target, MouseState state); private static MouseCall[] mouseCalls = new MouseCall[] { (TestButtonBase target, MouseState state) => target.MouseDown(state, MouseButtonFlags.Left), (TestButtonBase target, MouseState state) => target.MouseUp(state, MouseButtonFlags.Left), (TestButtonBase target, MouseState state) => target.MouseMoveStart(state), (TestButtonBase target, MouseState state) => target.MouseMove(state), (TestButtonBase target, MouseState state) => target.MouseMoveEnd(state), (TestButtonBase target, MouseState state) => target.Click(state, MouseButtonFlags.Left), }; [Parameterized(nameof(mouseCalls))] public void DoesNotCallCallback(MouseCall call) { var mouseState = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Middle, }; call(button, mouseState); CheckFalse(callbackCalled); } [Parameterized(nameof(mouseCalls))] public void ReturnsFalse(MouseCall call) { var mouseState = new MouseState { Position = new Vector(3, 4), Buttons = MouseButtonFlags.Middle, }; CheckFalse(call(button, mouseState)); } } } }