encompass-cs/test/EngineTest.cs

617 lines
18 KiB
C#

using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System;
using System.Linq;
using System.Collections.Generic;
using Encompass.Exceptions;
namespace Tests
{
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 = entity.AddComponent(mockComponent);
var componentBID = entity.AddComponent(mockComponentB);
var inactiveComponentAID = entity.AddComponent(mockComponent);
entity.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";
entity.AddComponent(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";
entity.AddComponent(mockComponent);
entity.AddComponent(mockComponentB);
var world = worldBuilder.Build();
Assert.Throws<InvalidOperationException>(() => world.Update(0.01f));
}
[Reads(typeof(MockComponent))]
[Writes(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);
resultComponent = ReadComponent<MockComponent>().Item2;
}
}
[Test]
public void UpdateComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new UpdateComponentTestEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
world.Update(0.01f);
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) = this.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";
entity.AddComponent(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;
}
[Writes(typeof(MockMessage))]
public class MessageEmitEngine : Engine
{
public override void Update(double dt)
{
MockMessage message;
message.myString = "howdy";
this.EmitMessage(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.EmitMessage(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 emit undeclared Message MockMessage"));
}
static bool someTest;
[Writes(typeof(MockMessage))]
class EmitMockMessageEngine : Engine
{
public override void Update(double dt)
{
MockMessage message;
message.myString = "howdy";
this.EmitMessage(message);
}
}
[Reads(typeof(MockMessage))]
class SomeTestEngine : Engine
{
public override void Update(double dt)
{
someTest = this.Some<MockMessage>();
}
}
[Test]
public void Some()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new EmitMockMessageEngine());
worldBuilder.AddEngine(new SomeTestEngine());
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.That(someTest, Is.True);
}
class UndeclaredSomeEngine : Engine
{
public override void Update(double dt)
{
someTest = this.Some<MockMessage>();
}
}
[Test]
public void IllegalSome()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new EmitMockMessageEngine());
worldBuilder.AddEngine(new UndeclaredSomeEngine());
var world = worldBuilder.Build();
Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
}
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();
entity.AddComponent(componentA);
entity.AddComponent(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";
entity.AddComponent(destroyerComponent);
var componentID = entity.AddComponent(mockComponent);
entityB.AddComponent(destroyerComponent);
var componentBID = entityB.AddComponent(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))]
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, _) = entity.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();
entity.AddComponent(new DestroyerComponent());
entity.AddComponent(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();
entity.AddComponent(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();
entity.AddComponent(component);
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.That(component, Is.EqualTo(mockComponentByIDResult));
}
struct OtherComponent : IComponent { }
[Reads(typeof(MockComponent), typeof(OtherComponent))]
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();
entity.AddComponent(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;
entity.AddComponent(entityIDComponent);
var world = worldBuilder.Build();
world.Update(0.01);
Assert.IsTrue(hasEntity);
world.Update(0.01);
Assert.IsFalse(hasEntity);
}
}
}