encompass-cs/test/EngineTest.cs

1455 lines
46 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<MockComponent> resultComponents;
static MockComponent resultComponent;
static List<MockMessage> resultMessages = new List<MockMessage>();
[Reads(typeof(MockComponent))]
public class ReadComponentsTestEngine : Engine
{
public override void Update(double dt)
{
resultComponents = ReadComponents<MockComponent>().ToList();
}
}
static List<(MockComponent, Entity)> resultComponentsIncludingEntity;
static (MockComponent, Entity) resultComponentIncludingEntity;
[Reads(typeof(MockComponent))]
public class ReadComponentsIncludingEntityEngine : Engine
{
public override void Update(double dt)
{
resultComponentsIncludingEntity = ReadComponentsIncludingEntity<MockComponent>().ToList();
}
}
[Reads(typeof(MockComponent))]
public class ReadComponentTestEngine : Engine
{
public override void Update(double dt)
{
resultComponent = ReadComponent<MockComponent>();
}
}
[Reads(typeof(MockComponent))]
public class ReadComponentIncludingEntityEngine : Engine
{
public override void Update(double dt)
{
resultComponentIncludingEntity = ReadComponentIncludingEntity<MockComponent>();
}
}
[Test]
public void ReadComponents()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadComponentsTestEngine());
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
MockComponent mockComponentB;
mockComponentB.myInt = 1;
mockComponentB.myString = "howdy";
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SetComponent(entityB, mockComponentB);
var world = worldBuilder.Build();
world.Update(0.01f);
resultComponents.Should().Contain(mockComponent);
resultComponents.Should().Contain(mockComponentB);
}
[Test]
public void ReadComponentsIncludingEntity()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadComponentsIncludingEntityEngine());
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
MockComponent mockComponentB;
mockComponentB.myInt = 1;
mockComponentB.myString = "howdy";
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SetComponent(entityB, mockComponentB);
var world = worldBuilder.Build();
world.Update(0.01f);
var resultComponentValues = resultComponentsIncludingEntity.Select((kv) => kv.Item1);
resultComponentValues.Should().Contain(mockComponent);
resultComponentValues.Should().Contain(mockComponentB);
var resultEntities = resultComponentsIncludingEntity.Select((kv) => kv.Item2);
resultEntities.Should().Contain(entity);
resultEntities.Should().Contain(entityB);
resultComponentsIncludingEntity.Should().Contain((mockComponent, entity));
resultComponentsIncludingEntity.Should().Contain((mockComponentB, entityB));
}
[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.SetComponent(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();
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
MockComponent mockComponentB;
mockComponentB.myInt = 1;
mockComponentB.myString = "howdy";
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SetComponent(entityB, mockComponentB);
var world = worldBuilder.Build();
world.Update(0.01);
Assert.That(resultComponent, Is.EqualTo(mockComponent).Or.EqualTo(mockComponentB));
}
[Test]
public void ReadComponentWithEntity()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadComponentIncludingEntityEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
worldBuilder.SetComponent(entity, mockComponent);
var world = worldBuilder.Build();
world.Update(0.01f);
(mockComponent, entity).Should().BeEquivalentTo(resultComponentIncludingEntity);
}
[Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
public class UpdateComponentTestEngine : Engine
{
public override void Update(double dt)
{
var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
component.myInt = 420;
component.myString = "blaze it";
SetComponent(entity, 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.SetComponent(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 (component, entity) = ReadComponentIncludingEntity<MockComponent>();
component.myInt = 420;
component.myString = "blaze it";
SetComponent(entity, component);
component = ReadComponent<MockComponent>();
}
}
[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.SetComponent(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 update 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);
}
}
[Receives(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;
[Receives(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<IllegalSendException>(() => world.Update(0.01f));
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to send 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);
}
}
[Receives(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.SetComponent(entity, new MockComponent());
var world = worldBuilder.Build();
world.Update(0.01);
}
static (MockComponent, Entity) pairA;
static (MockComponent, Entity) pairB;
[Reads(typeof(MockComponent))]
class SameValueComponentReadEngine : Engine
{
public override void Update(double dt)
{
var components = ReadComponentsIncludingEntity<MockComponent>();
pairA = components.First();
pairB = components.Last();
}
}
// Tests that components with identical values should be distinguishable by their entities
[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.SetComponent(entity, componentA);
var entityB = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityB, componentB);
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.That(EngineTest.pairA, Is.Not.EqualTo(EngineTest.pairB));
Assert.That(EngineTest.pairA.Item1, Is.EqualTo(EngineTest.pairB.Item1));
}
static IEnumerable<(MockComponent, Entity)> emptyComponentReadResult;
[Reads(typeof(MockComponent))]
class ReadEmptyMockComponentsEngine : Engine
{
public override void Update(double dt)
{
emptyComponentReadResult = ReadComponentsIncludingEntity<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 (component, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
{
Destroy(entity);
}
}
}
static List<(MockComponent, Entity)> results;
[Reads(typeof(MockComponent))]
class ReaderEngine : Engine
{
public override void Update(double dt)
{
results = ReadComponentsIncludingEntity<MockComponent>().ToList();
}
}
[Test]
public void DestroyEntity()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new DestroyerEngine());
worldBuilder.AddEngine(new ReaderEngine());
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
DestroyerComponent destroyerComponent;
MockComponent mockComponent;
mockComponent.myInt = 2;
mockComponent.myString = "blah";
worldBuilder.SetComponent(entity, destroyerComponent);
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SetComponent(entityB, destroyerComponent);
worldBuilder.SetComponent(entityB, mockComponent);
worldBuilder.SetComponent(entityC, mockComponent);
var world = worldBuilder.Build();
world.Update(0.01);
world.Update(0.01);
Assert.That(results, Does.Not.Contain((mockComponent, entity)));
Assert.That(results, Does.Not.Contain((mockComponent, entityB)));
Assert.That(results, Does.Contain((mockComponent, entityC)));
}
[Receives(typeof(DestroyComponentMessage))]
class DestroyEntityEngine : Engine
{
public override void Update(double dt)
{
foreach (var message in ReadMessages<DestroyComponentMessage>())
{
Destroy(message.entity);
}
}
}
[Test]
public void DestroyEntityWithoutID()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AddComponentEngine());
worldBuilder.AddEngine(new DestroyEntityEngine());
worldBuilder.AddEngine(new ReaderEngine());
var mockComponent = new MockComponent { };
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity });
var world = worldBuilder.Build();
world.Update(0.01);
Assert.DoesNotThrow(() => world.Update(0.01));
Assert.That(results, Does.Not.Contain((mockComponent, entity)));
}
[Reads(typeof(DestroyerComponent), typeof(MockComponent))]
class DestroyAndAddComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var (componentPair, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
{
RemoveComponent<MockComponent>(entity);
Destroy(entity);
}
}
}
[Test]
public void DestroyEntityWhileRemovingComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new DestroyAndAddComponentEngine());
worldBuilder.AddEngine(new ReaderEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new DestroyerComponent());
worldBuilder.SetComponent(entity, new MockComponent());
var world = worldBuilder.Build();
Assert.DoesNotThrow(() => world.Update(0.01));
}
[Reads(typeof(MockComponent))]
[WritesPending(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class AddAndRemoveMockComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var (mockComponent, entity) in ReadComponentsIncludingEntity<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
SetComponent(entity, new MockComponent());
}
}
}
static Entity entityResult;
[ReadsPending(typeof(MockComponent))]
class GetEntityFromPendingReadComponents : Engine
{
public override void Update(double dt)
{
var (_, entity) = ReadComponentIncludingEntity<MockComponent>();
}
}
[Test]
public void GetEntityFromPendingComponentID()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AddAndRemoveMockComponentEngine());
worldBuilder.AddEngine(new GetEntityFromPendingReadComponents());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
var world = worldBuilder.Build();
Assert.DoesNotThrow(() => world.Update(0.01));
}
[Reads(typeof(MockComponent))]
class DelayedMessageEngine : Engine
{
public override void Update(double dt)
{
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
SendMessage(new MockMessage { }, 1);
}
}
}
[Test]
public void EngineSendMessageDelayed()
{
resultMessages.Clear();
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
worldBuilder.AddEngine(new DelayedMessageEngine());
worldBuilder.AddEngine(new MessageReadEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var world = worldBuilder.Build();
world.Update(0.01);
resultMessages.Should().BeEmpty();
world.Update(0.5);
resultMessages.Should().BeEmpty();
world.Update(0.5);
resultMessages.Should().BeEmpty();
world.Update(2);
resultMessages.Should().NotBeEmpty();
resultMessages.First().Should().BeOfType<MockMessage>();
}
[Reads(typeof(MockComponent))]
class DelayedMessageIgnoringTimeDilationEngine : Engine
{
public override void Update(double dt)
{
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
SendMessageIgnoringTimeDilation(new MockMessage { }, 1);
}
}
}
[Test]
public void EngineSendMessageDelayedIgnoringTimeDilation()
{
resultMessages.Clear();
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
worldBuilder.AddEngine(new DelayedMessageIgnoringTimeDilationEngine());
worldBuilder.AddEngine(new MessageReadEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var world = worldBuilder.Build();
world.Update(0.01);
resultMessages.Should().BeEmpty();
world.Update(0.5);
resultMessages.Should().BeEmpty();
world.Update(0.5);
resultMessages.Should().NotBeEmpty();
resultMessages.First().Should().BeOfType<MockMessage>();
}
[Receives(typeof(MockMessage))]
[WritesPending(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class ActivateComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var message in ReadMessages<MockMessage>())
{
var entity = CreateEntity();
SetComponent(entity, new MockComponent { });
}
}
}
[ReadsPending(typeof(MockComponent))]
class RemoveComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var entity in ReadEntities<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
}
}
}
[Test]
public void EngineAddAndRemoveComponentSameFrame()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateComponentEngine());
worldBuilder.AddEngine(new RemoveComponentEngine());
worldBuilder.SendMessage(new MockMessage { });
var world = worldBuilder.Build();
Assert.DoesNotThrow(() => world.Update(0.01));
}
struct DestroyComponentMessage : IMessage { public Entity entity; }
[Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
class AddComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var entity in ReadEntities<MockComponent>())
{
SetComponent(entity, new MockComponent { });
}
}
}
static Entity readEntity;
[Reads(typeof(MockComponent))]
class ReadEntityByComponentTypeEngine : Engine
{
public override void Update(double dt)
{
readEntity = ReadEntity<MockComponent>();
}
}
[Test]
public void GetEntityByComponentType()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var world = worldBuilder.Build();
world.Update(0.01);
entity.Should().BeEquivalentTo(readEntity);
}
static Entity[] readEntities;
[Reads(typeof(MockComponent))]
class ReadEntitiesWithComponentTypeEngine : Engine
{
public override void Update(double dt)
{
readEntities = ReadEntities<MockComponent>().ToArray();
}
}
[Test]
public void ReadEntities()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
worldBuilder.AddEngine(new DestroyAllWithEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var entityB = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityB, new MockComponent { });
var world = worldBuilder.Build();
world.Update(0.01);
readEntities.Should().Contain(entity);
readEntities.Should().Contain(entityB);
}
[Reads(typeof(MockComponent))]
class DestroyWithEngine : Engine
{
public override void Update(double dt)
{
if (SomeComponent<MockComponent>())
{
DestroyWith<MockComponent>();
}
}
}
[Test]
public void DestroyWith()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
worldBuilder.AddEngine(new DestroyWithEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var world = worldBuilder.Build();
world.Update(0.01);
world.Update(0.01); // update twice so the read happens after destroy
readEntities.Should().BeEmpty();
}
[Reads(typeof(MockComponent))]
class DestroyAllWithEngine : Engine
{
public override void Update(double dt)
{
DestroyAllWith<MockComponent>();
}
}
[Test]
public void DestroyAllWith()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
worldBuilder.AddEngine(new DestroyAllWithEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var entityB = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityB, new MockComponent { });
var world = worldBuilder.Build();
world.Update(0.01);
world.Update(0.01); // update twice so the read happens after destroy
readEntities.Should().BeEmpty();
}
[Reads(typeof(MockComponent))]
class RemoveComponentByTypeEngine : Engine
{
public override void Update(double dt)
{
foreach (var (_, entity) in ReadComponentsIncludingEntity<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
}
}
}
[Test]
public void RemoveComponentByType()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadComponentsTestEngine());
worldBuilder.AddEngine(new RemoveComponentByTypeEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var entityB = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
var world = worldBuilder.Build();
world.Update(0.01);
world.Update(0.01);
resultComponents.Should().BeEmpty();
}
static double dilatedDeltaTime;
class ActivateTimeDilationEngine : Engine
{
public override void Update(double dt)
{
if (!TimeDilationActive)
{
ActivateTimeDilation(0.2, 1, 1, 1);
}
else
{
dilatedDeltaTime = dt;
}
}
}
[Test]
public void ActivateTimeDilation()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
var world = worldBuilder.Build();
world.Update(0.01); // activate time dilation
world.Update(0.5);
dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
world.Update(0.5);
dilatedDeltaTime.Should().BeApproximately(0.1, 0.01);
world.Update(1);
world.Update(0.5);
dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
}
class ActivateTimeDilationLowerFactorEngine : Engine
{
private bool activated = false;
public override void Update(double dt)
{
if (!activated)
{
ActivateTimeDilation(0.2, 1, 1, 1);
activated = true;
}
}
}
class ActivateTimeDilationHigherFactorEngine : Engine
{
private bool activated = false;
public override void Update(double dt)
{
if (!activated)
{
ActivateTimeDilation(0.5, 1, 1, 1);
activated = true;
}
}
}
static bool timeDilationActive;
class ReadDilatedDeltaTimeEngine : Engine
{
public override void Update(double dt)
{
dilatedDeltaTime = dt;
timeDilationActive = TimeDilationActive;
}
}
[Test]
public void MultipleActivateTimeDilation()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadDilatedDeltaTimeEngine());
worldBuilder.AddEngine(new ActivateTimeDilationLowerFactorEngine());
worldBuilder.AddEngine(new ActivateTimeDilationHigherFactorEngine());
var world = worldBuilder.Build();
world.Update(0.01); // activate time dilation
world.Update(0.5); // 0.3 and 0.375
dilatedDeltaTime.Should().BeApproximately(0.3375, 0.01);
timeDilationActive.Should().BeTrue();
world.Update(5);
dilatedDeltaTime.Should().BeApproximately(5, 0.01);
timeDilationActive.Should().BeFalse();
}
static double undilatedDeltaTime;
[IgnoresTimeDilation]
class IgnoresTimeDilationEngine : Engine
{
public override void Update(double dt)
{
undilatedDeltaTime = dt;
}
}
[Test]
public void IgnoresTimeDilation()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
worldBuilder.AddEngine(new IgnoresTimeDilationEngine());
var world = worldBuilder.Build();
world.Update(0.01); // activate time dilation
world.Update(0.5);
undilatedDeltaTime.Should().Be(0.5);
}
public class QueryTests
{
struct MockComponentB : IComponent { }
struct MockComponentC : IComponent { }
struct MockComponentD : IComponent { }
[Reads(typeof(MockComponent), typeof(MockComponentB))]
class EntityQueryWithComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>().With<MockComponentB>());
}
}
[Test]
public void EntitiesWithComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
worldBuilder.SetComponent(entity, new MockComponentB());
worldBuilder.SetComponent(entityB, new MockComponent());
worldBuilder.SetComponent(entityB, new MockComponentB());
worldBuilder.SetComponent(entityC, new MockComponentB());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new EntityQueryWithComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB });
}
[Reads(typeof(MockComponent))]
class EntityQueryWithoutComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithoutComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().Without<MockComponent>());
}
}
[Test]
public void EntitiesWithoutComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
worldBuilder.SetComponent(entity, new MockComponentB());
worldBuilder.SetComponent(entityB, new MockComponent());
worldBuilder.SetComponent(entityB, new MockComponentB());
worldBuilder.SetComponent(entityC, new MockComponentB());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new EntityQueryWithoutComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityC });
}
[Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))]
class EntityQueryWithandWithoutComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithandWithoutComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>()
.With<MockComponentB>()
.Without<MockComponentD>());
}
}
[Test]
public void EntitiesWithAndWithoutComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
var entityD = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
worldBuilder.SetComponent(entity, new MockComponentB());
worldBuilder.SetComponent(entity, new MockComponentD());
worldBuilder.SetComponent(entityB, new MockComponent());
worldBuilder.SetComponent(entityC, new MockComponent());
worldBuilder.SetComponent(entityC, new MockComponentB());
worldBuilder.SetComponent(entityC, new MockComponentC());
worldBuilder.SetComponent(entityC, new MockComponentD());
worldBuilder.SetComponent(entityD, new MockComponent());
worldBuilder.SetComponent(entityD, new MockComponentB());
worldBuilder.SetComponent(entityD, new MockComponentC());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new EntityQueryWithandWithoutComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD });
}
[Reads(typeof(MockComponent))]
[WritesPending(typeof(MockComponentB))]
[Writes(typeof(MockComponentB), 0)]
class AddPendingComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var entity in QueryEntities().With<MockComponent>())
{
SetComponent(entity, new MockComponentB());
}
}
}
[ReadsPending(typeof(MockComponentB))]
class EntityQueryWithPendingComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithPendingComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().With<MockComponentB>());
}
}
[Test]
public void EntitiesWithPendingComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new AddPendingComponentEngine());
worldBuilder.AddEngine(new EntityQueryWithPendingComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entity });
}
[ReadsPending(typeof(MockComponentB))]
class EntityQueryWithoutPendingComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithoutPendingComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().Without<MockComponentB>());
}
}
[Test]
public void EntitiesWithoutPendingComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new AddPendingComponentEngine());
worldBuilder.AddEngine(new EntityQueryWithoutPendingComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
}
[Reads(typeof(MockComponentC), typeof(MockComponentD))]
[WritesPending(typeof(MockComponent), typeof(MockComponentB))]
[Writes(typeof(MockComponent), 0)]
[Writes(typeof(MockComponentB), 0)]
class ConditionallyAddPendingComponentsEngine : Engine
{
public override void Update(double dt)
{
foreach (var entity in QueryEntities().With<MockComponentC>())
{
SetComponent(entity, new MockComponent());
}
foreach (var entity in QueryEntities().With<MockComponentD>())
{
SetComponent(entity, new MockComponent());
SetComponent(entity, new MockComponentB());
}
}
}
[ReadsPending(typeof(MockComponent), typeof(MockComponentB))]
class EntityQueryWithAndWithoutPendingComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithAndWithoutPendingComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>().Without<MockComponentB>());
}
}
[Test]
public void EntitiesWithAndWithoutPendingComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityB, new MockComponentC());
worldBuilder.SetComponent(entityC, new MockComponentD());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new ConditionallyAddPendingComponentsEngine());
worldBuilder.AddEngine(new EntityQueryWithAndWithoutPendingComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
}
[Reads(typeof(MockComponentC))]
[WritesPending(typeof(MockComponentB))]
[Writes(typeof(MockComponentB), 0)]
class ConditionallyAddPendingComponentEngine : Engine
{
public override void Update(double dt)
{
foreach (var entity in QueryEntities().With<MockComponentC>())
{
SetComponent(entity, new MockComponentB());
}
}
}
[ReadsPending(typeof(MockComponentB))]
[Reads(typeof(MockComponent))]
class EntityQueryWithPendingAndNonPendingComponents : Engine
{
private List<Entity> entities;
public EntityQueryWithPendingAndNonPendingComponents(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>().With<MockComponentB>());
}
}
[Test]
public void EntitiesWithPendingAndNonPendingComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityB, new MockComponent());
worldBuilder.SetComponent(entityB, new MockComponentC());
worldBuilder.SetComponent(entityC, new MockComponentD());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new ConditionallyAddPendingComponentEngine());
worldBuilder.AddEngine(new EntityQueryWithPendingAndNonPendingComponents(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
}
}
}
}