oecs-sharp/OECS.Tests/QueryAndSystemTests.cs

222 lines
6.1 KiB
C#
Raw Normal View History

using FluentAssertions;
using Xunit;
namespace OECS.Tests;
public class QueryTests
{
private struct Position { public float X; public float Y; }
private struct Velocity { public float X; public float Y; }
private struct Health { public int Value; }
private struct Frozen { }
[Fact]
public void Query_WithSingleComponent_ReturnsMatchingEntities()
{
var world = new World();
var a = world.CreateEntity();
var b = world.CreateEntity();
var c = world.CreateEntity();
world.AddComponent(a, new Position { X = 1, Y = 2 });
world.AddComponent(b, new Position { X = 3, Y = 4 });
// c has no Position
var results = new List<Entity>();
foreach (var it in world.Select<Position>())
results.Add(it.Entity);
results.Should().BeEquivalentTo([a, b]);
}
[Fact]
public void Query_WithMultipleComponents_ReturnsIntersection()
{
var world = new World();
var a = world.CreateEntity();
var b = world.CreateEntity();
var c = world.CreateEntity();
world.AddComponent(a, new Position { X = 1, Y = 2 });
world.AddComponent(a, new Velocity { X = 1, Y = 0 });
world.AddComponent(b, new Position { X = 3, Y = 4 });
// b has no Velocity
world.AddComponent(c, new Velocity { X = 0, Y = 1 });
// c has no Position
var results = new List<Entity>();
foreach (var it in world.Select<Position, Velocity>())
results.Add(it.Entity);
results.Should().BeEquivalentTo([a]);
}
[Fact]
public void Query_Without_ExcludesEntities()
{
var world = new World();
var a = world.CreateEntity();
var b = world.CreateEntity();
world.AddComponent(a, new Position { X = 1, Y = 2 });
world.AddComponent(b, new Position { X = 3, Y = 4 });
world.AddComponent(b, new Frozen());
var query = new Query<Position>().Without<Frozen>();
var results = new List<Entity>();
foreach (var it in world.Select(query))
results.Add(it.Entity);
results.Should().BeEquivalentTo([a]);
}
[Fact]
public void Query_EmptyResult_WhenNoEntitiesMatch()
{
var world = new World();
var count = 0;
foreach (var it in world.Select<Position>())
count++;
count.Should().Be(0);
}
[Fact]
public void Query_RefMutation_IsVisible()
{
var world = new World();
var entity = world.CreateEntity();
world.AddComponent(entity, new Position { X = 0, Y = 0 });
foreach (var it in world.Select<Position>())
it.Item1.X = 42;
ref var pos = ref world.GetComponent<Position>(entity);
pos.X.Should().Be(42);
}
[Fact]
public void Query_DestroyedEntities_DoNotAppear()
{
var world = new World();
var a = world.CreateEntity();
var b = world.CreateEntity();
world.AddComponent(a, new Position { X = 1, Y = 2 });
world.AddComponent(b, new Position { X = 3, Y = 4 });
world.DestroyEntity(a);
var results = new List<Entity>();
foreach (var it in world.Select<Position>())
results.Add(it.Entity);
results.Should().BeEquivalentTo([b]);
}
[Fact]
public void Query_ThreeComponents_Works()
{
var world = new World();
var entity = world.CreateEntity();
world.AddComponent(entity, new Position { X = 1, Y = 2 });
world.AddComponent(entity, new Velocity { X = 3, Y = 4 });
world.AddComponent(entity, new Health { Value = 100 });
var count = 0;
foreach (var it in world.Select<Position, Velocity, Health>())
{
count++;
it.Item1.X.Should().Be(1);
it.Item2.Y.Should().Be(4);
it.Item3.Value.Should().Be(100);
}
count.Should().Be(1);
}
[Fact]
public void FindEntity_ReturnsFirstMatch()
{
var world = new World();
var a = world.CreateEntity();
var b = world.CreateEntity();
world.AddComponent(a, new Position { X = 1, Y = 2 });
world.AddComponent(b, new Position { X = 3, Y = 4 });
var found = world.FindEntity<Position>();
found.Should().NotBe(Entity.Null);
}
}
public class SystemTests
{
private struct Position { public float X; public float Y; }
private struct Velocity { public float X; public float Y; }
private class MovementSystem : ITickedSystem
{
public void RunImpl(World world) => RunImpl(world, Tick.Logical());
public void RunImpl(World world, Tick tick)
{
float dt = tick.DeltaTime;
foreach (var it in world.Select<Position, Velocity>())
{
it.Item1.X += it.Item2.X * dt;
it.Item1.Y += it.Item2.Y * dt;
}
}
}
[Fact]
public void System_RunTimed_UpdatesComponents()
{
var world = new World();
var group = new SystemGroup(world);
group.Add(new MovementSystem());
var entity = world.CreateEntity();
world.AddComponent(entity, new Position { X = 0, Y = 0 });
world.AddComponent(entity, new Velocity { X = 1, Y = 2 });
group.RunTimed(0.5f);
ref var pos = ref world.GetComponent<Position>(entity);
pos.X.Should().Be(0.5f);
pos.Y.Should().Be(1.0f);
}
[Fact]
public void System_RunLogical_DoesNotUseDeltaTime()
{
var world = new World();
var group = new SystemGroup(world);
var system = new TestLogicalSystem();
group.Add(system);
group.RunLogical();
system.WasCalled.Should().BeTrue();
system.ReceivedTick.Type.Should().Be(TickType.Logical);
system.ReceivedTick.DeltaTime.Should().Be(0);
}
private class TestLogicalSystem : ITickedSystem
{
public bool WasCalled { get; private set; }
public Tick ReceivedTick { get; private set; }
public void RunImpl(World world) { }
public void RunImpl(World world, Tick tick)
{
WasCalled = true;
ReceivedTick = tick;
}
}
}