using NUnit.Framework; using FluentAssertions; using Encompass; using System.Runtime.CompilerServices; namespace Tests { public class ComponentTests { struct MockComponent : IComponent { public int myInt; } struct EntityMessage : IMessage { public Entity entity; } static MockComponent gottenMockComponent; [Receives(typeof(EntityMessage))] [Reads(typeof(MockComponent))] class GetMockComponentEngine : Engine { public override void Update(double dt) { foreach (ref readonly var entityMessage in ReadMessages<EntityMessage>()) { gottenMockComponent = GetComponent<MockComponent>(entityMessage.entity); } } } struct AddComponentTestMessage : IMessage { public Entity entity; public MockComponent mockComponent; } [Receives(typeof(AddComponentTestMessage))] [Reads(typeof(MockComponent))] class AddComponentTestEngine : Engine { public override void Update(double dt) { foreach (ref readonly var addComponentTestMessage in ReadMessages<AddComponentTestMessage>()) { Assert.IsTrue(HasComponent<MockComponent>(addComponentTestMessage.entity)); ref readonly var gottenComponent = ref GetComponent<MockComponent>(addComponentTestMessage.entity); gottenComponent.Should().BeEquivalentTo(addComponentTestMessage.mockComponent); } } } [Test] public unsafe void AddComponent() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new AddComponentTestEngine()); var entity = worldBuilder.CreateEntity(); const string MyString = "hello"; MockComponent mockComponent; mockComponent.myInt = 3; worldBuilder.SetComponent(entity, mockComponent); AddComponentTestMessage addComponentTestMessage; addComponentTestMessage.entity = entity; addComponentTestMessage.mockComponent = mockComponent; worldBuilder.SendMessage(addComponentTestMessage); var world = worldBuilder.Build(); world.Update(0.01); world.Update(0.01); } [Test] public void SetMultipleComponentOfSameTypeOnEntity() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new ReadMockComponentEngine()); var entity = worldBuilder.CreateEntity(); worldBuilder.SetComponent(entity, new MockComponent { myInt = 20 }); worldBuilder.SetComponent(entity, new MockComponent { myInt = 50 }); worldBuilder.SetComponent(entity, new MockComponent { myInt = 40 }); var world = worldBuilder.Build(); world.Update(0.01); Assert.That(gottenMockComponent.myInt, Is.EqualTo(40)); } [Reads(typeof(MockComponent))] [Writes(typeof(MockComponent))] class OverwriteEngine : Engine { public override void Update(double dt) { foreach (ref readonly var entity in ReadEntities<MockComponent>()) { ref readonly var mockComponent = ref GetComponent<MockComponent>(entity); SetComponent(entity, new MockComponent { myInt = mockComponent.myInt + 1 }); } } } [Reads(typeof(MockComponent))] class ReadMockComponentEngine : Engine { public override void Update(double dt) { gottenMockComponent = ReadComponent<MockComponent>(); } } [Test] public void EngineOverwriteComponent() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new OverwriteEngine()); worldBuilder.AddEngine(new ReadMockComponentEngine()); var entity = worldBuilder.CreateEntity(); worldBuilder.SetComponent(entity, new MockComponent { myInt = 420 }); var world = worldBuilder.Build(); world.Update(0.01); Assert.That(gottenMockComponent.myInt, Is.EqualTo(420)); world.Update(0.01); Assert.That(gottenMockComponent.myInt, Is.EqualTo(421)); world.Update(0.01); Assert.That(gottenMockComponent.myInt, Is.EqualTo(422)); } [Reads(typeof(MockComponent))] [Writes(typeof(MockComponent))] class AddAndRemoveComponentEngine : Engine { public override void Update(double dt) { foreach (ref readonly var entity in ReadEntities<MockComponent>()) { ref readonly var mockComponent = ref GetComponent<MockComponent>(entity); SetComponent(entity, mockComponent); RemoveComponent<MockComponent>(entity); } } } [Test] public void AddMultipleComponentSameFrameAsRemove() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new AddAndRemoveComponentEngine()); var entity = worldBuilder.CreateEntity(); worldBuilder.SetComponent(entity, new MockComponent()); var world = worldBuilder.Build(); Assert.DoesNotThrow(() => world.Update(0.01)); } struct AddMockComponentMessage : IMessage { public Entity entity; public MockComponent mockComponent; } [Sends(typeof(AddMockComponentMessage))] class EmitMockComponentMessageEngine : Engine { private Entity entity; public EmitMockComponentMessageEngine(Entity entity) { this.entity = entity; } public override void Update(double dt) { MockComponent mockComponent; mockComponent.myInt = 10; AddMockComponentMessage addMockComponentMessage; addMockComponentMessage.entity = entity; addMockComponentMessage.mockComponent = mockComponent; SendMessage(addMockComponentMessage); } } [WritesImmediate(typeof(MockComponent))] [Receives(typeof(AddMockComponentMessage))] [Writes(typeof(MockComponent))] class AddMockComponentEngine : Engine { public override void Update(double dt) { foreach (ref readonly var message in ReadMessages<AddMockComponentMessage>()) { SetComponent(message.entity, message.mockComponent); } } } [ReadsImmediate(typeof(MockComponent))] class HasMockComponentEngine : Engine { private Entity entity; public HasMockComponentEngine(Entity entity) { this.entity = entity; } public override void Update(double dt) { Assert.IsTrue(HasComponent<MockComponent>(entity)); } } [Test] public void AddComponentAndReadSameFrame() { var worldBuilder = new WorldBuilder(); var entity = worldBuilder.CreateEntity(); worldBuilder.AddEngine(new EmitMockComponentMessageEngine(entity)); worldBuilder.AddEngine(new AddMockComponentEngine()); worldBuilder.AddEngine(new HasMockComponentEngine(entity)); var world = worldBuilder.Build(); world.Update(0.01); } [Test] public void GetComponent() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new GetMockComponentEngine()); var entity = worldBuilder.CreateEntity(); MockComponent mockComponent; mockComponent.myInt = 3; worldBuilder.SetComponent(entity, mockComponent); EntityMessage entityMessage; entityMessage.entity = entity; worldBuilder.SendMessage(entityMessage); var world = worldBuilder.Build(); world.Update(0.01); world.Update(0.01); Assert.AreEqual(mockComponent, gottenMockComponent); } struct HasComponentTestMessage : IMessage { public Entity entity; } [Receives(typeof(HasComponentTestMessage))] [Reads(typeof(MockComponent))] class HasComponentTestEngine : Engine { public override void Update(double dt) { foreach (ref readonly var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>()) { Assert.IsTrue(HasComponent<MockComponent>(hasComponentTestEngine.entity)); } } } [Test] public void HasComponent() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new HasComponentTestEngine()); var entity = worldBuilder.CreateEntity(); MockComponent mockComponent; mockComponent.myInt = 3; worldBuilder.SetComponent(entity, mockComponent); HasComponentTestMessage hasComponentTestMessage; hasComponentTestMessage.entity = entity; worldBuilder.SendMessage(hasComponentTestMessage); var world = worldBuilder.Build(); world.Update(0.01); } static bool hasComponentRuntimeTypeResult; [Receives(typeof(HasComponentTestMessage))] [Reads(typeof(MockComponent))] class HasComponentWithRuntimeTypeEngine : Engine { public override void Update(double dt) { foreach (ref readonly var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>()) { hasComponentRuntimeTypeResult = HasComponent(hasComponentTestEngine.entity, typeof(MockComponent)); } } } [Test] public void HasComponentWithRuntimeType() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new HasComponentWithRuntimeTypeEngine()); var entity = worldBuilder.CreateEntity(); MockComponent mockComponent; mockComponent.myInt = 3; worldBuilder.SetComponent(entity, mockComponent); HasComponentTestMessage hasComponentTestMessage; hasComponentTestMessage.entity = entity; worldBuilder.SendMessage(hasComponentTestMessage); var world = worldBuilder.Build(); world.Update(0.01); Assert.IsTrue(hasComponentRuntimeTypeResult); } [Test] public void HasComponentWithRuntimeTypeFalseWhenNoneHaveBeenCreated() { var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new HasComponentWithRuntimeTypeEngine()); var entity = worldBuilder.CreateEntity(); HasComponentTestMessage hasComponentTestMessage; hasComponentTestMessage.entity = entity; worldBuilder.SendMessage(hasComponentTestMessage); var world = worldBuilder.Build(); world.Update(0.01); Assert.IsFalse(hasComponentRuntimeTypeResult); } struct RemoveComponentTestMessage : IMessage { public Entity entity; } [Reads(typeof(MockComponent))] [Receives(typeof(RemoveComponentTestMessage))] [Writes(typeof(MockComponent))] class RemoveComponentTestEngine : Engine { public override void Update(double dt) { foreach (ref readonly var removeComponentMessage in ReadMessages<RemoveComponentTestMessage>()) { RemoveComponent<MockComponent>(removeComponentMessage.entity); } } } [Receives(typeof(RemoveComponentTestMessage))] [Sends(typeof(CheckHasMockComponentMessage))] class DoRemoveCheckEngine : Engine { private Entity entity; public DoRemoveCheckEngine(Entity entity) { this.entity = entity; } public override void Update(double dt) { if (SomeMessage<RemoveComponentTestMessage>()) { CheckHasMockComponentMessage checkHasMockComponentMessage; checkHasMockComponentMessage.entity = entity; checkHasMockComponentMessage.shouldHaveComponent = true; SendMessage(checkHasMockComponentMessage); } else { CheckHasMockComponentMessage checkHasMockComponentMessage; checkHasMockComponentMessage.entity = entity; checkHasMockComponentMessage.shouldHaveComponent = false; SendMessage(checkHasMockComponentMessage); } } } static bool hasComponentResult; [Receives(typeof(CheckHasMockComponentMessage))] [Reads(typeof(MockComponent))] class CheckHasMockComponentEngine : Engine { public override void Update(double dt) { foreach (ref readonly var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>()) { hasComponentResult = HasComponent<MockComponent>(checkHasMockComponentMessage.entity); } } } [Test] public void RemovedComponentShouldStillExistOnSameFrame() { var worldBuilder = new WorldBuilder(); var entity = worldBuilder.CreateEntity(); worldBuilder.AddEngine(new RemoveComponentTestEngine()); worldBuilder.AddEngine(new CheckHasMockComponentEngine()); worldBuilder.AddEngine(new DoRemoveCheckEngine(entity)); MockComponent mockComponent; mockComponent.myInt = 3; worldBuilder.SetComponent(entity, mockComponent); RemoveComponentTestMessage removeComponentMessage; removeComponentMessage.entity = entity; worldBuilder.SendMessage(removeComponentMessage); var world = worldBuilder.Build(); world.Update(0.01f); hasComponentResult.Should().BeTrue(); world.Update(0.01f); hasComponentResult.Should().BeFalse(); } struct CheckHasMockComponentMessage : IMessage { public Entity entity; public bool shouldHaveComponent; } [Receives(typeof(CheckHasMockComponentMessage))] [ReadsImmediate(typeof(MockComponent))] class CheckHasImmediateMockComponentEngine : Engine { public override void Update(double dt) { foreach (ref readonly var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>()) { Assert.IsTrue(HasComponent<MockComponent>(checkHasMockComponentMessage.entity)); } } } } }