using System.Numerics; using AcDream.Core.World; using Xunit; namespace AcDream.Core.Tests.World; public sealed class SkyStateTests { [Fact] public void Default_Has4Keyframes() { var sky = SkyStateProvider.Default(); Assert.Equal(4, sky.KeyframeCount); } [Fact] public void Interpolate_AtExactKeyframe_ReturnsThatFrameData() { var sky = SkyStateProvider.Default(); var noon = sky.Interpolate(0.5f); // noon keyframe // Noon sky color should be near white (1.0 ish). Assert.InRange(noon.SunColor.X, 0.9f, 1.1f); Assert.InRange(noon.SunColor.Y, 0.9f, 1.1f); } [Fact] public void Interpolate_BetweenKeyframes_LerpsColors() { var sky = SkyStateProvider.Default(); var dawn = sky.Interpolate(0.25f); var noon = sky.Interpolate(0.5f); var midPt = sky.Interpolate(0.375f); // Midpoint should fall between dawn & noon for sun color Y (green channel). float low = System.Math.Min(dawn.SunColor.Y, noon.SunColor.Y); float high = System.Math.Max(dawn.SunColor.Y, noon.SunColor.Y); Assert.InRange(midPt.SunColor.Y, low, high); } [Fact] public void Interpolate_Wraps_AcrossMidnight() { var sky = SkyStateProvider.Default(); var justAfterMidnight = sky.Interpolate(0.01f); // Should return finite valid state (not NaN). Assert.False(float.IsNaN(justAfterMidnight.SunColor.X)); Assert.False(float.IsNaN(justAfterMidnight.AmbientColor.X)); } [Fact] public void SunDirectionFromKeyframe_ReturnsUnitVector() { var kf = new SkyKeyframe( Begin: 0.5f, SunHeadingDeg: 180f, // south SunPitchDeg: 70f, SunColor: Vector3.One, AmbientColor: Vector3.One, FogColor: Vector3.One, FogDensity: 0.001f); var dir = SkyStateProvider.SunDirectionFromKeyframe(kf); Assert.InRange(dir.Length(), 0.99f, 1.01f); } [Fact] public void WorldTimeService_SyncFromServer_SetsTicks() { var service = new WorldTimeService(SkyStateProvider.Default()); service.SyncFromServer(12345.0); // NowTicks advances by real elapsed time; but immediately after // sync it should be at or very close to the synced value. Assert.InRange(service.NowTicks, 12345.0, 12346.0); } [Fact] public void WorldTimeService_DayFraction_RespectsSync() { var service = new WorldTimeService(SkyStateProvider.Default()); // Need to aim for dayFraction 0.5 (Gloaming-and-Half, slot 15 since tick 0 = slot 7). // Sync to (0.5 - 7/16) * DayTicks = (1/16) * DayTicks — 1 slot past Morntide-and-Half = Midsong. // Actually simpler: target fraction 7/16 (slot 7 = Morntide-and-Half) by syncing to tick 0. service.SyncFromServer(0); Assert.InRange(service.DayFraction, 0.43, 0.44); // 7/16 = 0.4375 Assert.True(service.IsDaytime); } }