package com.projectsacrifice.game; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.Arrays; import org.junit.Test; public final class VersionedSessionCodecTest { @Test public void currentSnapshotRoundTripsEveryPuzzleCriticalField() { GameSessionSnapshot original = sampleSnapshot(); VersionedSessionCodec.DecodeResult decoded = VersionedSessionCodec.decode(VersionedSessionCodec.encode(original)); assertEquals(VersionedSessionCodec.Status.RESTORED, decoded.status()); GameSessionSnapshot restored = decoded.snapshot(); assertEquals(5, restored.levelNumber()); assertEquals(12.75f, restored.elapsedSeconds(), 0f); assertEquals(2, restored.deaths()); assertEquals(640f, restored.player().x(), 0f); assertFalse(restored.player().alive()); assertEquals(2, restored.bodies().size()); assertTrue(restored.bodies().get(0).pinned()); assertTrue(restored.bodies().get(1).conductive()); assertTrue(restored.bodies().get(0).crushed()); assertTrue(restored.bodies().get(1).burned()); assertEquals(2, restored.objects().size()); assertEquals(LevelDefinition.PhysicalObjectType.BOX, restored.objects().get(0).type()); assertEquals(3, restored.objects().get(1).mass()); assertTrue(restored.collectibleFound()); assertEquals(Arrays.asList(1.25f, 3.5f), restored.platformMotionTimes()); assertTrue(restored.toggleSignals().get("gate")); assertEquals(2.25f, restored.timedSignals().get("clock"), 0f); assertTrue(restored.previousSwitchContacts().get("gate@420.0")); } @Test public void legacyV1MigratesWithSafeEmptyEdgeContacts() { VersionedSessionCodec.DecodeResult decoded = VersionedSessionCodec.decode( VersionedSessionCodec.encodeLegacyV1(sampleSnapshot())); assertEquals(VersionedSessionCodec.Status.MIGRATED, decoded.status()); assertEquals(VersionedSessionCodec.CURRENT_VERSION, decoded.snapshot().saveVersion()); assertTrue(decoded.snapshot().previousSwitchContacts().isEmpty()); assertEquals(2, decoded.snapshot().bodies().size()); assertFalse(decoded.snapshot().bodies().get(0).crushed()); assertFalse(decoded.snapshot().bodies().get(1).burned()); } @Test public void legacyV2MigratesWithSafeDefaultCorpseStates() { VersionedSessionCodec.DecodeResult decoded = VersionedSessionCodec.decode( VersionedSessionCodec.encodeLegacyV2(sampleSnapshot())); assertEquals(VersionedSessionCodec.Status.MIGRATED, decoded.status()); assertTrue(decoded.snapshot().previousSwitchContacts().get("gate@420.0")); assertFalse(decoded.snapshot().bodies().get(0).crushed()); assertFalse(decoded.snapshot().bodies().get(1).burned()); assertTrue(decoded.snapshot().objects().isEmpty()); } @Test public void legacyV3MigratesWithSafeEmptyPhysicalObjectRuntime() { VersionedSessionCodec.DecodeResult decoded = VersionedSessionCodec.decode( VersionedSessionCodec.encodeLegacyV3(sampleSnapshot())); assertEquals(VersionedSessionCodec.Status.MIGRATED, decoded.status()); assertTrue(decoded.snapshot().bodies().get(0).crushed()); assertTrue(decoded.snapshot().bodies().get(1).burned()); assertTrue(decoded.snapshot().objects().isEmpty()); } @Test public void legacyV4MigratesWithCollectibleNotInvented() { VersionedSessionCodec.DecodeResult decoded = VersionedSessionCodec.decode( VersionedSessionCodec.encodeLegacyV4(sampleSnapshot())); assertEquals(VersionedSessionCodec.Status.MIGRATED, decoded.status()); assertEquals(2, decoded.snapshot().objects().size()); assertFalse(decoded.snapshot().collectibleFound()); } @Test public void corruptAndFutureSnapshotsFailClosed() { VersionedSessionCodec.DecodeResult corrupt = VersionedSessionCodec.decode(new byte[] {1, 2, 3}); VersionedSessionCodec.DecodeResult future = VersionedSessionCodec.decode(VersionedSessionCodec.encodeVersionForTest(99)); assertEquals(VersionedSessionCodec.Status.CORRUPT, corrupt.status()); assertNull(corrupt.snapshot()); assertEquals(VersionedSessionCodec.Status.UNSUPPORTED, future.status()); assertNull(future.snapshot()); } @Test public void productionTeachingLevelSnapshotRoundTrips() { GameSessionSnapshot snapshot = GameSessionSnapshot.builder(14) .elapsedSeconds(3f) .player(new GameSessionSnapshot.PlayerData( 130f, 760f, 0f, 0f, true, true, 0f, 0f, -1, 1)) .build(); VersionedSessionCodec.DecodeResult decoded = VersionedSessionCodec.decode( VersionedSessionCodec.encode(snapshot)); assertEquals(VersionedSessionCodec.Status.RESTORED, decoded.status()); assertEquals(14, decoded.snapshot().levelNumber()); } private static GameSessionSnapshot sampleSnapshot() { return GameSessionSnapshot.builder(5) .elapsedSeconds(12.75f) .deaths(2) .collectibleFound(true) .respawnSeconds(0.4f) .player(new GameSessionSnapshot.PlayerData( 640f, 780f, 120f, -40f, false, false, 0.1f, 0.08f, -1, -1)) .addBody(new GameSessionSnapshot.BodyData( 500f, 826f, 72f, 54f, 0f, 0f, false, false, true, true, true, false, -1, "CRUSH")) .addBody(new GameSessionSnapshot.BodyData( 720f, 826f, 72f, 54f, 0f, 0f, false, true, true, true, false, true, -1, "FIRE")) .addObject(new GameSessionSnapshot.ObjectData( LevelDefinition.PhysicalObjectType.BOX, 300f, 800f, 80f, 80f, 20f, 0f, 1, true, -1)) .addObject(new GameSessionSnapshot.ObjectData( LevelDefinition.PhysicalObjectType.COUNTERWEIGHT, 700f, 780f, 80f, 100f, 0f, 0f, 3, true, 1)) .platformMotionTimes(Arrays.asList(1.25f, 3.5f)) .toggleSignal("gate", true) .timedSignal("clock", 2.25f) .previousSwitchContact("gate@420.0", true) .build(); } }