using NUnit.Framework; using Encompass; using System.Collections.Concurrent; using Encompass.Exceptions; using System.Collections.Generic; namespace Tests { public class WorldBuilderTest { public class EngineCycleSimple { struct AMessage : IMessage { } struct BMessage : IMessage { } [Receives(typeof(AMessage))] [Sends(typeof(BMessage))] class AEngine : Engine { public override void Update(double dt) { BMessage message; this.SendMessage(message); } } [Receives(typeof(BMessage))] [Sends(typeof(AMessage))] class BEngine : Engine { public override void Update(double dt) { AMessage message; this.SendMessage(message); } } [Test] public void EngineCycle() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new AEngine()); worldBuilder.AddEngine(new BEngine()); Assert.Throws(() => worldBuilder.Build()); } } public class EngineCycleComplex { struct AMessage : IMessage { } struct BMessage : IMessage { } struct CMessage : IMessage { } struct DMessage : IMessage { } [Receives(typeof(AMessage))] [Sends(typeof(BMessage))] class AEngine : Engine { public override void Update(double dt) { BMessage message; this.SendMessage(message); } } [Receives(typeof(BMessage))] [Sends(typeof(CMessage))] class BEngine : Engine { public override void Update(double dt) { CMessage message; this.SendMessage(message); } } [Receives(typeof(CMessage))] [Sends(typeof(DMessage))] class CEngine : Engine { public override void Update(double dt) { DMessage message; this.SendMessage(message); } } [Receives(typeof(DMessage))] [Sends(typeof(AMessage))] class DEngine : Engine { public override void Update(double dt) { AMessage message; this.SendMessage(message); } } [Test] public void EngineCycle() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new AEngine()); worldBuilder.AddEngine(new BEngine()); worldBuilder.AddEngine(new CEngine()); worldBuilder.AddEngine(new DEngine()); Assert.Throws(() => worldBuilder.Build()); } } public class MutationConflict { struct AComponent : IComponent { } [Updates(typeof(AComponent))] class AEngine : Engine { public override void Update(double dt) { } } [Updates(typeof(AComponent))] class BEngine : Engine { public override void Update(double dt) { } } [Test] public void MutationConflictException() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new AEngine()); worldBuilder.AddEngine(new BEngine()); Assert.Throws(() => worldBuilder.Build()); } } public class EngineMessageSelfCycle { struct AMessage : IMessage { } [Receives(typeof(AMessage))] [Sends(typeof(AMessage))] class AEngine : Engine { public override void Update(double dt) { } } [Test] public void ThrowsError() { var worldBuilder = new WorldBuilder(); Assert.Throws(() => worldBuilder.AddEngine(new AEngine()), "Engine both sends and receives Message AMessage"); } } public class IllegalReadType { struct ANonMessage { } [Reads(typeof(ANonMessage))] class MyEngine : Engine { public override void Update(double dt) { } } [Test] public void ThrowsError() { var worldBuilder = new WorldBuilder(); Assert.Throws(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component"); } } public class IllegalWriteType { struct ANonMessage { } [Sends(typeof(ANonMessage))] class MyEngine : Engine { public override void Update(double dt) { } } [Test] public void ThrowsError() { var worldBuilder = new WorldBuilder(); Assert.Throws(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component"); } } public class LegalEngines { static ConcurrentQueue queue = new ConcurrentQueue(); struct AComponent : IComponent { } struct BComponent : IComponent { } struct AMessage : IMessage { } struct BMessage : IMessage { } struct CMessage : IMessage { } struct DMessage : IMessage { } [Sends(typeof(AMessage))] class AEngine : Engine { public override void Update(double dt) { queue.Enqueue(this); } } [Sends(typeof(BMessage))] class BEngine : Engine { public override void Update(double dt) { queue.Enqueue(this); } } [Receives(typeof(AMessage), typeof(BMessage))] [Sends(typeof(DMessage))] class CEngine : Engine { public override void Update(double dt) { queue.Enqueue(this); } } [Receives(typeof(DMessage))] class DEngine : Engine { public override void Update(double dt) { queue.Enqueue(this); } } [TestCase(true)] [TestCase(false)] public void EngineOrder(bool parallelUpdate) { var worldBuilder = new WorldBuilder(); var engineA = worldBuilder.AddEngine(new AEngine()); var engineB = worldBuilder.AddEngine(new BEngine()); var engineC = worldBuilder.AddEngine(new CEngine()); var engineD = worldBuilder.AddEngine(new DEngine()); Assert.DoesNotThrow(() => worldBuilder.Build()); var world = worldBuilder.Build(); if (parallelUpdate) { world.ParallelUpdate(0.01); } else { world.Update(0.01); } var order = new List(); while(!queue.IsEmpty) { Engine engine; queue.TryDequeue(out engine); order.Add(engine); } Assert.That(order.IndexOf(engineA), Is.LessThan(order.IndexOf(engineC))); Assert.That(order.IndexOf(engineB), Is.LessThan(order.IndexOf(engineC))); Assert.That(order.IndexOf(engineC), Is.LessThan(order.IndexOf(engineD))); } } } }