acdream/tests/AcDream.App.Tests/World/WorldEnvironmentControllerTests.cs
Erik d09e246d3a refactor(world): own live environment state
Move the DAT sky, selected day group, world clock, weather, AdminEnvirons bridge, and debug cycles into a one-shot WorldEnvironmentController while preserving GameWindow's public aliases and accepted startup/session/render order. Correct the named retail citations and register the remaining environment audio and fog/radar gaps.

Co-authored-by: Codex <codex@openai.com>
2026-07-22 10:54:33 +02:00

187 lines
6.7 KiB
C#

using System.Numerics;
using AcDream.App.World;
using AcDream.Core.World;
namespace AcDream.App.Tests.World;
[Collection(WorldEnvironmentControllerCollection.Name)]
public sealed class WorldEnvironmentControllerTests
{
[Fact]
public void Initialize_InstallsDatDayGroupAndSeedsOfflineNoon()
{
List<string> log = [];
var controller = CreateController(log);
LoadedSkyDesc sky = SingleGroupSky("Sunny", begin: 0f);
controller.Initialize(sky, zeroTimeOfYear: null);
Assert.Same(sky.DayGroups[0], controller.ActiveDayGroup);
Assert.Equal(WeatherKind.Clear, controller.Weather.Kind);
Assert.Equal(1.0, controller.WorldTime.TickSize);
Assert.InRange(controller.DayFraction, 0.499f, 0.501f);
Assert.Contains(log, line => line.Contains("loaded Region 0x13000000", StringComparison.Ordinal));
Assert.Contains(log, line => line.Contains("DayGroup[0] \"Sunny\"", StringComparison.Ordinal));
}
[Fact]
public void SynchronizeFromServer_RefreshesAtMostOnceWithinSameDay()
{
List<string> log = [];
var controller = CreateController(log);
controller.Initialize(SingleGroupSky("Cloudy", begin: 0f), zeroTimeOfYear: null);
controller.SynchronizeFromServer(100_000.0);
int selectedAfterFirstSync = log.Count(IsDayGroupSelection);
controller.SynchronizeFromServer(100_000.0);
Assert.Equal(selectedAfterFirstSync, log.Count(IsDayGroupSelection));
Assert.Equal(WeatherKind.Overcast, controller.Weather.Kind);
}
[Fact]
public void Initialize_IsOneShotAndRetainsFirstPublishedEnvironment()
{
var controller = CreateController([]);
LoadedSkyDesc first = SingleGroupSky("Sunny", begin: 0f);
controller.Initialize(first, zeroTimeOfYear: null);
InvalidOperationException error = Assert.Throws<InvalidOperationException>(
() => controller.Initialize(
SingleGroupSky("Cloudy", begin: 0f),
zeroTimeOfYear: 3600.0));
Assert.Contains("one-shot", error.Message, StringComparison.Ordinal);
Assert.Same(first.DayGroups[0], controller.ActiveDayGroup);
Assert.Equal(WeatherKind.Clear, controller.Weather.Kind);
}
[Fact]
public void Initialize_WithoutGameTimeRestoresDocumentedFallbackOrigin()
{
double previous = DerethDateTime.OriginOffsetTicks;
try
{
DerethDateTime.SetOriginOffsetFromDat(3600.0);
var controller = CreateController([]);
controller.Initialize(SingleGroupSky("Sunny", begin: 0f), zeroTimeOfYear: null);
Assert.Equal(
DerethDateTime.DayFractionOriginOffsetTicks,
DerethDateTime.OriginOffsetTicks);
Assert.InRange(controller.DayFraction, 0.499f, 0.501f);
}
finally
{
DerethDateTime.SetOriginOffsetFromDat(previous);
}
}
[Theory]
[InlineData(0x00u, EnvironOverride.None)]
[InlineData(0x01u, EnvironOverride.RedFog)]
[InlineData(0x06u, EnvironOverride.BlackFog2)]
public void ApplyAdminEnvirons_FogValuesSetStickyOverride(
uint raw,
EnvironOverride expected)
{
List<string> log = [];
var controller = CreateController(log);
controller.ApplyAdminEnvirons(raw);
Assert.Equal(expected, controller.Weather.Override);
Assert.Contains(log, line => line.Contains(expected.ToString(), StringComparison.Ordinal));
}
[Fact]
public void ApplyAdminEnvirons_SoundValuePreservesWeatherAndLogsRetailName()
{
List<string> log = [];
var controller = CreateController(log);
controller.Weather.Override = EnvironOverride.GreenFog;
controller.ApplyAdminEnvirons(0x78u);
Assert.Equal(EnvironOverride.GreenFog, controller.Weather.Override);
Assert.Contains(log, line => line.Contains("Thunder3Sound", StringComparison.Ordinal));
}
[Fact]
public void CycleTimeOfDay_PreservesAcceptedFiveStepSequence()
{
var controller = CreateController([]);
Assert.Equal("Time override = 0.00", controller.CycleTimeOfDay());
Assert.Equal(0f, controller.DayFraction);
Assert.Equal("Time override = 0.25", controller.CycleTimeOfDay());
Assert.Equal(0.25f, controller.DayFraction);
Assert.Equal("Time override = 0.50", controller.CycleTimeOfDay());
Assert.Equal(0.5f, controller.DayFraction);
Assert.Equal("Time override = 0.75", controller.CycleTimeOfDay());
Assert.Equal(0.75f, controller.DayFraction);
Assert.Equal("Time override cleared", controller.CycleTimeOfDay());
}
[Fact]
public void CycleWeather_PreservesAcceptedOrder()
{
var controller = CreateController([]);
Assert.Equal("Weather = Overcast", controller.CycleWeather());
Assert.Equal(WeatherKind.Overcast, controller.Weather.Kind);
Assert.Equal("Weather = Rain", controller.CycleWeather());
Assert.Equal(WeatherKind.Rain, controller.Weather.Kind);
Assert.Equal("Weather = Snow", controller.CycleWeather());
Assert.Equal(WeatherKind.Snow, controller.Weather.Kind);
Assert.Equal("Weather = Storm", controller.CycleWeather());
Assert.Equal(WeatherKind.Storm, controller.Weather.Kind);
Assert.Equal("Weather = Clear", controller.CycleWeather());
Assert.Equal(WeatherKind.Clear, controller.Weather.Kind);
}
private static WorldEnvironmentController CreateController(List<string> log) =>
new(
new WorldTimeService(SkyStateProvider.Default()),
new WeatherSystem(),
log.Add);
private static LoadedSkyDesc SingleGroupSky(string name, float begin)
{
var keyframe = new SkyKeyframe(
Begin: begin,
SunHeadingDeg: 0f,
SunPitchDeg: 90f,
DirColor: Vector3.One,
DirBright: 1f,
AmbColor: Vector3.One,
AmbBright: 1f,
FogColor: Vector3.Zero,
FogDensity: 0f);
var group = new DayGroupData
{
ChanceOfOccur = 100f,
Name = name,
SkyTimes =
[
new DatSkyKeyframeData { Keyframe = keyframe },
],
};
return new LoadedSkyDesc
{
TickSize = 0.8,
LightTickSize = 0.2,
DayGroups = [group],
};
}
private static bool IsDayGroupSelection(string line) =>
line.Contains("→ DayGroup[", StringComparison.Ordinal);
}
[CollectionDefinition(Name, DisableParallelization = true)]
public sealed class WorldEnvironmentControllerCollection
{
public const string Name = "World environment process-global calendar";
}