using Amphora; using Annulus; using Azimuth; using Clave; using System; using System.Collections.Generic; using UnitTestSharp; namespace Glass.UnitTests { public class SliderTests : TestFixture { /// /// A 120x20 slider at the origin with a 20 wide thumb, holding 0 to 10 in whole steps with a coarse step of 5. /// The thumb travels 100, so one unit of value is ten pixels. /// public static Slider NewSlider() { var slider = new Slider { Grid = new NumberGrid { Max = 10, CoarseStep = 5 }, }; slider.Thumb.ButtonMaterial = new ScrollTrackTests.FixedSizeMaterial(new Vector(20, 20)); slider.Bounds = Rect.FromPositionAndSize(Vector.Zero, new Vector(120, 20)); return slider; } /// /// A look that wants a size but has no minimum, as a stretchy sprite does. /// public record PreferredOnlyMaterial : Material { private readonly Vector size; public PreferredOnlyMaterial(Vector size) { this.size = size; } protected override Vector PreferredSizeInModelSpace(Constraints constraints) => constraints.Constrain(size); } public static MouseState At(Scalar x, Scalar y) => new MouseState { Position = new Vector(x, y) }; public static List Watch(Slider slider) { var returnMe = new List(); slider.OnValueChanged += returnMe.Add; return returnMe; } public static void Drag(Slider slider, Scalar fromX, Scalar toX) { slider.Thumb.MouseDown(At(fromX, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); slider.Thumb.MouseMove(At(toX, 10), NoKeys.Held, NoKeys.Manager); } public class CtorTests : TestFixture { public void IsFocusableAndTakesFocusOnClick() { var slider = new Slider(); CheckTrue(slider.Focusable); CheckTrue(slider.FocusOnClick); } public void StartsAtTheGridsMinimum() { CheckEqual(0m, new Slider().Value); } public void DefaultsToZeroThroughOneHundred() { var slider = new Slider(); CheckEqual(0m, slider.Grid.Min); CheckEqual(100m, slider.Grid.Max); } public void KeepsTheBehaviorItWasGiven() { var behavior = new SliderBehavior(); CheckTrue(ReferenceEquals(behavior, new Slider(behavior).Behavior)); } public void WithoutOne_UsesTheDefaultBehavior() { CheckTrue(ReferenceEquals(SliderBehavior.Default, new Slider().Behavior)); } public void NullBehavior_Throws() { CheckThrow(typeof(ArgumentNullException)); new Slider(null); } public void DrawsNothingOfItsOwn() { CheckEqual(Material.Invisible, new Slider().Material); } public void HoldsTheTrackAsAChild() { var slider = new Slider(); CheckTrue(slider.Track.Thumb == slider.Thumb); CheckEqual(1, slider.CanvasChildren.Count); } } public class GridTests : TestFixture { public void InvalidGrid_Throws() { var slider = new Slider(); CheckThrow(typeof(ArgumentException)); slider.Grid = new NumberGrid { Min = 5, Max = 1 }; } public void InvalidGrid_LeavesTheOldOneInPlace() { Slider slider = NewSlider(); try { slider.Grid = new NumberGrid { Min = 5, Max = 1 }; } catch (ArgumentException) { } CheckEqual(10m, slider.Grid.Max); } public void ValueOutsideTheNewGrid_MovesOntoIt() { Slider slider = NewSlider(); slider.Value = 8; slider.Grid = new NumberGrid { Max = 5 }; CheckEqual(5m, slider.Value); } public void ValueMovedByTheNewGrid_RaisesTheEvent() { Slider slider = NewSlider(); slider.Value = 8; List seen = Watch(slider); slider.Grid = new NumberGrid { Max = 5 }; CheckEqual(1, seen.Count); CheckEqual(5m, seen[0]); } public void ValueAlreadyOnTheNewGrid_RaisesNothing() { Slider slider = NewSlider(); slider.Value = 3; List seen = Watch(slider); slider.Grid = new NumberGrid { Max = 20 }; CheckEqual(0, seen.Count); } public void ValueOffTheNewResolution_SnapsOntoIt() { Slider slider = NewSlider(); slider.Value = 3; slider.Grid = new NumberGrid { Max = 10, Resolution = 5 }; CheckEqual(5m, slider.Value); } public void RescalesTheThumb() { Slider slider = NewSlider(); slider.Value = 5; CheckEqual(50, slider.Thumb.Bounds.TopLeft.X); slider.Grid = new NumberGrid { Max = 20 }; CheckEqual(25, slider.Thumb.Bounds.TopLeft.X); } public void PagesByTheNewCoarseStep() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Max = 10, CoarseStep = 2 }; slider.Track.MouseDown(At(110, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckEqual(2m, slider.Value); } public void MinAboveZero_MeasuresTheThumbFromMin() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Min = 10, Max = 20 }; slider.Value = 15; CheckEqual(50, slider.Thumb.Bounds.TopLeft.X); } public void SingleValueGrid_PutsTheThumbAtTheStart() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Min = 4, Max = 4 }; CheckEqual(4m, slider.Value); CheckEqual(0, slider.Thumb.Bounds.TopLeft.X); } } public class ValueTests : TestFixture { public void SnapsOntoTheGrid() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Max = 10, Resolution = 2 }; slider.Value = 5; CheckEqual(6m, slider.Value); } public void ClampsIntoTheRange() { Slider slider = NewSlider(); slider.Value = 99; CheckEqual(10m, slider.Value); slider.Value = -4; CheckEqual(0m, slider.Value); } public void Change_RaisesTheEventWithTheNewValue() { Slider slider = NewSlider(); List seen = Watch(slider); slider.Value = 7; CheckEqual(1, seen.Count); CheckEqual(7m, seen[0]); } public void SnappedToTheCurrentValue_RaisesNothing() { Slider slider = NewSlider(); slider.Value = 3; List seen = Watch(slider); slider.Value = 3.2m; CheckEqual(0, seen.Count); } public void MovesTheThumbAlongTheTrack() { Slider slider = NewSlider(); slider.Value = 0; CheckEqual(0, slider.Thumb.Bounds.TopLeft.X); slider.Value = 5; CheckEqual(50, slider.Thumb.Bounds.TopLeft.X); slider.Value = 10; CheckEqual(100, slider.Thumb.Bounds.TopLeft.X); } public void ThumbKeepsItsLengthAcrossTheRange() { Slider slider = NewSlider(); slider.Value = 0; CheckEqual(20, slider.Thumb.Bounds.Size.X); slider.Value = 10; CheckEqual(20, slider.Thumb.Bounds.Size.X); } } public class SetValueWithoutNotifyTests : TestFixture { public void ChangesTheValue() { Slider slider = NewSlider(); slider.SetValueWithoutNotify(7); CheckEqual(7m, slider.Value); } public void RaisesNothing() { Slider slider = NewSlider(); List seen = Watch(slider); slider.SetValueWithoutNotify(7); CheckEqual(0, seen.Count); } public void SnapsOntoTheGridAndClamps() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Max = 10, Resolution = 2 }; slider.SetValueWithoutNotify(5); CheckEqual(6m, slider.Value); slider.SetValueWithoutNotify(99); CheckEqual(10m, slider.Value); } public void MovesTheThumb() { Slider slider = NewSlider(); slider.SetValueWithoutNotify(5); CheckEqual(50, slider.Thumb.Bounds.TopLeft.X); } public void LaterChangesStillRaiseTheEvent() { Slider slider = NewSlider(); List seen = Watch(slider); slider.SetValueWithoutNotify(7); slider.Value = 8; CheckEqual(1, seen.Count); CheckEqual(8m, seen[0]); } public void SettingTheValueItAlreadyHasThroughTheNotifyingSetter_RaisesNothing() { Slider slider = NewSlider(); slider.SetValueWithoutNotify(7); List seen = Watch(slider); slider.Value = 7; CheckEqual(0, seen.Count); } } public class OrientationTests : TestFixture { public void Vertical_MovesTheThumbDownTheTrack() { Slider slider = NewSlider(); slider.Orientation = Orientation.Vertical; slider.Bounds = Rect.FromPositionAndSize(Vector.Zero, new Vector(20, 120)); slider.Value = 5; CheckEqual(50, slider.Thumb.Bounds.TopLeft.Y); CheckEqual(0, slider.Thumb.Bounds.TopLeft.X); } public void ChangingIt_ResizesTheTrackToFillTheSlider() { Slider slider = NewSlider(); slider.Orientation = Orientation.Vertical; CheckEqual(slider.CanvasRect.Size, slider.Track.Bounds.Size); } } public class BoundsTests : TestFixture { public void TrackFillsTheSlider() { Slider slider = NewSlider(); slider.Bounds = Rect.FromPositionAndSize(new Vector(30, 40), new Vector(200, 24)); CheckEqual(new Vector(200, 24), slider.Track.Bounds.Size); CheckEqual(Vector.Zero, slider.Track.Bounds.TopLeft); } } public class EnabledTests : TestFixture { public void Disabling_DisablesTheTrackAndThumb() { Slider slider = NewSlider(); slider.Enabled = false; CheckFalse(slider.Track.Enabled); CheckFalse(slider.Thumb.Enabled); } public void ReEnabling_EnablesThemAgain() { Slider slider = NewSlider(); slider.Enabled = false; slider.Enabled = true; CheckTrue(slider.Track.Enabled); CheckTrue(slider.Thumb.Enabled); } } public class SliderMaterialTests : TestFixture { public void RoundTripsTheTrackAndThumbLooks() { var slider = new Slider(); var track = new Material(); ButtonMaterial thumb = new ScrollTrackTests.FixedSizeMaterial(new Vector(8, 8)); slider.SliderMaterial = new SliderMaterial { Track = track, Thumb = thumb }; CheckEqual(track, slider.SliderMaterial.Track); CheckEqual(thumb, slider.SliderMaterial.Thumb); } public void ThumbTakesItsPreferredLengthEvenWithNoMinimum() { Slider slider = NewSlider(); slider.Thumb.ButtonMaterial = new PreferredOnlyMaterial(new Vector(16, 20)); slider.Value = 0; CheckEqual(16, slider.Thumb.Bounds.Size.X); slider.Value = 10; CheckEqual(16, slider.Thumb.Bounds.Size.X); CheckEqual(104, slider.Thumb.Bounds.TopLeft.X); } public void ThumbLengthFollowsTheTrackWhenTheSliderIsResized() { Slider slider = NewSlider(); slider.Value = 10; slider.Bounds = Rect.FromPositionAndSize(Vector.Zero, new Vector(220, 20)); CheckEqual(200, slider.Thumb.Bounds.TopLeft.X); CheckEqual(20, slider.Thumb.Bounds.Size.X); } public void ThumbTakesTheTracksWholeLengthWhenItIsNoLongerThanTheThumb() { Slider slider = NewSlider(); slider.Bounds = Rect.FromPositionAndSize(Vector.Zero, new Vector(15, 20)); CheckEqual(15, slider.Thumb.Bounds.Size.X); } public void SizesTheThumbFromItsMaterial() { Slider slider = NewSlider(); slider.SliderMaterial = new SliderMaterial { Track = null, Thumb = new ScrollTrackTests.FixedSizeMaterial(new Vector(30, 20)), }; CheckEqual(30, slider.Thumb.Bounds.Size.X); } } public class KeyDownTests : TestFixture { [IgnoreTest] private void CheckKeyMoves(KeyCode key, decimal from, decimal expected) { Slider slider = NewSlider(); slider.Value = from; var keys = new FocusedKeys(slider); CheckTrue(keys.Press(key)); CheckEqual(expected, slider.Value); } public void ArrowRight_StepsUp() { CheckKeyMoves(KeyCode.ArrowRight, from: 3, expected: 4); } public void ArrowUp_StepsUp() { CheckKeyMoves(KeyCode.ArrowUp, from: 3, expected: 4); } public void ArrowLeft_StepsDown() { CheckKeyMoves(KeyCode.ArrowLeft, from: 3, expected: 2); } public void ArrowDown_StepsDown() { CheckKeyMoves(KeyCode.ArrowDown, from: 3, expected: 2); } public void PageUp_StepsUpByTheCoarseStep() { CheckKeyMoves(KeyCode.PageUp, from: 1, expected: 5); } public void PageDown_StepsDownByTheCoarseStep() { CheckKeyMoves(KeyCode.PageDown, from: 8, expected: 5); } public void Home_JumpsToTheMinimum() { CheckKeyMoves(KeyCode.Home, from: 6, expected: 0); } public void End_JumpsToTheMaximum() { CheckKeyMoves(KeyCode.End, from: 6, expected: 10); } public void StepUpAtTheMaximum_ClaimsTheKeyAndStaysPut() { CheckKeyMoves(KeyCode.ArrowRight, from: 10, expected: 10); } public void StepDownAtTheMinimum_ClaimsTheKeyAndStaysPut() { CheckKeyMoves(KeyCode.ArrowLeft, from: 0, expected: 0); } public void StepAtTheLimit_RaisesNoEvent() { Slider slider = NewSlider(); slider.Value = 10; List seen = Watch(slider); new FocusedKeys(slider).Press(KeyCode.ArrowRight); CheckEqual(0, seen.Count); } public void HeldKey_StepsAgainOnEachRepeat() { Slider slider = NewSlider(); var keys = new FocusedKeys(slider); keys.Press(KeyCode.ArrowRight); CheckTrue(keys.Repeat(KeyCode.ArrowRight)); CheckTrue(keys.Repeat(KeyCode.ArrowRight)); CheckEqual(3m, slider.Value); } public void StepsOffTheGridValue_ByTheGridsRules() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Max = 10, Resolution = 1, Step = 2 }; slider.Value = 3; new FocusedKeys(slider).Press(KeyCode.ArrowRight); CheckEqual(4m, slider.Value); } public void BehaviorsKeys_ReplaceTheDefaultOnes() { var slider = new Slider(SliderBehavior.Default with { IncreaseChords = SliderBehaviorTests.Pressing(KeyCode.E), }); slider.Grid = new NumberGrid { Max = 10 }; var keys = new FocusedKeys(slider); CheckFalse(keys.Press(KeyCode.ArrowRight)); CheckEqual(0m, slider.Value); CheckTrue(keys.Press(KeyCode.E)); CheckEqual(1m, slider.Value); } public void OtherKeys_AreLeftUnclaimed() { Slider slider = NewSlider(); var keys = new FocusedKeys(slider); CheckFalse(keys.Press(KeyCode.A)); CheckFalse(keys.Press(KeyCode.Space)); CheckEqual(0m, slider.Value); } public void WhenDisabled_LeavesTheKeyUnclaimed() { Slider slider = NewSlider(); var keys = new FocusedKeys(slider); slider.Enabled = false; CheckFalse(keys.Press(KeyCode.ArrowRight)); CheckEqual(0m, slider.Value); } public void GameShortcutOnTheSameKey_LosesToTheSlider() { Slider slider = NewSlider(); var keys = new FocusedKeys(slider); Chord shortcut = keys.Manager.AddShortcut(Chord.PressOrRepeat(KeyCode.ArrowRight)); KeyboardSnapshot snapshot = keys.Tracker.KeyDown(KeyCode.ArrowRight, KeyCode.None, isRepeat: false); CheckTrue(keys.Manager.KeyDown(snapshot)); CheckFalse(keys.Manager.ShouldFire(shortcut, snapshot)); CheckEqual(1m, slider.Value); } } public class ThumbDragTests : TestFixture { public void DraggingHalfwayAcrossTheTravel_HoldsHalfTheRange() { Slider slider = NewSlider(); Drag(slider, fromX: 10, toX: 60); CheckEqual(5m, slider.Value); CheckEqual(50, slider.Thumb.Bounds.TopLeft.X); } public void DraggingBetweenGridPoints_SnapsTheValueAndTheThumb() { Slider slider = NewSlider(); Drag(slider, fromX: 10, toX: 64); CheckEqual(5m, slider.Value); CheckEqual(50, slider.Thumb.Bounds.TopLeft.X); } public void DraggingPastTheEnd_StopsAtTheMaximum() { Slider slider = NewSlider(); Drag(slider, fromX: 10, toX: 500); CheckEqual(10m, slider.Value); CheckEqual(100, slider.Thumb.Bounds.TopLeft.X); } public void DraggingBackBeforeTheStart_StopsAtTheMinimum() { Slider slider = NewSlider(); slider.Value = 5; Drag(slider, fromX: 60, toX: -300); CheckEqual(0m, slider.Value); } public void RaisesTheEventOnlyWhenTheValueReachesANewGridPoint() { Slider slider = NewSlider(); List seen = Watch(slider); slider.Thumb.MouseDown(At(10, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); slider.Thumb.MouseMove(At(14, 10), NoKeys.Held, NoKeys.Manager); slider.Thumb.MouseMove(At(16, 10), NoKeys.Held, NoKeys.Manager); slider.Thumb.MouseMove(At(18, 10), NoKeys.Held, NoKeys.Manager); slider.Thumb.MouseMove(At(31, 10), NoKeys.Held, NoKeys.Manager); CheckEqual(2, seen.Count); CheckEqual(1m, seen[0]); CheckEqual(2m, seen[1]); } public void ADragThatCrossesSeveralPointsAtOnce_RaisesOneEvent() { Slider slider = NewSlider(); List seen = Watch(slider); Drag(slider, fromX: 10, toX: 90); CheckEqual(1, seen.Count); CheckEqual(8m, seen[0]); } public void ExactValueOnAFinerGrid_IsKeptWithoutFloatingPointNoise() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Max = 1, Resolution = 0.1m }; Drag(slider, fromX: 10, toX: 40); CheckEqual(0.3m, slider.Value); } } public class TrackClickTests : TestFixture { public void BeyondTheThumb_PagesUpByTheCoarseStep() { Slider slider = NewSlider(); slider.Track.MouseDown(At(110, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckEqual(5m, slider.Value); } public void BeforeTheThumb_PagesDownByTheCoarseStep() { Slider slider = NewSlider(); slider.Value = 8; slider.Track.MouseDown(At(10, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckEqual(3m, slider.Value); } public void PastTheEnd_StopsAtTheMaximum() { Slider slider = NewSlider(); slider.Value = 8; slider.Track.MouseDown(At(115, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckEqual(10m, slider.Value); } public void OnAFinerGrid_PagesOntoAGridPoint() { Slider slider = NewSlider(); slider.Grid = new NumberGrid { Max = 1, Resolution = 0.1m, CoarseStep = 0.3m }; slider.Track.MouseDown(At(110, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckEqual(0.3m, slider.Value); } public void WhenDisabled_DoesNothing() { Slider slider = NewSlider(); slider.Enabled = false; slider.Track.MouseDown(At(110, 10), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckEqual(0m, slider.Value); } } } }