using System; namespace Prism.UnitTests { public class OklchTests : UnitTestSharp.TestFixture { private const double Tolerance = 1e-4; private const double ExactTolerance = 1e-9; public static bool Near(double expected, double actual) { return Math.Abs(expected - actual) < Tolerance; } public static bool NearExactly(double expected, double actual) { return Math.Abs(expected - actual) < ExactTolerance; } public class CtorTests : UnitTestSharp.TestFixture { public void AssignsAllFourParts() { var color = new Oklch(0.25, 0.5, 0.75, 0.125); CheckEqual(0.25, color.Lightness); CheckEqual(0.5, color.Chroma); CheckEqual(0.75, color.Hue); CheckEqual(0.125, color.Alpha); } public void WithoutAlpha_IsOpaque() { CheckEqual(1.0, new Oklch(0.5, 0.1, 0.25).Alpha); } public void HueOutsideZeroToOne_Wraps() { CheckEqual(0.25, new Oklch(0.5, 0.1, 1.25).Hue); CheckEqual(0.75, new Oklch(0.5, 0.1, -0.25).Hue); } public void LightnessAndAlphaOutsideWhatAColorShows_AreKept() { var color = new Oklch(1.5, 0.1, 0, 4); CheckEqual(1.5, color.Lightness); CheckEqual(4.0, color.Alpha); } public void NegativeChroma_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklch(0.5, -0.1, 0); } public void NaNChroma_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklch(0.5, double.NaN, 0); } public void InfiniteChroma_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklch(0.5, double.PositiveInfinity, 0); } public void NaNLightness_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklch(double.NaN, 0.1, 0); } public void NaNHue_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklch(0.5, 0.1, double.NaN); } public void InfiniteAlpha_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklch(0.5, 0.1, 0, double.PositiveInfinity); } } public class FromOklabTests : UnitTestSharp.TestFixture { public void PositiveA_IsHueZero() { Oklch color = Oklch.FromOklab(new Oklab(0.5, 0.1, 0)); CheckTrue(NearExactly(0.1, color.Chroma)); CheckEqual(0.0, color.Hue); } public void PositiveB_IsAQuarterOfTheWayRound() { CheckTrue(NearExactly(0.25, Oklch.FromOklab(new Oklab(0.5, 0, 0.1)).Hue)); } public void NegativeA_IsHalfwayRound() { CheckTrue(NearExactly(0.5, Oklch.FromOklab(new Oklab(0.5, -0.1, 0)).Hue)); } public void NegativeB_IsThreeQuartersOfTheWayRound() { CheckTrue(NearExactly(0.75, Oklch.FromOklab(new Oklab(0.5, 0, -0.1)).Hue)); } public void ChromaIsTheDistanceFromTheGrayAxis() { CheckTrue(NearExactly(0.5, Oklch.FromOklab(new Oklab(0.5, 0.3, 0.4)).Chroma)); } public void LightnessAndAlphaAreCarriedOver() { Oklch color = Oklch.FromOklab(new Oklab(0.25, 0.3, 0.4, 0.5)); CheckEqual(0.25, color.Lightness); CheckEqual(0.5, color.Alpha); } public void AColorTooCloseToTheAxis_IsAGrayWithHueZero() { CheckEqual(new Oklch(0.5, 0, 0), Oklch.FromOklab(new Oklab(0.5, 1e-8, 1e-8))); } public void AColorJustPastTheThreshold_KeepsItsChromaAndHue() { Oklch color = Oklch.FromOklab(new Oklab(0.5, 1e-4, 0)); CheckTrue(NearExactly(1e-4, color.Chroma)); CheckEqual(0.0, color.Hue); } } public class FromColorTests : UnitTestSharp.TestFixture { public void Red_MatchesTheReferenceValues() { Oklch red = Oklch.FromColor(Color.FromArgb(255, 0, 0)); CheckTrue(Near(0.62796, red.Lightness)); CheckTrue(Near(0.25768, red.Chroma)); CheckTrue(Near(0.08121, red.Hue)); } public void Green_MatchesTheReferenceValues() { Oklch green = Oklch.FromColor(Color.FromArgb(0, 255, 0)); CheckTrue(Near(0.86644, green.Lightness)); CheckTrue(Near(0.29483, green.Chroma)); CheckTrue(Near(0.39582, green.Hue)); } public void Blue_MatchesTheReferenceValues() { Oklch blue = Oklch.FromColor(Color.FromArgb(0, 0, 255)); CheckTrue(Near(0.45201, blue.Lightness)); CheckTrue(Near(0.31321, blue.Chroma)); CheckTrue(Near(0.73348, blue.Hue)); } public void White_HasNoChromaOrHue() { Oklch white = Oklch.FromColor(Color.FromArgb(255, 255, 255)); CheckEqual(0.0, white.Chroma); CheckEqual(0.0, white.Hue); } public void Gray_HasNoChromaOrHue() { Oklch gray = Oklch.FromColor(Color.FromArgb(128, 128, 128)); CheckEqual(0.0, gray.Chroma); CheckEqual(0.0, gray.Hue); } public void Black_IsAllZero() { CheckEqual(new Oklch(0, 0, 0), Oklch.FromColor(Color.FromArgb(0, 0, 0))); } } public class ToOklabTests : UnitTestSharp.TestFixture { public void HueZero_PutsAllTheChromaOnA() { CheckEqual(new Oklab(0.5, 0.5, 0), new Oklch(0.5, 0.5, 0).ToOklab()); } public void AQuarterOfTheWayRound_PutsAllTheChromaOnB() { Oklab color = new Oklch(0.5, 0.5, 0.25).ToOklab(); CheckTrue(NearExactly(0, color.A)); CheckTrue(NearExactly(0.5, color.B)); } public void HalfwayRound_PutsAllTheChromaOnNegativeA() { Oklab color = new Oklch(0.5, 0.5, 0.5).ToOklab(); CheckTrue(NearExactly(-0.5, color.A)); CheckTrue(NearExactly(0, color.B)); } public void LightnessAndAlphaAreCarriedOver() { Oklab color = new Oklch(0.25, 0, 0, 0.5).ToOklab(); CheckEqual(0.25, color.Lightness); CheckEqual(0.5, color.Alpha); } } public class ToSrgbTests : UnitTestSharp.TestFixture { public void IsTheOklabColorEncoded() { var color = new Oklch(0.6, 0.05, 0.3, 0.5); CheckEqual(color.ToOklab().ToSrgb(), color.ToSrgb()); } } public class ToColorTests : UnitTestSharp.TestFixture { public void UndoesFromColor_ForAnOrange() { CheckEqual(Color.FromArgb(255, 165, 0), Oklch.FromColor(Color.FromArgb(255, 165, 0)).ToColor()); } public void UndoesFromColor_ForAColorWithAlpha() { Color original = Color.FromArgb(77, 12, 34, 56); CheckEqual(original, Oklch.FromColor(original).ToColor()); } public void UndoesFromColor_ForAColorWithBlueTheLargest() { Color original = Color.FromArgb(200, 40, 220); CheckEqual(original, Oklch.FromColor(original).ToColor()); } public void UndoesFromColor_ForAGray() { CheckEqual(Color.FromArgb(128, 128, 128), Oklch.FromColor(Color.FromArgb(128, 128, 128)).ToColor()); } public void AChromaBeyondTheGamut_ClampsTheChannel() { CheckEqual((byte)255, new Oklch(1, 0.3, 0).ToColor().Red); } } public class LerpTests : UnitTestSharp.TestFixture { public void AtZero_IsTheStart() { var from = new Oklch(0.25, 0.5, 0.25, 1); CheckEqual(from, Oklch.Lerp(from, new Oklch(0.75, 0.25, 0.5, 0), 0)); } public void AtOne_IsTheEnd() { var to = new Oklch(0.75, 0.25, 0.5, 0.5); CheckEqual(to, Oklch.Lerp(new Oklch(0.25, 0.5, 0.25, 1), to, 1)); } public void BlendsLightnessAndChromaLinearly() { Oklch blended = Oklch.Lerp(new Oklch(0.25, 0.25, 0.25, 1), new Oklch(0.75, 0.75, 0.25, 1), 0.5); CheckEqual(new Oklch(0.5, 0.5, 0.25, 1), blended); } public void AlphaBlendsLinearly_LeavingTheSameColorAlone() { Oklch blended = Oklch.Lerp( new Oklch(0.5, 0.25, 0.25, 0.25), new Oklch(0.5, 0.25, 0.25, 0.75), 0.25); CheckEqual(new Oklch(0.5, 0.25, 0.25, 0.375), blended); } public void ATransparentStart_AddsNoColor() { Oklch blended = Oklch.Lerp( new Oklch(0.25, 0.25, 0.25, 0), new Oklch(0.75, 0.75, 0.5, 1), 0.5); CheckEqual(new Oklch(0.75, 0.75, 0.5, 0.5), blended); } public void ATransparentEnd_AddsNoColor() { Oklch blended = Oklch.Lerp( new Oklch(0.25, 0.25, 0.25, 1), new Oklch(0.75, 0.75, 0.5, 0), 0.5); CheckEqual(new Oklch(0.25, 0.25, 0.25, 0.5), blended); } public void BothTransparent_BlendTheColorsLinearly() { Oklch blended = Oklch.Lerp( new Oklch(0.25, 0.25, 0.25, 0), new Oklch(0.75, 0.75, 0.5, 0), 0.5); CheckEqual(new Oklch(0.5, 0.5, 0.375, 0), blended); } public void UnequalAlphas_WeighEachColorByItsAlpha() { Oklch blended = Oklch.Lerp( new Oklch(0.25, 0.25, 0.25, 0.25), new Oklch(0.75, 0.75, 0.5, 0.75), 0.5); CheckEqual(new Oklch(0.625, 0.625, 0.4375, 0.5), blended); } public void ANegativeAlpha_CountsAsTransparent() { Oklch blended = Oklch.Lerp( new Oklch(0.25, 0.25, 0.25, -1), new Oklch(0.75, 0.75, 0.5, 1), 0.5); CheckEqual(new Oklch(0.75, 0.75, 0.5, 0), blended); } public void AmountBelowZero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Oklch.Lerp(new Oklch(0.5, 0.1, 0), new Oklch(0.5, 0.1, 0.5), -0.1); } public void AmountAboveOne_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Oklch.Lerp(new Oklch(0.5, 0.1, 0), new Oklch(0.5, 0.1, 0.5), 1.1); } public void NaNAmount_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Oklch.Lerp(new Oklch(0.5, 0.1, 0), new Oklch(0.5, 0.1, 0.5), double.NaN); } public class DirectionTests : UnitTestSharp.TestFixture { public void WithoutADirection_TakesTheShorterWay() { CheckEqual(0.0, Oklch.Lerp(new Oklch(0.5, 0.1, 0.875), new Oklch(0.5, 0.1, 0.125), 0.5).Hue); } public void Longest_TakesTheLongerArc() { CheckEqual( 0.875, Oklch.Lerp(new Oklch(0.5, 0.1, 0.25), new Oklch(0.5, 0.1, 0.5), 0.5, HueDirection.Longest).Hue); } public void Increasing_WrapsPastOneToReachALowerHue() { CheckEqual( 0.0, Oklch.Lerp( new Oklch(0.5, 0.1, 0.75), new Oklch(0.5, 0.1, 0.25), 0.5, HueDirection.Increasing).Hue); } public void Decreasing_WrapsPastZeroToReachAHigherHue() { CheckEqual( 0.0, Oklch.Lerp( new Oklch(0.5, 0.1, 0.25), new Oklch(0.5, 0.1, 0.75), 0.5, HueDirection.Decreasing).Hue); } public void AnUnknownDirection_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Oklch.Lerp(new Oklch(0.5, 0.1, 0), new Oklch(0.5, 0.1, 0.5), 0.5, (HueDirection)99); } } public class GraysTests : UnitTestSharp.TestFixture { public void AGrayStart_TakesTheEndsHue() { CheckEqual( new Oklch(0.5, 0.125, 0.25), Oklch.Lerp(new Oklch(0.5, 0, 0.5), new Oklch(0.5, 0.25, 0.25), 0.5)); } public void AGrayEnd_KeepsTheStartsHue() { CheckEqual( new Oklch(0.5, 0.125, 0.25), Oklch.Lerp(new Oklch(0.5, 0.25, 0.25), new Oklch(0.5, 0, 0.5), 0.5)); } public void TwoGrays_KeepTheStartsHue() { CheckEqual( new Oklch(0.75, 0, 0.25), Oklch.Lerp(new Oklch(0.5, 0, 0.25), new Oklch(1, 0, 0.75), 0.5)); } } } public class EqualityTests : UnitTestSharp.TestFixture { public void SameParts_AreEqual() { CheckTrue(new Oklch(0.1, 0.2, 0.3, 0.4).Equals(new Oklch(0.1, 0.2, 0.3, 0.4))); CheckTrue(new Oklch(0.1, 0.2, 0.3, 0.4) == new Oklch(0.1, 0.2, 0.3, 0.4)); CheckFalse(new Oklch(0.1, 0.2, 0.3, 0.4) != new Oklch(0.1, 0.2, 0.3, 0.4)); } public void AnyDifferentPart_IsNotEqual() { var baseColor = new Oklch(0.1, 0.2, 0.3, 0.4); CheckFalse(baseColor.Equals(new Oklch(0.9, 0.2, 0.3, 0.4))); CheckFalse(baseColor.Equals(new Oklch(0.1, 0.9, 0.3, 0.4))); CheckFalse(baseColor.Equals(new Oklch(0.1, 0.2, 0.9, 0.4))); CheckFalse(baseColor.Equals(new Oklch(0.1, 0.2, 0.3, 0.9))); CheckTrue(baseColor != new Oklch(0.1, 0.2, 0.3, 0.9)); } public void BoxedColor_IsEqualToTheSameColor() { CheckTrue(new Oklch(0.1, 0.2, 0.3).Equals((object)new Oklch(0.1, 0.2, 0.3))); } public void ANonOklch_IsNotEqual() { CheckFalse(new Oklch(0.1, 0.2, 0.3).Equals("color")); } public void EqualColors_HaveTheSameHashCode() { CheckEqual(new Oklch(0.1, 0.2, 0.3, 0.4).GetHashCode(), new Oklch(0.1, 0.2, 0.3, 0.4).GetHashCode()); } } } }