using Annulus; using Azimuth; using System; using System.Drawing; using System.Linq; using UnitTestSharp; namespace Glass.UnitTests { public class DashedFrameMaterialTests : TestFixture { public static readonly Material Dash = new SolidColorMaterial { Color = Color.Blue }; /// /// Dashes 3 long, at least 2 apart and 1 thick, with no inset. /// public static DashedFrameMaterial Standard => new DashedFrameMaterial(Dash); public static Rect RectAt(Scalar x, Scalar y, Scalar width, Scalar height) { return Rect.FromPositionAndSize(new Vector(x, y), new Vector(width, height)); } public class CtorTests : TestFixture { public void KeepsTheDashMaterial() { CheckEqual(Dash, Standard.DashMaterial); } public void DefaultsToThreeLongTwoApartOneThickAndNoInset() { DashedFrameMaterial material = Standard; CheckEqual(3, material.DashLength); CheckEqual(2, material.MinGap); CheckEqual(1, material.Thickness); CheckEqual(0, material.Inset); } } public class DashLengthTests : TestFixture { public void Assigned_IsKept() { CheckEqual(5, (Standard with { DashLength = 5 }).DashLength); } public void Zero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { DashLength = 0 }; } public void Negative_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { DashLength = -1 }; } public void NaN_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { DashLength = double.NaN }; } } public class MinGapTests : TestFixture { public void Assigned_IsKept() { CheckEqual(4, (Standard with { MinGap = 4 }).MinGap); } public void Zero_IsAccepted() { CheckEqual(0, (Standard with { MinGap = 0 }).MinGap); } public void Negative_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { MinGap = -1 }; } public void NaN_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { MinGap = double.NaN }; } } public class ThicknessTests : TestFixture { public void Assigned_IsKept() { CheckEqual(2, (Standard with { Thickness = 2 }).Thickness); } public void Zero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { Thickness = 0 }; } public void Negative_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { Thickness = -1 }; } public void NaN_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { Thickness = double.NaN }; } } public class InsetTests : TestFixture { public void Assigned_IsKept() { CheckEqual(2, (Standard with { Inset = 2 }).Inset); } public void Zero_IsAccepted() { CheckEqual(0, (Standard with { Inset = 0 }).Inset); } public void Negative_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { Inset = -1 }; } public void NaN_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); var unused = Standard with { Inset = double.NaN }; } } public class OuterTests : TestFixture { public void NoInset_IsTheRect() { Rect rect = RectAt(5, 7, 20, 10); CheckEqual(rect, Standard.Outer(rect)); } public void Inset_PullsEveryEdgeIn() { var material = Standard with { Inset = 2 }; CheckEqual(RectAt(7, 9, 16, 6), material.Outer(RectAt(5, 7, 20, 10))); } public void WidthBelowTwoThicknesses_IsNull() { var material = Standard with { Thickness = 2 }; CheckNull(material.Outer(RectAt(0, 0, 3, 10))); } public void HeightBelowTwoThicknesses_IsNull() { var material = Standard with { Thickness = 2 }; CheckNull(material.Outer(RectAt(0, 0, 10, 3))); } public void ExactlyTwoThicknesses_IsKept() { var material = Standard with { Thickness = 2 }; CheckEqual(RectAt(0, 0, 4, 4), material.Outer(RectAt(0, 0, 4, 4))); } public void InsetLeavingNoRoom_IsNull() { var material = Standard with { Inset = 5 }; CheckNull(material.Outer(RectAt(0, 0, 10, 10))); } } public class DashCountAlongTests : TestFixture { public void ZeroLength_IsZero() { CheckEqual(0, Standard.DashCountAlong(0)); } public void NegativeLength_IsZero() { CheckEqual(0, Standard.DashCountAlong(-4)); } public void ShorterThanADash_IsOne() { CheckEqual(1, Standard.DashCountAlong(2)); } public void RoomForOneDashButNotTwoWithTheGap_IsOne() { CheckEqual(1, Standard.DashCountAlong(7)); } public void ExactlyTwoDashesAndTheGap_IsTwo() { CheckEqual(2, Standard.DashCountAlong(8)); } public void WholePeriods_FitThatManyDashes() { CheckEqual(3, Standard.DashCountAlong(13)); } public void JustUnderThePeriodForAnotherDash_DoesNotAddOne() { CheckEqual(2, Standard.DashCountAlong(12)); } public void ZeroGap_PacksDashesEndToEnd() { var material = Standard with { MinGap = 0 }; CheckEqual(4, material.DashCountAlong(12)); } } public class DashLengthAlongTests : TestFixture { public void LoneDash_CoversTheWholeEdge() { CheckEqual(6, Standard.DashLengthAlong(6, count: 1)); } public void SeveralDashes_AreTheDashLength() { CheckEqual(3, Standard.DashLengthAlong(20, count: 4)); } } public class DashStartAlongTests : TestFixture { public void FirstDash_StartsAtZero() { CheckEqual(0, Standard.DashStartAlong(20, count: 4, index: 0)); } public void LastDash_EndsExactlyAtTheEdgesEnd() { CheckEqual(17, Standard.DashStartAlong(20, count: 4, index: 3)); } public void LastDashOnAFractionalEdge_StillEndsExactlyAtTheEnd() { CheckEqual(10.5, Standard.DashStartAlong(13.5, count: 3, index: 2)); } public void DashesBetween_AreSpreadEvenly() { CheckEqual(5, Standard.DashStartAlong(13, count: 3, index: 1)); } public void DashesBetween_RoundToWholeUnits() { CheckEqual(6, Standard.DashStartAlong(20, count: 4, index: 1)); CheckEqual(11, Standard.DashStartAlong(20, count: 4, index: 2)); } public void LoneDash_StartsAtZero() { CheckEqual(0, Standard.DashStartAlong(6, count: 1, index: 0)); } public void IndexBelowZero_StartsAtZero() { CheckEqual(0, Standard.DashStartAlong(20, count: 4, index: -1)); } public void IndexPastTheLast_IsTheLastStart() { CheckEqual(17, Standard.DashStartAlong(20, count: 4, index: 9)); } } /// /// The guarantees an edge's dashes make at every length, checked at every whole length so no size is missed. /// public class EdgeSpacingTests : TestFixture { public static readonly DashedFrameMaterial[] Materials = { Standard, Standard with { DashLength = 4, MinGap = 3 }, Standard with { DashLength = 1, MinGap = 1 }, Standard with { MinGap = 0 }, }; public static Scalar GapAfter(DashedFrameMaterial material, Scalar length, int count, int index) { Scalar end = material.DashStartAlong(length, count, index) + material.DashLengthAlong(length, count); return material.DashStartAlong(length, count, index + 1) - end; } public void EveryEdgeLongEnoughForADash_GetsOne() { foreach (DashedFrameMaterial material in Materials) { for (int length = 1; length <= 200; ++length) { Check(material.DashCountAlong(length) >= 1); } } } public void FirstDash_AlwaysStartsFlushWithTheCorner() { foreach (DashedFrameMaterial material in Materials) { for (int length = 1; length <= 200; ++length) { int count = material.DashCountAlong(length); CheckEqual(0, material.DashStartAlong(length, count, 0)); } } } public void LastDash_AlwaysEndsFlushWithTheCorner() { foreach (DashedFrameMaterial material in Materials) { for (int length = 1; length <= 200; ++length) { int count = material.DashCountAlong(length); Scalar end = material.DashStartAlong(length, count, count - 1) + material.DashLengthAlong(length, count); CheckEqual(length, end); } } } public void NoGap_IsNarrowerThanTheMinimum() { foreach (DashedFrameMaterial material in Materials) { for (int length = 1; length <= 200; ++length) { int count = material.DashCountAlong(length); for (int index = 0; index < count - 1; ++index) { Check(GapAfter(material, length, count, index) >= material.MinGap); } } } } public void GapsOfOneEdge_DifferByAtMostOneUnit() { foreach (DashedFrameMaterial material in Materials) { for (int length = 1; length <= 200; ++length) { int count = material.DashCountAlong(length); if (count < 3) { continue; } Scalar[] gaps = Enumerable.Range(0, count - 1) .Select(index => GapAfter(material, length, count, index)) .ToArray(); Check(gaps.Max() - gaps.Min() <= 1); } } } public void Dashes_NeverOverlapOneAnother() { foreach (DashedFrameMaterial material in Materials) { for (int length = 1; length <= 200; ++length) { int count = material.DashCountAlong(length); for (int index = 0; index < count - 1; ++index) { Check(GapAfter(material, length, count, index) >= 0); } } } } } public class HorizontalDashTests : TestFixture { private static readonly Rect Outer = RectAt(10, 20, 20, 10); public void TopDash_SitsOnTheTopEdgeFromTheStartOffset() { Rect dash = Standard.HorizontalDash(Outer, bottom: false, count: 4, index: 1); CheckEqual(RectAt(16, 20, 3, 1), dash); } public void BottomDash_SitsOnTheBottomEdge() { Rect dash = Standard.HorizontalDash(Outer, bottom: true, count: 4, index: 1); CheckEqual(RectAt(16, 29, 3, 1), dash); } public void LastDash_EndsAtTheRightEdge() { Rect dash = Standard.HorizontalDash(Outer, bottom: false, count: 4, index: 3); CheckEqual(30, dash.Right); } public void ThicknessSetsTheHeight() { var material = Standard with { Thickness = 2 }; CheckEqual(RectAt(10, 28, 3, 2), material.HorizontalDash(Outer, bottom: true, count: 4, index: 0)); } public void LoneDash_SpansTheWholeEdge() { Rect dash = Standard.HorizontalDash(RectAt(0, 0, 6, 10), bottom: false, count: 1, index: 0); CheckEqual(RectAt(0, 0, 6, 1), dash); } } public class VerticalSpanTests : TestFixture { public void ExcludesTheCornersTheHorizontalEdgesOwn() { CheckEqual(8, Standard.VerticalSpan(RectAt(0, 0, 20, 10))); } public void ThickerEdges_TakeMoreOffTheSpan() { var material = Standard with { Thickness = 2 }; CheckEqual(6, material.VerticalSpan(RectAt(0, 0, 20, 10))); } } public class VerticalDashTests : TestFixture { private static readonly Rect Outer = RectAt(10, 20, 20, 10); public void LeftDash_SitsBelowTheTopCornerOnTheLeftEdge() { Rect dash = Standard.VerticalDash(Outer, right: false, count: 2, index: 0); CheckEqual(RectAt(10, 21, 1, 3), dash); } public void RightDash_SitsOnTheRightEdge() { Rect dash = Standard.VerticalDash(Outer, right: true, count: 2, index: 0); CheckEqual(RectAt(29, 21, 1, 3), dash); } public void LastDash_EndsAboveTheBottomCorner() { Rect dash = Standard.VerticalDash(Outer, right: false, count: 2, index: 1); CheckEqual(29, dash.Bottom); } public void LoneDash_SpansTheWholeSpan() { Rect dash = Standard.VerticalDash(RectAt(0, 0, 10, 7), right: false, count: 1, index: 0); CheckEqual(RectAt(0, 1, 1, 5), dash); } public void EveryDash_StaysBetweenTheCornersTheHorizontalEdgesOwn() { Rect outer = RectAt(0, 0, 20, 10); DashedFrameMaterial material = Standard; int down = material.DashCountAlong(material.VerticalSpan(outer)); for (int index = 0; index < down; ++index) { Rect dash = material.VerticalDash(outer, right: false, down, index); Check(dash.Top >= outer.Top + material.Thickness); Check(dash.Bottom <= outer.Bottom - material.Thickness); } } } } }