aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/src/Godosa.Core.Tests/Runs/RunsTests.cs
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
committergodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
commit39066900773e7857faf2d02e7ed51b71d219d97d (patch)
treeffab8e4ddd971626776c3b1f4ce1da2c186c2187 /src/Godosa.Core.Tests/Runs/RunsTests.cs
downloadgodosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.tar.gz
godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.zip
godosa-engine: initial public history
Diffstat (limited to 'src/Godosa.Core.Tests/Runs/RunsTests.cs')
-rw-r--r--src/Godosa.Core.Tests/Runs/RunsTests.cs149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/Godosa.Core.Tests/Runs/RunsTests.cs b/src/Godosa.Core.Tests/Runs/RunsTests.cs
new file mode 100644
index 0000000..1b6d6d4
--- /dev/null
+++ b/src/Godosa.Core.Tests/Runs/RunsTests.cs
@@ -0,0 +1,149 @@
+using Godosa.Core.Runs;
+using Xunit;
+
+namespace Godosa.Core.Tests.Runs;
+
+public class RunsTests
+{
+ private static RunFlags Parse(params string[] args)
+ {
+ var flags = new RunFlags();
+ for (var i = 0; i < args.Length; i++)
+ Assert.True(RunFlags.TryTake(args, ref i, ref flags), args[i]);
+ return flags;
+ }
+
+ [Fact]
+ public void Flags_DeterministicWithAndWithoutSeed()
+ {
+ Assert.Equal(new RunFlags(Deterministic: true, Seed: 7), Parse("--deterministic", "7"));
+ Assert.Equal(new RunFlags(Deterministic: true), Parse("--deterministic"));
+ Assert.Equal(1UL, Parse("--deterministic").SeedOr(1));
+ Assert.Equal(7UL, Parse("--deterministic", "7").SeedOr(1));
+ }
+
+ [Fact]
+ public void Flags_SeedNotTakenWhenNextIsAnOption()
+ {
+ string[] args = ["--deterministic", "--mute"];
+ var flags = new RunFlags();
+ var i = 0;
+ Assert.True(RunFlags.TryTake(args, ref i, ref flags));
+ Assert.Equal(0, i);
+ i = 1;
+ Assert.False(RunFlags.TryTake(args, ref i, ref flags));
+ }
+
+ [Fact]
+ public void Flags_DigestEveryAndHeadless()
+ {
+ Assert.Equal(new RunFlags(Digest: true), Parse("--digest"));
+ Assert.Equal(new RunFlags(Digest: true, DigestEvery: 60), Parse("--digest", "every=60"));
+ Assert.Equal(new RunFlags(Deterministic: true, Headless: true), Parse("--headless"));
+ Assert.Equal(new RunFlags(Deterministic: true, Seed: 3, Headless: true), Parse("--headless", "--deterministic", "3"));
+ }
+
+ [Fact]
+ public void Flags_BadDigestEvery_Throws() =>
+ Assert.Throws<ArgumentException>(() => Parse("--digest", "every=0"));
+
+ [Fact]
+ public void Clock_DeterministicFixedStep()
+ {
+ var clock = new FrameClock(true);
+ Assert.Equal(1.0 / 60, clock.Next(0.5));
+ Assert.Equal(1.0 / 60, clock.Next(0.001));
+ Assert.Equal(2, clock.Frame);
+ Assert.Equal(2.0 / 60, clock.Seconds, 12);
+ }
+
+ [Fact]
+ public void Clock_WallClamped()
+ {
+ var clock = new FrameClock(false);
+ Assert.Equal(0.1, clock.Next(0.5));
+ Assert.Equal(0.02, clock.Next(0.02));
+ Assert.Equal(0, clock.Next(-1));
+ Assert.Equal(3, clock.Frame);
+ }
+
+ [Fact]
+ public void Clock_Held_NoTimeNoFrame()
+ {
+ var clock = new FrameClock(true) { Held = true };
+ Assert.Equal(0, clock.Next(1.0 / 60));
+ Assert.Equal(0, clock.Frame);
+ Assert.Equal(0, clock.Seconds);
+ clock.Held = false;
+ clock.Next(0);
+ Assert.Equal(1, clock.Frame);
+ }
+
+ // Published FNV-1a 64 vectors (isthe.com/chongo/tech/comp/fnv).
+ [Theory]
+ [InlineData("", 0xcbf29ce484222325UL)]
+ [InlineData("a", 0xaf63dc4c8601ec8cUL)]
+ [InlineData("foobar", 0x85944171f73967e8UL)]
+ public void Digest_FnvVectors(string text, ulong expected)
+ {
+ var d = new StateDigest();
+ d.Add(text);
+ Assert.Equal(expected, d.Value);
+ Assert.Equal(expected, StateDigest.Of(System.Text.Encoding.UTF8.GetBytes(text)));
+ }
+
+ // Hand-checked: FNV-1a 64 of the little-endian bytes 01 00 00 00 and of 1.5 as an IEEE double.
+ [Fact]
+ public void Digest_NumbersLittleEndian()
+ {
+ var u = new StateDigest();
+ u.Add(1u);
+ Assert.Equal(0xad2aca7747985764UL, u.Value);
+ var d = new StateDigest();
+ d.Add(1.5);
+ Assert.Equal(0xaa95e93229a27c80UL, d.Value);
+ Assert.Equal("aa95e93229a27c80", d.Hex);
+ }
+
+ [Fact]
+ public void Line_Format() =>
+ Assert.Equal("digest 120 3.5 000000000000beef", DigestLine.Format(120, 3.5, 0xbeef));
+
+ [Fact]
+ public void Line_FormatRoundsGameTime() =>
+ Assert.Equal("digest 1 0.017 0000000000000001", DigestLine.Format(1, 1.0 / 60, 1));
+
+ [Fact]
+ public void Line_Due()
+ {
+ Assert.True(DigestLine.Due(60, 60));
+ Assert.False(DigestLine.Due(59, 60));
+ Assert.False(DigestLine.Due(0, 60));
+ Assert.False(DigestLine.Due(60, 0));
+ }
+
+ // SplitMix64 seeded 0: outputs e220a8397b1dcdaf, 6e789e6aa1b965f4 (published reference sequence).
+ [Fact]
+ public void Seeds_SplitMixReference()
+ {
+ Assert.Equal(0xe220a8397b1dcdafUL, SeedStreams.Of(0, 0));
+ Assert.Equal(0x6e789e6aa1b965f4UL, SeedStreams.Of(0, 1));
+ Assert.Equal(0x63cbe1e459320dd7UL, SeedStreams.Of(7, 0));
+ }
+
+ [Fact]
+ public void Loop_StopsWhenFrameReturnsFalse()
+ {
+ var seen = new List<long>();
+ var (frames, capped) = HeadlessLoop.Run(n => { seen.Add(n); return n < 3; }, null);
+ Assert.Equal([1L, 2, 3], seen);
+ Assert.Equal(3, frames);
+ Assert.False(capped);
+ }
+
+ [Fact]
+ public void Loop_FramesLimit() => Assert.Equal((5L, false), HeadlessLoop.Run(_ => true, 5));
+
+ [Fact]
+ public void Loop_Cap() => Assert.Equal((10L, true), HeadlessLoop.Run(_ => true, null, cap: 10));
+}