using System; using System.Collections.Generic; using System.Linq; using UnitTestSharp; namespace Blacklight.Bezier.UnitTests { public class ShellTestSetTests : TestFixture { public class CtorTests : TestFixture { public void KeepsEveryArray() { var samples = new ShellSample[1]; var g = new double[1]; var references = new ReferenceResult[1]; var set = new ShellTestSet("Name", samples, g, references); CheckEqual("Name", set.Name); Check(ReferenceEquals(samples, set.Samples)); Check(ReferenceEquals(g, set.G)); Check(ReferenceEquals(references, set.References)); } } } public class ShellTestSetsTests : TestFixture { public static List Create(Func relativeOffset) { return ShellTestSets.Create(SampleSets.Create(samplesPerSet: 500, seed: 3), relativeOffset, seed: 3); } public class NearEdgeTests : TestFixture { public void StaysWithinATenthOfTheDistance() { var random = new Random(1); for (int i = 0; i < 2000; i++) { double offset = Math.Abs(ShellTestSets.NearEdge(random)); Check(offset >= 1e-9 * (1 - 1e-9) && offset <= 0.1 * (1 + 1e-9)); } } } public class TypicalTests : TestFixture { public void StaysWithinAFactorOfE() { var random = new Random(1); for (int i = 0; i < 2000; i++) { double ratio = 1 + ShellTestSets.Typical(random); Check(ratio >= Math.Exp(-1) * (1 - 1e-12) && ratio <= Math.E * (1 + 1e-12)); } } } public class IsOwnedTests : TestFixture { public static bool IsOwned(double x, double y) { ShellSample sample = ShellSample.FromPosition(x, y, double.PositiveInfinity); return ShellTestSets.IsOwned(sample, ReferenceDistance.Solve(sample)); } public void FootClearlyNearerThanTheVertex_IsOwned() { CheckTrue(IsOwned(3, 3)); } public void VertexNearest_IsNotOwned() { CheckFalse(IsOwned(0, -2)); } public void OnTheCurve_IsNotOwned() { CheckFalse(IsOwned(2, 2)); } public void FootAndVertexTiedWithinRounding_IsNotOwned() { CheckFalse(IsOwned(-1.139125502888478E-12, 1.0000000164633347)); } } public class CreateTests : TestFixture { public void KeepsOnlyOwnedPixelsOnTheUnboundedHalf() { foreach (ShellTestSet set in Create(ShellTestSets.NearEdge)) { Check(set.Samples.All(sample => double.IsPositiveInfinity(sample.End))); Check(set.Samples.Zip(set.References, ShellTestSets.IsOwned).All(owned => owned)); } } public void ThicknessSitsAtTheRequestedOffsetFromTheDistance() { foreach (ShellTestSet set in Create(random => 0.5)) { for (int i = 0; i < set.Samples.Length; i++) { double expected = 1.5 * set.References[i].Distance; CheckGreater(1e-12, Math.Abs(set.G[i] / (expected * expected) - 1)); } } } public void DropsThicknessesPastTheOverflowLimit() { Check(Create(random => 1e7).All(set => set.G.All(g => g <= ShellTestSets.LargestG))); } public void ReferencesMatchASolveOfEachSample() { ShellTestSet set = Create(ShellTestSets.NearEdge)[0]; for (int i = 0; i < set.Samples.Length; i++) { CheckEqual(ReferenceDistance.Solve(set.Samples[i]).Distance, set.References[i].Distance); } } public void KeepsTheSourceSetNames() { List sources = SampleSets.Create(samplesPerSet: 10, seed: 3); List shellSets = ShellTestSets.Create(sources, ShellTestSets.NearEdge, seed: 3); Check(sources.Select(set => set.Name).SequenceEqual(shellSets.Select(set => set.Name))); } } } public class ShellStatisticsTests : TestFixture { // Below the vertex at distance 2, with h = 2 + k 2^-22, the floor is 2^-22 + 2^-24 (2 + h): severity ~ k / 2. public static readonly ShellSample BelowVertex = ShellSample.FromPosition(0, -2, double.PositiveInfinity); public static readonly ReferenceResult BelowVertexReference = new ReferenceResult(2, 0); public static double ThicknessSquared(int steps) { double thickness = 2 + steps * Math.Pow(2, -22); return thickness * thickness; } public static ShellTestSet SetOf(params int[] steps) { return new ShellTestSet("Set", steps.Select(step => BelowVertex).ToArray(), steps.Select(ThicknessSquared).ToArray(), steps.Select(step => BelowVertexReference).ToArray()); } public class CtorTests : TestFixture { public void AssignsEveryField() { var statistics = new ShellStatistics(compared: 1, misclassified: 2, seriousMisses: 3, worstSeverity: 4, worstIndex: 5); CheckEqual(1, statistics.Compared); CheckEqual(2, statistics.Misclassified); CheckEqual(3, statistics.SeriousMisses); CheckEqual(4.0, statistics.WorstSeverity); CheckEqual(5, statistics.WorstIndex); } } public class SeverityTests : TestFixture { public void OnTheEdge_IsZero() { CheckEqual(0.0, ShellStatistics.Severity(BelowVertex, BelowVertexReference, 4)); } public void GrowsWithTheGapToTheEdge() { double near = ShellStatistics.Severity(BelowVertex, BelowVertexReference, ThicknessSquared(3)); double far = ShellStatistics.Severity(BelowVertex, BelowVertexReference, ThicknessSquared(30)); CheckGreater(1e-3, Math.Abs(far / near - 10)); } public void IsScaledByTheInputFloor() { double severity = ShellStatistics.Severity(BelowVertex, BelowVertexReference, ThicknessSquared(3)); CheckGreater(1e-3, Math.Abs(severity - 1.5)); } } public class MeasureTests : TestFixture { public void CorrectAnswers_AreNotMisses() { ShellStatistics statistics = ShellStatistics.Measure(new[] { 1f, 0f }, SetOf(3, -3)); CheckEqual(2, statistics.Compared); CheckEqual(0, statistics.Misclassified); CheckEqual(-1, statistics.WorstIndex); } public void WrongAnswers_CountWithTheirSeverity() { ShellStatistics statistics = ShellStatistics.Measure(new[] { 0f, 1f, 0f }, SetOf(3, -30, 30)); CheckEqual(3, statistics.Misclassified); CheckEqual(2, statistics.SeriousMisses); CheckEqual(1, statistics.WorstIndex); } public void NaN_CountsAsOutside() { CheckEqual(1, ShellStatistics.Measure(new[] { float.NaN }, SetOf(3)).Misclassified); CheckEqual(0, ShellStatistics.Measure(new[] { float.NaN }, SetOf(-3)).Misclassified); } public void ExtraReadbackPastTheSamples_IsIgnored() { CheckEqual(0, ShellStatistics.Measure(new[] { 1f, 0f, 0f, 0f }, SetOf(3)).Misclassified); } } } }