package coffee.liz.ecs import kotlin.reflect.KClass import kotlin.test.* // Test systems for circular dependency test class CircularSystemA : System { override val dependencies: Set> get() = setOf(CircularSystemB::class) override fun update(world: World, deltaTime: Float) {} } class CircularSystemB : System { override val dependencies: Set> get() = setOf(CircularSystemA::class) override fun update(world: World, deltaTime: Float) {} } class DAGWorldTest { @Test fun `can create entities with unique ids`() { val world = DAGWorld(emptyList()) val entity1 = world.createEntity() val entity2 = world.createEntity() assertNotEquals(entity1.id, entity2.id) } @Test fun `can destroy entity`() { val world = DAGWorld(emptyList()) val entity = world.createEntity() entity.add(Position(10f, 20f)) world.destroyEntity(entity) val results = world.query() assertFalse(results.contains(entity)) } @Test fun `query returns entities with matching components`() { val world = DAGWorld(emptyList()) val entity1 = world.createEntity().add(Position(10f, 20f)) val entity2 = world.createEntity().add(Position(30f, 40f)) val entity3 = world.createEntity().add(Velocity(1f, 2f)) world.update(0f) // Trigger cache update val results = world.query() assertEquals(2, results.size) assertTrue(results.contains(entity1)) assertTrue(results.contains(entity2)) assertFalse(results.contains(entity3)) } @Test fun `query with multiple components returns intersection`() { val world = DAGWorld(emptyList()) val entity1 = world.createEntity() .add(Position(10f, 20f)) .add(Velocity(1f, 2f)) val entity2 = world.createEntity() .add(Position(30f, 40f)) val entity3 = world.createEntity() .add(Velocity(3f, 4f)) world.update(0f) // Trigger cache update val results = world.query() assertEquals(1, results.size) assertTrue(results.contains(entity1)) } @Test fun `systems execute in dependency order`() { val executionOrder = mutableListOf() val systemA = object : System { override fun update(world: World, deltaTime: Float) { executionOrder.add("A") } } val systemB = object : System { override val dependencies = setOf(systemA::class) override fun update(world: World, deltaTime: Float) { executionOrder.add("B") } } val systemC = object : System { override val dependencies = setOf(systemB::class) override fun update(world: World, deltaTime: Float) { executionOrder.add("C") } } val world = DAGWorld(listOf(systemC, systemA, systemB)) world.update(0f) assertEquals(listOf("A", "B", "C"), executionOrder) } @Test fun `systems with no dependencies can execute in any order`() { val executionOrder = mutableListOf() val systemA = object : System { override fun update(world: World, deltaTime: Float) { executionOrder.add("A") } } val systemB = object : System { override fun update(world: World, deltaTime: Float) { executionOrder.add("B") } } val world = DAGWorld(listOf(systemA, systemB)) world.update(0f) assertEquals(2, executionOrder.size) assertTrue(executionOrder.contains("A")) assertTrue(executionOrder.contains("B")) } @Test fun `circular dependency throws exception`() { val systemA = CircularSystemA() val systemB = CircularSystemB() assertFailsWith { DAGWorld(listOf(systemA, systemB)) } } @Test fun `system receives correct deltaTime`() { var receivedDelta = 0f val system = object : System { override fun update(world: World, deltaTime: Float) { receivedDelta = deltaTime } } val world = DAGWorld(listOf(system)) world.update(0.016f) assertEquals(0.016f, receivedDelta) } @Test fun `systems can query entities during update`() { var queriedEntities: Set? = null val system = object : System { override fun update(world: World, deltaTime: Float) { queriedEntities = world.query() } } val world = DAGWorld(listOf(system)) val entity = world.createEntity().add(Position(10f, 20f)) world.update(0f) assertNotNull(queriedEntities) assertEquals(1, queriedEntities!!.size) assertTrue(queriedEntities!!.contains(entity)) } @Test fun `component cache updates after entity changes`() { val world = DAGWorld(emptyList()) val entity = world.createEntity() world.update(0f) assertEquals(0, world.query().size) entity.add(Position(10f, 20f)) world.update(0f) assertEquals(1, world.query().size) entity.remove() world.update(0f) assertEquals(0, world.query().size) } @Test fun `diamond dependency resolves correctly`() { val executionOrder = mutableListOf() // A // / \ // B C // \ / // D val systemA = object : System { override fun update(world: World, deltaTime: Float) { executionOrder.add("A") } } val systemB = object : System { override val dependencies = setOf(systemA::class) override fun update(world: World, deltaTime: Float) { executionOrder.add("B") } } val systemC = object : System { override val dependencies = setOf(systemA::class) override fun update(world: World, deltaTime: Float) { executionOrder.add("C") } } val systemD = object : System { override val dependencies = setOf(systemB::class, systemC::class) override fun update(world: World, deltaTime: Float) { executionOrder.add("D") } } val world = DAGWorld(listOf(systemD, systemC, systemB, systemA)) world.update(0f) // A must come first, D must come last, B and C in between assertEquals("A", executionOrder.first()) assertEquals("D", executionOrder.last()) assertTrue(executionOrder.indexOf("B") < executionOrder.indexOf("D")) assertTrue(executionOrder.indexOf("C") < executionOrder.indexOf("D")) } }