707 lines
22 KiB
C#
707 lines
22 KiB
C#
using NUnit.Framework;
|
|
using FluentAssertions;
|
|
|
|
using Encompass;
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using Encompass.Exceptions;
|
|
|
|
namespace Tests
|
|
{
|
|
struct MockComponent : IComponent
|
|
{
|
|
public int myInt;
|
|
public string myString;
|
|
}
|
|
|
|
public class EngineTest
|
|
{
|
|
static List<ValueTuple<Guid, MockComponent>> resultComponents;
|
|
static MockComponent resultComponent;
|
|
|
|
static List<MockMessage> resultMessages;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class ReadComponentsTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultComponents = ReadComponents<MockComponent>().ToList();
|
|
}
|
|
}
|
|
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class ReadComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultComponent = ReadComponent<MockComponent>().Item2;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentsTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
MockComponent mockComponentB;
|
|
mockComponentB.myInt = 1;
|
|
mockComponentB.myString = "howdy";
|
|
|
|
var componentAID = worldBuilder.AddComponent(entity, mockComponent);
|
|
var componentBID = worldBuilder.AddComponent(entity, mockComponentB);
|
|
var inactiveComponentAID = worldBuilder.AddComponent(entity, mockComponent);
|
|
|
|
worldBuilder.DeactivateComponent(inactiveComponentAID);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
var resultComponentValues = resultComponents.Select((kv) => kv.Item2);
|
|
resultComponentValues.Should().Contain(mockComponent);
|
|
resultComponentValues.Should().Contain(mockComponentB);
|
|
resultComponents.Should().NotContain((inactiveComponentAID, mockComponent));
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
worldBuilder.AddComponent(entity, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
Assert.AreEqual(mockComponent, resultComponent);
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponentWhenMultipleComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
MockComponent mockComponentB;
|
|
mockComponentB.myInt = 1;
|
|
mockComponentB.myString = "howdy";
|
|
|
|
worldBuilder.AddComponent(entity, mockComponent);
|
|
worldBuilder.AddComponent(entity, mockComponentB);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.Throws<InvalidOperationException>(() => world.Update(0.01f));
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
[Updates(typeof(MockComponent))]
|
|
public class UpdateComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
(var componentID, var component) = ReadComponent<MockComponent>();
|
|
|
|
component.myInt = 420;
|
|
component.myString = "blaze it";
|
|
UpdateComponent(componentID, component);
|
|
}
|
|
}
|
|
|
|
// this test needs to be improved...
|
|
|
|
[Test]
|
|
public void UpdateComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
worldBuilder.AddEngine(new UpdateComponentTestEngine());
|
|
worldBuilder.AddEngine(new ReadComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
worldBuilder.AddComponent(entity, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
world.Update(0.01);
|
|
|
|
Assert.AreEqual(420, resultComponent.myInt);
|
|
Assert.AreEqual("blaze it", resultComponent.myString);
|
|
}
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
public class UndeclaredUpdateComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
(var componentID, var component) = ReadComponent<MockComponent>();
|
|
|
|
component.myInt = 420;
|
|
component.myString = "blaze it";
|
|
UpdateComponent(componentID, component);
|
|
|
|
component = ReadComponent<MockComponent>().Item2;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void UpdateUndeclaredComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new UndeclaredUpdateComponentTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 0;
|
|
mockComponent.myString = "hello";
|
|
|
|
worldBuilder.AddComponent(entity, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
|
|
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to write undeclared Component MockComponent"));
|
|
}
|
|
|
|
struct MockMessage : IMessage
|
|
{
|
|
public string myString;
|
|
}
|
|
|
|
[Sends(typeof(MockMessage))]
|
|
public class MessageEmitEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
MockMessage message;
|
|
message.myString = "howdy";
|
|
|
|
this.SendMessage(message);
|
|
}
|
|
}
|
|
|
|
[Reads(typeof(MockMessage))]
|
|
public class MessageReadEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
resultMessages = this.ReadMessages<MockMessage>().ToList();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EmitAndReadMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new MessageEmitEngine());
|
|
worldBuilder.AddEngine(new MessageReadEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
Assert.AreEqual(resultMessages.First().myString, "howdy");
|
|
}
|
|
|
|
public class UndeclaredMessageEmitEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
MockMessage message;
|
|
message.myString = "howdy";
|
|
|
|
this.SendMessage(message);
|
|
}
|
|
}
|
|
|
|
static IEnumerable<MockMessage> emptyReadMessagesResult;
|
|
|
|
[Reads(typeof(MockMessage))]
|
|
class ReadMessagesWhenNoneExistEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
emptyReadMessagesResult = ReadMessages<MockMessage>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadMessagesWhenNoneHaveBeenEmitted()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadMessagesWhenNoneExistEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
emptyReadMessagesResult.Should().BeEmpty();
|
|
}
|
|
|
|
[Test]
|
|
public void EmitUndeclaredMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new UndeclaredMessageEmitEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
|
|
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to write undeclared Message MockMessage"));
|
|
}
|
|
|
|
static bool someTest;
|
|
|
|
[Sends(typeof(MockMessage))]
|
|
class EmitMockMessageEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
MockMessage message;
|
|
message.myString = "howdy";
|
|
|
|
this.SendMessage(message);
|
|
}
|
|
}
|
|
|
|
[Reads(typeof(MockMessage))]
|
|
class SomeMessageTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
someTest = this.SomeMessage<MockMessage>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void SomeMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new EmitMockMessageEngine());
|
|
worldBuilder.AddEngine(new SomeMessageTestEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(someTest, Is.True);
|
|
}
|
|
|
|
class UndeclaredSomeMessageEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
someTest = this.SomeMessage<MockMessage>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void UndeclaredSomeMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new EmitMockMessageEngine());
|
|
worldBuilder.AddEngine(new UndeclaredSomeMessageEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
|
|
}
|
|
|
|
class SomeComponentTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
Assert.IsTrue(SomeComponent<MockComponent>());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void SomeComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.AddComponent(entity, new MockComponent());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
}
|
|
|
|
static ValueTuple<Guid, MockComponent> pairA;
|
|
static ValueTuple<Guid, MockComponent> pairB;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class SameValueComponentReadEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var components = ReadComponents<MockComponent>();
|
|
|
|
pairA = components.First();
|
|
pairB = components.Last();
|
|
}
|
|
}
|
|
|
|
// Tests that components with identical values should be distinguishable by ID
|
|
[Test]
|
|
public void SameValueComponents()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new SameValueComponentReadEngine());
|
|
|
|
MockComponent componentA;
|
|
componentA.myInt = 20;
|
|
componentA.myString = "hello";
|
|
|
|
MockComponent componentB;
|
|
componentB.myInt = 20;
|
|
componentB.myString = "hello";
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.AddComponent(entity, componentA);
|
|
worldBuilder.AddComponent(entity, componentB);
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(pairA, Is.Not.EqualTo(pairB));
|
|
Assert.That(pairA.Item2, Is.EqualTo(pairB.Item2));
|
|
}
|
|
|
|
static IEnumerable<ValueTuple<Guid, MockComponent>> emptyComponentReadResult;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class ReadEmptyMockComponentsEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
emptyComponentReadResult = ReadComponents<MockComponent>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadComponentsOfTypeWhereNoneExist()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new ReadEmptyMockComponentsEngine());
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(emptyComponentReadResult, Is.Empty);
|
|
}
|
|
|
|
struct DestroyerComponent : IComponent { }
|
|
|
|
[Reads(typeof(DestroyerComponent))]
|
|
class DestroyerEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var componentPair in ReadComponents<DestroyerComponent>())
|
|
{
|
|
var componentID = componentPair.Item1;
|
|
var entityID = GetEntityIDByComponentID(componentID);
|
|
Destroy(entityID);
|
|
}
|
|
}
|
|
}
|
|
|
|
static IEnumerable<ValueTuple<Guid, MockComponent>> results;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class ReaderEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
results = ReadComponents<MockComponent>();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void DestroyEntity()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new DestroyerEngine());
|
|
worldBuilder.AddEngine(new ReaderEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityB = worldBuilder.CreateEntity();
|
|
|
|
DestroyerComponent destroyerComponent;
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 2;
|
|
mockComponent.myString = "blah";
|
|
|
|
worldBuilder.AddComponent(entity, destroyerComponent);
|
|
var componentID = worldBuilder.AddComponent(entity, mockComponent);
|
|
|
|
worldBuilder.AddComponent(entityB, destroyerComponent);
|
|
var componentBID = worldBuilder.AddComponent(entityB, mockComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(results, Does.Not.Contain((componentID, mockComponent)));
|
|
Assert.That(results, Does.Not.Contain((componentBID, mockComponent)));
|
|
}
|
|
|
|
[Reads(typeof(DestroyerComponent), typeof(MockComponent))]
|
|
class DestroyAndAddComponentEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var componentPair in ReadComponents<DestroyerComponent>())
|
|
{
|
|
var componentID = componentPair.Item1;
|
|
var entity = GetEntityByComponentID(componentID);
|
|
var (id, _) = GetComponent<MockComponent>(entity);
|
|
RemoveComponent(id);
|
|
Destroy(entity.ID);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void DestroyEntityWhileRemovingComponent()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new DestroyAndAddComponentEngine());
|
|
worldBuilder.AddEngine(new ReaderEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
worldBuilder.AddComponent(entity, new DestroyerComponent());
|
|
worldBuilder.AddComponent(entity, new MockComponent());
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.DoesNotThrow(() => world.Update(0.01));
|
|
}
|
|
|
|
static Entity entityFromComponentIDResult;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class GetEntityFromComponentIDEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var componentID = ReadComponent<MockComponent>().Item1;
|
|
entityFromComponentIDResult = GetEntityByComponentID(componentID);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetEntityFromComponentID()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new GetEntityFromComponentIDEngine());
|
|
|
|
MockComponent component;
|
|
component.myInt = 2;
|
|
component.myString = "howdy";
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.AddComponent(entity, component);
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(entity, Is.EqualTo(entityFromComponentIDResult));
|
|
}
|
|
|
|
static MockComponent mockComponentByIDResult;
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class GetComponentByIDEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var componentID = ReadComponent<MockComponent>().Item1;
|
|
mockComponentByIDResult = GetComponentByID<MockComponent>(componentID);
|
|
}
|
|
}
|
|
[Test]
|
|
public void GetComponentByID()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new GetComponentByIDEngine());
|
|
|
|
MockComponent component;
|
|
component.myInt = 2;
|
|
component.myString = "howdy";
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.AddComponent(entity, component);
|
|
|
|
var world = worldBuilder.Build();
|
|
world.Update(0.01f);
|
|
|
|
Assert.That(component, Is.EqualTo(mockComponentByIDResult));
|
|
}
|
|
|
|
struct OtherComponent : IComponent { }
|
|
|
|
[Reads(typeof(MockComponent))]
|
|
class GetComponentByIDWithTypeMismatchEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
var componentID = ReadComponent<MockComponent>().Item1;
|
|
GetComponentByID<OtherComponent>(componentID);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetComponentByIDWithTypeMismatch()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new GetComponentByIDWithTypeMismatchEngine());
|
|
|
|
MockComponent component;
|
|
component.myInt = 2;
|
|
component.myString = "howdy";
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
worldBuilder.AddComponent(entity, component);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
Assert.Throws<ComponentTypeMismatchException>(() => world.Update(0.01f));
|
|
}
|
|
|
|
struct EntityIDComponent : IComponent { public Guid entityID; }
|
|
static bool hasEntity;
|
|
|
|
[Reads(typeof(EntityIDComponent))]
|
|
class HasEntityTestEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var (mockComponentID, mockComponent) in ReadComponents<EntityIDComponent>())
|
|
{
|
|
hasEntity = EntityExists(mockComponent.entityID);
|
|
if (hasEntity) { Destroy(mockComponent.entityID); }
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EntityExists()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new HasEntityTestEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
var entityTwo = worldBuilder.CreateEntity();
|
|
|
|
EntityIDComponent entityIDComponent;
|
|
entityIDComponent.entityID = entityTwo.ID;
|
|
|
|
worldBuilder.AddComponent(entity, entityIDComponent);
|
|
|
|
var world = worldBuilder.Build();
|
|
|
|
world.Update(0.01);
|
|
|
|
Assert.IsTrue(hasEntity);
|
|
|
|
world.Update(0.01);
|
|
|
|
Assert.IsFalse(hasEntity);
|
|
}
|
|
|
|
[Sends(typeof(MockMessage))]
|
|
[Updates(typeof(MockComponent))]
|
|
class EngineThatWritesComponentAndMessage : Engine
|
|
{
|
|
public override void Update(double dt) { }
|
|
}
|
|
|
|
[Test]
|
|
public void EngineWritesComponentAndMessage()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
|
|
Assert.Throws<IllegalEngineAttributesException>(() => worldBuilder.AddEngine(new EngineThatWritesComponentAndMessage()));
|
|
}
|
|
|
|
struct MockComponentUpdateMessage : IMessage
|
|
{
|
|
public Guid componentID;
|
|
public MockComponent mockComponent;
|
|
}
|
|
|
|
[Reads(typeof(MockComponentUpdateMessage))]
|
|
[Updates(typeof(MockComponent))]
|
|
class RepeatUpdateEngine : Engine
|
|
{
|
|
public override void Update(double dt)
|
|
{
|
|
foreach (var mockComponentUpdateMessage in ReadMessages<MockComponentUpdateMessage>())
|
|
{
|
|
UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent);
|
|
UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EngineUpdatesComponentMultipleTimes()
|
|
{
|
|
var worldBuilder = new WorldBuilder();
|
|
worldBuilder.AddEngine(new RepeatUpdateEngine());
|
|
|
|
var entity = worldBuilder.CreateEntity();
|
|
|
|
MockComponent mockComponent;
|
|
mockComponent.myInt = 1;
|
|
mockComponent.myString = "5";
|
|
|
|
var mockComponentID = worldBuilder.AddComponent(entity, mockComponent);
|
|
|
|
MockComponentUpdateMessage mockComponentUpdateMessage;
|
|
mockComponentUpdateMessage.componentID = mockComponentID;
|
|
mockComponentUpdateMessage.mockComponent = mockComponent;
|
|
worldBuilder.SendMessage(mockComponentUpdateMessage);
|
|
|
|
var world = worldBuilder.Build();
|
|
Assert.Throws<RepeatUpdateComponentException>(() => world.Update(0.01));
|
|
}
|
|
}
|
|
}
|