namespace AcDream.Runtime.Tests; public sealed class GameRuntimeContractTests { [Fact] public void InstanceClock_NormalizesHostTimeAndCanFreezeSimulation() { var first = new GameRuntimeClock(); var second = new GameRuntimeClock(); RuntimeFrameTime frame1 = first.Advance(0.25); RuntimeFrameTime frozen = first.Advance(0.5, advanceSimulationTime: false); RuntimeFrameTime invalid = first.Advance(double.NaN); Assert.Equal(1UL, frame1.FrameNumber); Assert.Equal(0.25, frame1.DeltaSeconds); Assert.Equal(0.25, frame1.SimulationTimeSeconds); Assert.Equal(2UL, frozen.FrameNumber); Assert.Equal(0.5, frozen.DeltaSeconds); Assert.Equal(0.25, frozen.SimulationTimeSeconds); Assert.Equal(3UL, invalid.FrameNumber); Assert.Equal(0.0, invalid.DeltaSeconds); Assert.Equal(0.25, invalid.SimulationTimeSeconds); Assert.Equal(0UL, second.FrameNumber); Assert.Equal(0.0, second.SimulationTimeSeconds); } [Theory] [InlineData(double.NegativeInfinity)] [InlineData(double.PositiveInfinity)] [InlineData(double.NaN)] [InlineData(-1.0)] [InlineData(0.0)] public void InstanceClock_InvalidDeltaNormalizesToZero(double input) { Assert.Equal(0.0, GameRuntimeClock.NormalizeDeltaSeconds(input)); } [Fact] public void GenerationAndTeardownAcknowledgementAreExact() { RuntimeGenerationToken first = RuntimeGenerationToken.Initial.Next(); var complete = new RuntimeTeardownAcknowledgement( first, first.Next(), RuntimeCommandStatus.Accepted, RuntimeTeardownStage.Complete); var stale = complete with { Status = RuntimeCommandStatus.StaleGeneration, }; var partial = complete with { CompletedStages = RuntimeTeardownStage.CommandsInert | RuntimeTeardownStage.InboundDetached, }; Assert.Equal(1UL, first.Value); Assert.True(complete.IsComplete); Assert.False(stale.IsComplete); Assert.False(partial.IsComplete); } [Fact] public void EventSequencersAreMonotonicAndInstanceScoped() { var first = new RuntimeEventSequencer(); var second = new RuntimeEventSequencer(); var generation = new RuntimeGenerationToken(7); RuntimeEventStamp one = first.Next(generation, frameNumber: 11); RuntimeEventStamp two = first.Next(generation, frameNumber: 12); RuntimeEventStamp independent = second.Next(generation, frameNumber: 99); Assert.Equal(1UL, one.Sequence); Assert.Equal(2UL, two.Sequence); Assert.Equal(1UL, independent.Sequence); Assert.Equal(12UL, two.FrameNumber); Assert.Equal(99UL, independent.FrameNumber); } [Fact] public void TraceRecorderNormalizesTypedDeltasWithoutRetainingOwners() { var recorder = new RuntimeTraceRecorder(); var stamp = new RuntimeEventStamp( new RuntimeGenerationToken(3), Sequence: 1, FrameNumber: 8); var command = new RuntimeCommandDelta( stamp, RuntimeCommandDomain.Selection, (int)RuntimeSelectionCommand.SelectClosestHostile, RuntimeCommandStatus.Accepted, PrimaryObjectId: 0x50000001u); var chat = new RuntimeChatDelta( stamp with { Sequence = 2 }, new RuntimeChatEntry( Revision: 4, SenderGuid: 0x50000002u, Kind: 2, Sender: "Grey", Text: "hello", ChannelName: "General")); recorder.OnCommand(in command); recorder.OnChat(in chat); Assert.Collection( recorder.Entries, entry => { Assert.Equal(RuntimeTraceKind.Command, entry.Kind); Assert.Equal(0x50000001u, entry.PrimaryObjectId); Assert.Equal((long)RuntimeCommandStatus.Accepted, entry.Value); }, entry => { Assert.Equal(RuntimeTraceKind.Chat, entry.Kind); Assert.Equal(4L, entry.Value); Assert.Equal("General|Grey|hello", entry.Text); }); } }