diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2026-01-23 20:22:30 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2026-01-23 20:22:30 -0800 |
| commit | 52864cb701e59a1d847fd5586245519eb5e3b3bc (patch) | |
| tree | 1d3df85b939e2c50ebf154ab4fcac6f02ad087c2 /core/src/test/java/coffee/liz/abstractionengine | |
| download | the-abstraction-engine-v2-52864cb701e59a1d847fd5586245519eb5e3b3bc.tar.gz the-abstraction-engine-v2-52864cb701e59a1d847fd5586245519eb5e3b3bc.zip | |
Move code over
Diffstat (limited to 'core/src/test/java/coffee/liz/abstractionengine')
4 files changed, 352 insertions, 0 deletions
diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystemTest.java new file mode 100644 index 0000000..3743f3e --- /dev/null +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystemTest.java @@ -0,0 +1,188 @@ +package coffee.liz.abstractionengine.grid.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import coffee.liz.abstractionengine.grid.component.GridCollidable; +import coffee.liz.abstractionengine.grid.component.GridCollidable.CollisionBehavior; +import coffee.liz.abstractionengine.grid.component.GridCollidable.CollisionBehavior.CollisionBehaviorType; +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridMomentum; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.ecs.math.Vec2i; +import coffee.liz.ecs.model.Entity; +import coffee.liz.ecs.model.World; + +import lombok.Getter; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +final class GridCollisionPropagatationSystemTest { + private static final Duration FRAME = Duration.ZERO; + private static final GridCollidable WALL_COLLIDABLE = (me, them) -> CollisionBehavior.builder() + .collisionBehaviorType(CollisionBehaviorType.WALL).priority(10).build(); + private static final GridCollidable PROPAGATE_COLLIDABLE = (me, them) -> CollisionBehavior.builder() + .collisionBehaviorType(CollisionBehaviorType.PROPAGATE).priority(0).build(); + + @Getter + private static class SwallowCollidable implements GridCollidable { + private final Set<Entity> swallowed = new HashSet<>(); + @Override + public <T> void onSwallow(final Entity them, final World<T> world) { + swallowed.add(them); + } + + @Override + public CollisionBehavior getCollisionBehaviorBetween(final Entity me, final Entity them) { + return CollisionBehavior.builder().collisionBehaviorType(CollisionBehaviorType.SWALLOW).priority(0).build(); + } + } + + @Test + public void testPrioritization() { + final World<GridInputState> world = mockWorld(); + final GridIndexSystem indexSystem = mock(GridIndexSystem.class); + when(indexSystem.inBounds(any())).thenReturn(true); + when(world.getSystem(GridIndexSystem.class)).thenReturn(indexSystem); + + final Entity pusher = Entity.builder().id(1).build().add(PROPAGATE_COLLIDABLE).add(new GridMomentum(Vec2i.EAST)) + .add(new GridPosition(Vec2i.ZERO)); + final Entity toPropagate = Entity.builder().id(2).build().add(PROPAGATE_COLLIDABLE) + .add(new GridPosition(Vec2i.EAST)); + final Entity wall = Entity.builder().id(3).build().add(WALL_COLLIDABLE).add(new GridPosition(Vec2i.EAST)); + + // Propagation takes priority because priority(0) is lower + when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + .thenReturn(Set.of(pusher)); + + when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); + when(indexSystem.entitiesAt(Vec2i.EAST)).thenReturn(List.of(toPropagate, wall)); + + final GridCollisionPropagatationSystem system = new GridCollisionPropagatationSystem(); + system.update(world, mock(GridInputState.class), FRAME); + + assertEquals(Vec2i.EAST, pusher.get(GridMomentum.class).getVelocity()); + assertEquals(Vec2i.EAST, toPropagate.get(GridMomentum.class).getVelocity()); + } + + @Test + public void testWallCollisionHaltsRayMomentum() { + final World<GridInputState> world = mockWorld(); + final GridIndexSystem indexSystem = mock(GridIndexSystem.class); + when(indexSystem.inBounds(any())).thenReturn(true); + when(world.getSystem(GridIndexSystem.class)).thenReturn(indexSystem); + + final Entity pusher = Entity.builder().id(1).build().add(PROPAGATE_COLLIDABLE).add(new GridMomentum(Vec2i.EAST)) + .add(new GridPosition(Vec2i.ZERO)); + final Entity toPropagate = Entity.builder().id(2).build().add(PROPAGATE_COLLIDABLE) + .add(new GridMomentum(Vec2i.EAST)).add(new GridPosition(Vec2i.EAST)); + final Entity wall = Entity.builder().id(3).build().add(WALL_COLLIDABLE) + .add(new GridPosition(Vec2i.EAST.scale(2, 0))); + + when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + .thenReturn(Set.of(pusher, toPropagate)); + + when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); + when(indexSystem.entitiesAt(Vec2i.EAST)).thenReturn(List.of(toPropagate)); + when(indexSystem.entitiesAt(Vec2i.EAST.scale(2, 0))).thenReturn(List.of(wall)); + + final GridCollisionPropagatationSystem system = new GridCollisionPropagatationSystem(); + system.update(world, GridInputState.builder().build(), FRAME); + + assertEquals(Vec2i.ZERO, pusher.get(GridMomentum.class).getVelocity()); + assertEquals(Vec2i.ZERO, toPropagate.get(GridMomentum.class).getVelocity()); + } + + @Test + public void testGoingOutOfBoundsHaltsMomentum() { + final World<GridInputState> world = mockWorld(); + final GridIndexSystem indexSystem = mock(GridIndexSystem.class); + + when(indexSystem.inBounds(any())).thenReturn(false); + when(world.getSystem(GridIndexSystem.class)).thenReturn(indexSystem); + + final Entity pusher = Entity.builder().id(1).build().add(PROPAGATE_COLLIDABLE).add(new GridMomentum(Vec2i.EAST)) + .add(new GridPosition(Vec2i.ZERO)); + + when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + .thenReturn(Set.of(pusher)); + + when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); + + final GridCollisionPropagatationSystem system = new GridCollisionPropagatationSystem(); + system.update(world, GridInputState.builder().build(), FRAME); + + assertEquals(Vec2i.ZERO, pusher.get(GridMomentum.class).getVelocity()); + } + + @Test + public void testZeroVelocity() { + final World<GridInputState> world = mockWorld(); + final GridIndexSystem indexSystem = mock(GridIndexSystem.class); + + when(indexSystem.inBounds(any())).thenReturn(true); + when(world.getSystem(GridIndexSystem.class)).thenReturn(indexSystem); + + final Entity pusher = Entity.builder().id(1).build().add(PROPAGATE_COLLIDABLE).add(new GridMomentum(Vec2i.ZERO)) + .add(new GridPosition(Vec2i.ZERO)); + + when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + .thenReturn(Set.of(pusher)); + + when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); + + final GridCollisionPropagatationSystem system = new GridCollisionPropagatationSystem(); + system.update(world, GridInputState.builder().build(), FRAME); + + assertEquals(Vec2i.ZERO, pusher.get(GridMomentum.class).getVelocity()); + } + + @Test + public void testSwallowInteraction() { + final World<GridInputState> world = mockWorld(); + final GridIndexSystem indexSystem = mock(GridIndexSystem.class); + + when(indexSystem.inBounds(any())).thenReturn(true); + when(world.getSystem(GridIndexSystem.class)).thenReturn(indexSystem); + + final SwallowCollidable swallowCollidable = new SwallowCollidable(); + + final Entity pusher = Entity.builder().id(1).build().add(PROPAGATE_COLLIDABLE).add(new GridMomentum(Vec2i.EAST)) + .add(new GridPosition(Vec2i.ZERO)); + + final Entity swallower = Entity.builder().id(2).build().add(swallowCollidable) + .add(new GridPosition(Vec2i.EAST)); + + when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + .thenReturn(Set.of(pusher)); + + when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); + when(indexSystem.entitiesAt(Vec2i.EAST)).thenReturn(List.of(swallower)); + + final GridCollisionPropagatationSystem system = new GridCollisionPropagatationSystem(); + system.update(world, GridInputState.builder().build(), FRAME); + + assertEquals(Vec2i.EAST, pusher.get(GridMomentum.class).getVelocity()); + assertFalse(swallower.has(GridMomentum.class)); + + assertEquals(swallowCollidable.getSwallowed(), Set.of(pusher)); + } + + @Test + public void testDependencies() { + assertEquals(Set.of(GridMovementSystem.class, GridIndexSystem.class), + new GridCollisionPropagatationSystem().getDependencies()); + } + + @SuppressWarnings("unchecked") + private static World<GridInputState> mockWorld() { + return (World<GridInputState>) mock(World.class); + } +} diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridIndexSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridIndexSystemTest.java new file mode 100644 index 0000000..d9afde5 --- /dev/null +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridIndexSystemTest.java @@ -0,0 +1,70 @@ +package coffee.liz.abstractionengine.grid.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.ecs.math.Vec2i; +import coffee.liz.ecs.model.Entity; +import coffee.liz.ecs.model.World; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.time.Duration; +import java.util.Set; + +class GridIndexSystemTest { + @Test + public void testUpdateIndexesEntitiesIntoGridSlots() { + final GridIndexSystem system = new GridIndexSystem(new Vec2i(4, 4)); + final World<GridInputState> world = mockWorld(); + final Entity alpha = Entity.builder().id(11).build(); + alpha.add(new GridPosition(new Vec2i(1, 2))); + final Entity beta = Entity.builder().id(12).build(); + beta.add(new GridPosition(new Vec2i(0, 0))); + when(world.query(Set.of(GridPosition.class))).thenReturn(Set.of(alpha, beta)); + + system.update(world, GridInputState.builder().movement(Vec2i.NORTH).build(), Duration.ZERO); + + assertTrue(system.entitiesAt(new Vec2i(1, 2)).contains(alpha)); + assertTrue(system.entitiesAt(new Vec2i(0, 0)).contains(beta)); + assertTrue(system.entitiesAt(new Vec2i(3, 3)).isEmpty()); + } + + @Test + public void testUpdateClearsPreviousIndexesBeforeRebuilding() { + final GridIndexSystem system = new GridIndexSystem(new Vec2i(2, 2)); + final World<GridInputState> world = mockWorld(); + final Entity moving = Entity.builder().id(77).build(); + moving.add(new GridPosition(Vec2i.ZERO)); + when(world.query(Set.of(GridPosition.class))).thenReturn(Set.of(moving)).thenReturn(Set.of()); + + system.update(world, GridInputState.builder().movement(Vec2i.EAST).build(), Duration.ZERO); + assertTrue(system.entitiesAt(Vec2i.ZERO).contains(moving)); + + system.update(world, GridInputState.builder().movement(Vec2i.NORTH).build(), Duration.ZERO); + assertTrue(system.entitiesAt(Vec2i.ZERO).isEmpty()); + } + + @Test + public void testEntitiesAtReturnsEmptySetForOutOfBoundsQuery() { + final GridIndexSystem system = new GridIndexSystem(new Vec2i(2, 2)); + + assertEquals(Set.of(), system.entitiesAt(new Vec2i(-1, 0))); + assertEquals(Set.of(), system.entitiesAt(new Vec2i(2, 1))); + assertEquals(Set.of(), system.entitiesAt(new Vec2i(1, 2))); + } + + @Test + public void testDependencies() { + assertEquals(Set.of(), new GridIndexSystem(Vec2i.ZERO).getDependencies()); + } + + @SuppressWarnings("unchecked") + private static World<GridInputState> mockWorld() { + return (World<GridInputState>) Mockito.mock(World.class); + } +} diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridMovementSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridMovementSystemTest.java new file mode 100644 index 0000000..8bd8ff3 --- /dev/null +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridMovementSystemTest.java @@ -0,0 +1,48 @@ +package coffee.liz.abstractionengine.grid.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import coffee.liz.abstractionengine.grid.component.GridControllable; +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridMomentum; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.ecs.math.Vec2i; +import coffee.liz.ecs.model.Entity; +import coffee.liz.ecs.model.World; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.time.Duration; +import java.util.Set; + +final class GridMovementSystemTest { + @Test + public void testUpdateAssignsMomentumToControllableEntities() { + final World<GridInputState> world = mockWorld(); + final Entity subject = Entity.builder().id(1).build(); + subject.add(new GridControllable()); + subject.add(new GridPosition(Vec2i.ZERO)); + final Set<Entity> controllableEntities = Set.of(subject); + when(world.query(Set.of(GridControllable.class, GridPosition.class))).thenReturn(controllableEntities); + + final GridInputState inputState = GridInputState.builder().movement(Vec2i.SOUTH).build(); + final GridMovementSystem system = new GridMovementSystem(); + + system.update(world, inputState, Duration.ofMillis(16)); + + final GridMomentum appliedMomentum = subject.get(GridMomentum.class); + assertEquals(Vec2i.SOUTH, appliedMomentum.getVelocity()); + } + + @Test + public void testDependencies() { + assertEquals(Set.of(), new GridMovementSystem().getDependencies()); + } + + @SuppressWarnings("unchecked") + private static World<GridInputState> mockWorld() { + return (World<GridInputState>) Mockito.mock(World.class); + } +} diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystemTest.java new file mode 100644 index 0000000..c3bb01e --- /dev/null +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystemTest.java @@ -0,0 +1,46 @@ +package coffee.liz.abstractionengine.grid.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridMomentum; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.math.Vec2i; +import coffee.liz.ecs.model.Entity; +import coffee.liz.ecs.model.World; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.time.Duration; +import java.util.Set; + +final class GridPhysicsSystemTest { + @Test + public void testUpdateMovesEntitiesByMomentumAndResetsVelocity() { + final World<GridInputState> world = mockWorld(); + final Entity body = Entity.builder().id(3).build(); + body.add(new GridPosition(Vec2i.ZERO)); + body.add(new GridMomentum(Vec2i.EAST)); + when(world.query(Set.of(GridMomentum.class, GridPosition.class))).thenReturn(Set.of(body)); + + final GridPhysicsSystem system = new GridPhysicsSystem(); + system.update(world, GridInputState.builder().movement(Vec2i.NORTH).build(), Duration.ZERO); + + final Vec2<Integer> newPosition = body.get(GridPosition.class).getPosition(); + assertEquals(Vec2i.EAST, newPosition); + assertEquals(Vec2i.ZERO, body.get(GridMomentum.class).getVelocity()); + } + + @Test + public void testDependencies() { + assertEquals(Set.of(GridCollisionPropagatationSystem.class), new GridPhysicsSystem().getDependencies()); + } + + @SuppressWarnings("unchecked") + private static World<GridInputState> mockWorld() { + return (World<GridInputState>) Mockito.mock(World.class); + } +} |
