using System.Collections.Generic;
using AcDream.App;
namespace AcDream.App.Tests;
///
/// Unit tests for startup-time parsing.
/// Behavior-preservation only: every assertion locks in the exact
/// boolean / numeric / nullability semantics from the env reads that
/// previously lived in GameWindow.cs.
///
public sealed class RuntimeOptionsTests
{
private const string AnyDatDir = "C:/Users/test/dats";
private static Func Env(Dictionary values)
=> name => values.TryGetValue(name, out var v) ? v : null;
private static Func EmptyEnv() => _ => null;
[Fact]
public void Defaults_AllSafeOff_WhenEnvironmentIsEmpty()
{
var opts = RuntimeOptions.Parse(AnyDatDir, EmptyEnv());
Assert.Equal(AnyDatDir, opts.DatDir);
Assert.False(opts.LiveMode);
Assert.Equal("127.0.0.1", opts.LiveHost);
Assert.Equal(9000, opts.LivePort);
Assert.Null(opts.LiveUser);
Assert.Null(opts.LivePass);
Assert.False(opts.DevTools);
Assert.False(opts.DumpMoveTruth);
Assert.False(opts.NoAudio);
Assert.False(opts.EnableSkyPesDebug);
Assert.Equal(-1, opts.HidePartIndex);
// Default-on: RetailCloseDegrades is true unless explicitly disabled.
Assert.True(opts.RetailCloseDegrades);
Assert.False(opts.DumpSceneryZ);
Assert.Null(opts.LegacyStreamRadius);
Assert.False(opts.HasLiveCredentials);
}
[Fact]
public void LiveMode_Set_ExactlyByValue1()
{
Assert.True(RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_LIVE"] = "1" })).LiveMode);
Assert.False(RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_LIVE"] = "0" })).LiveMode);
Assert.False(RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_LIVE"] = "true" })).LiveMode);
Assert.False(RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_LIVE"] = "" })).LiveMode);
}
[Fact]
public void LiveHostAndPort_FallBackToDefaults_WhenUnsetOrInvalid()
{
var withDefaults = RuntimeOptions.Parse(AnyDatDir, EmptyEnv());
Assert.Equal("127.0.0.1", withDefaults.LiveHost);
Assert.Equal(9000, withDefaults.LivePort);
var withOverrides = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_TEST_HOST"] = "play.example.com",
["ACDREAM_TEST_PORT"] = "9123",
}));
Assert.Equal("play.example.com", withOverrides.LiveHost);
Assert.Equal(9123, withOverrides.LivePort);
// Non-numeric port falls back to default; we don't throw at parse time.
var withBadPort = RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_TEST_PORT"] = "abc" }));
Assert.Equal(9000, withBadPort.LivePort);
}
[Fact]
public void LiveUserPass_NullWhenEmptyOrUnset()
{
var emptyValues = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_TEST_USER"] = "",
["ACDREAM_TEST_PASS"] = "",
}));
Assert.Null(emptyValues.LiveUser);
Assert.Null(emptyValues.LivePass);
var realValues = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_TEST_USER"] = "testaccount",
["ACDREAM_TEST_PASS"] = "testpassword",
}));
Assert.Equal("testaccount", realValues.LiveUser);
Assert.Equal("testpassword", realValues.LivePass);
}
[Fact]
public void HasLiveCredentials_RequiresLiveModeAndBothUserAndPass()
{
// Live mode off → no credentials regardless of user/pass.
var noLive = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_TEST_USER"] = "u",
["ACDREAM_TEST_PASS"] = "p",
}));
Assert.False(noLive.HasLiveCredentials);
// Live mode on but missing user → no credentials.
var missingUser = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_LIVE"] = "1",
["ACDREAM_TEST_PASS"] = "p",
}));
Assert.False(missingUser.HasLiveCredentials);
// Live mode on but missing pass → no credentials.
var missingPass = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_LIVE"] = "1",
["ACDREAM_TEST_USER"] = "u",
}));
Assert.False(missingPass.HasLiveCredentials);
// All three present → credentials available.
var ok = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_LIVE"] = "1",
["ACDREAM_TEST_USER"] = "u",
["ACDREAM_TEST_PASS"] = "p",
}));
Assert.True(ok.HasLiveCredentials);
}
[Fact]
public void HidePartIndex_MinusOneWhenUnset_ParsesIntegers()
{
Assert.Equal(-1, RuntimeOptions.Parse(AnyDatDir, EmptyEnv()).HidePartIndex);
Assert.Equal(7, RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_HIDE_PART"] = "7" })).HidePartIndex);
// Invalid → fall back to -1 (preserves the int.TryParse failure semantics).
Assert.Equal(-1, RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_HIDE_PART"] = "abc" })).HidePartIndex);
}
[Fact]
public void RetailCloseDegrades_DefaultOn_ExceptWhenValueIsExactlyZero()
{
// Unset → on.
Assert.True(RuntimeOptions.Parse(AnyDatDir, EmptyEnv()).RetailCloseDegrades);
// Exactly "0" → off. Matches the pre-refactor semantics:
// !string.Equals(env, "0", StringComparison.Ordinal)
Assert.False(RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_RETAIL_CLOSE_DEGRADES"] = "0",
})).RetailCloseDegrades);
// Any other value → on (including "1", "false", "True"). The original
// code only checked for the literal "0"; preserve that.
Assert.True(RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_RETAIL_CLOSE_DEGRADES"] = "1",
})).RetailCloseDegrades);
Assert.True(RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_RETAIL_CLOSE_DEGRADES"] = "false",
})).RetailCloseDegrades);
}
[Fact]
public void LegacyStreamRadius_NullWhenUnsetOrInvalid_ParsesNonNegativeIntegers()
{
Assert.Null(RuntimeOptions.Parse(AnyDatDir, EmptyEnv()).LegacyStreamRadius);
Assert.Null(RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_STREAM_RADIUS"] = "abc" })).LegacyStreamRadius);
// Negative values are filtered out by the pre-refactor `sr >= 0` guard.
Assert.Null(RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_STREAM_RADIUS"] = "-3" })).LegacyStreamRadius);
Assert.Equal(0, RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_STREAM_RADIUS"] = "0" })).LegacyStreamRadius);
Assert.Equal(5, RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_STREAM_RADIUS"] = "5" })).LegacyStreamRadius);
Assert.Equal(12, RuntimeOptions.Parse(AnyDatDir, Env(new() { ["ACDREAM_STREAM_RADIUS"] = "12" })).LegacyStreamRadius);
}
[Fact]
public void DiagnosticFlags_RespectExactValueOne()
{
var allOn = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_DEVTOOLS"] = "1",
["ACDREAM_DUMP_MOVE_TRUTH"] = "1",
["ACDREAM_NO_AUDIO"] = "1",
["ACDREAM_ENABLE_SKY_PES"] = "1",
["ACDREAM_DUMP_SCENERY_Z"] = "1",
}));
Assert.True(allOn.DevTools);
Assert.True(allOn.DumpMoveTruth);
Assert.True(allOn.NoAudio);
Assert.True(allOn.EnableSkyPesDebug);
Assert.True(allOn.DumpSceneryZ);
// Any non-"1" value leaves them off, matching the
// string.Equals(env, "1", StringComparison.Ordinal) check.
var anyOther = RuntimeOptions.Parse(AnyDatDir, Env(new()
{
["ACDREAM_DEVTOOLS"] = "true",
["ACDREAM_DUMP_MOVE_TRUTH"] = "yes",
["ACDREAM_NO_AUDIO"] = "2",
["ACDREAM_ENABLE_SKY_PES"] = "on",
["ACDREAM_DUMP_SCENERY_Z"] = " 1",
}));
Assert.False(anyOther.DevTools);
Assert.False(anyOther.DumpMoveTruth);
Assert.False(anyOther.NoAudio);
Assert.False(anyOther.EnableSkyPesDebug);
Assert.False(anyOther.DumpSceneryZ);
}
[Fact]
public void Parse_RejectsNullDatDirOrEnv()
{
Assert.Throws(() => RuntimeOptions.Parse(null!, EmptyEnv()));
Assert.Throws(() => RuntimeOptions.Parse(AnyDatDir, null!));
}
}