166 lines
4.5 KiB
C#
166 lines
4.5 KiB
C#
using FluentAssertions;
|
|
using Xunit;
|
|
|
|
namespace OECS.Tests;
|
|
|
|
public class SingletonTests
|
|
{
|
|
private struct GameConfig { public float Gravity; public int MaxPlayers; }
|
|
private struct TimeState { public float Elapsed; }
|
|
|
|
[Fact]
|
|
public void SetSingleton_GetSingleton_RoundTrips()
|
|
{
|
|
var world = new World();
|
|
|
|
world.SetSingleton(new GameConfig { Gravity = 9.8f, MaxPlayers = 4 });
|
|
|
|
ref var config = ref world.GetSingleton<GameConfig>();
|
|
config.Gravity.Should().Be(9.8f);
|
|
config.MaxPlayers.Should().Be(4);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetSingleton_ReplacesExisting()
|
|
{
|
|
var world = new World();
|
|
|
|
world.SetSingleton(new GameConfig { Gravity = 9.8f, MaxPlayers = 4 });
|
|
world.SetSingleton(new GameConfig { Gravity = 1.6f, MaxPlayers = 2 });
|
|
|
|
ref var config = ref world.GetSingleton<GameConfig>();
|
|
config.Gravity.Should().Be(1.6f);
|
|
config.MaxPlayers.Should().Be(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void HasSingleton_ReturnsCorrectly()
|
|
{
|
|
var world = new World();
|
|
|
|
world.HasSingleton<GameConfig>().Should().BeFalse();
|
|
world.SetSingleton(new GameConfig());
|
|
world.HasSingleton<GameConfig>().Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void RemoveSingleton_RemovesIt()
|
|
{
|
|
var world = new World();
|
|
|
|
world.SetSingleton(new GameConfig { Gravity = 9.8f, MaxPlayers = 4 });
|
|
world.HasSingleton<GameConfig>().Should().BeTrue();
|
|
|
|
world.RemoveSingleton<GameConfig>();
|
|
world.HasSingleton<GameConfig>().Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void RemoveSingleton_NoOp_WhenNotPresent()
|
|
{
|
|
var world = new World();
|
|
|
|
// Should not throw.
|
|
world.RemoveSingleton<GameConfig>();
|
|
}
|
|
|
|
[Fact]
|
|
public void SingletonEntity_DoesNotAppearInNormalQueries()
|
|
{
|
|
var world = new World();
|
|
|
|
// Set a singleton with the same component type that a regular entity has.
|
|
world.SetSingleton(new GameConfig { Gravity = 9.8f, MaxPlayers = 4 });
|
|
|
|
var regular = world.CreateEntity();
|
|
world.AddComponent(regular, new GameConfig { Gravity = 1.6f, MaxPlayers = 2 });
|
|
|
|
var results = new List<Entity>();
|
|
|
|
foreach (var it in world.Select<GameConfig>())
|
|
{
|
|
results.Add(it.Entity);
|
|
}
|
|
|
|
// Only the regular entity should appear, not the singleton.
|
|
results.Should().BeEquivalentTo([regular]);
|
|
}
|
|
|
|
[Fact]
|
|
public void SingletonEntity_IsAlive()
|
|
{
|
|
var world = new World();
|
|
|
|
world.SetSingleton(new GameConfig());
|
|
|
|
// Singletons are now on their own dedicated entities.
|
|
// Verify the singleton exists (its entity is alive).
|
|
world.HasSingleton<GameConfig>().Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Singleton_SurvivesTickExecution()
|
|
{
|
|
var world = new World();
|
|
var group = new SystemGroup(world);
|
|
|
|
world.SetSingleton(new TimeState { Elapsed = 0f });
|
|
|
|
// System that reads and updates the singleton.
|
|
group.Add(new TimeSystem(world));
|
|
|
|
group.RunTimed(0.5f);
|
|
group.RunTimed(0.5f);
|
|
|
|
ref var time = ref world.GetSingleton<TimeState>();
|
|
time.Elapsed.Should().Be(1.0f);
|
|
}
|
|
|
|
[Fact]
|
|
public void MultipleSingletonTypes_AreIndependent()
|
|
{
|
|
var world = new World();
|
|
|
|
world.SetSingleton(new GameConfig { Gravity = 9.8f, MaxPlayers = 4 });
|
|
world.SetSingleton(new TimeState { Elapsed = 42f });
|
|
|
|
ref var config = ref world.GetSingleton<GameConfig>();
|
|
ref var time = ref world.GetSingleton<TimeState>();
|
|
|
|
config.Gravity.Should().Be(9.8f);
|
|
time.Elapsed.Should().Be(42f);
|
|
}
|
|
|
|
[Fact]
|
|
public void Singleton_Mutation_ViaRef_IsVisible()
|
|
{
|
|
var world = new World();
|
|
|
|
world.SetSingleton(new GameConfig { Gravity = 0, MaxPlayers = 0 });
|
|
|
|
ref var config = ref world.GetSingleton<GameConfig>();
|
|
config.Gravity = 9.8f;
|
|
|
|
ref var config2 = ref world.GetSingleton<GameConfig>();
|
|
config2.Gravity.Should().Be(9.8f);
|
|
}
|
|
|
|
private class TimeSystem : ITickedSystem
|
|
{
|
|
private readonly World _world;
|
|
|
|
public TimeSystem(World world)
|
|
{
|
|
_world = world;
|
|
}
|
|
|
|
public void Run(World world) => Run(world, Tick.Logical());
|
|
|
|
public void Run(World world, Tick tick)
|
|
{
|
|
ref var time = ref _world.GetSingleton<TimeState>();
|
|
time.Elapsed += tick.DeltaTime;
|
|
}
|
|
}
|
|
}
|