using System; using NUnit.Framework; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using UnityEngine.Scripting; namespace Unity.Entities.Tests { class ComponentSystemTests : ECSTestsFixture { class TestSystem : ComponentSystem { public bool Created = false; protected override void OnUpdate() { } protected override void OnCreate() { Created = true; } protected override void OnDestroy() { Created = false; } } class DerivedTestSystem : TestSystem { protected override void OnUpdate() { } } class ThrowExceptionSystem : TestSystem { protected override void OnCreate() { throw new System.Exception(); } protected override void OnUpdate() { } } class ScheduleJobAndDestroyArray : JobComponentSystem { NativeArray test = new NativeArray(10, Allocator.Persistent); struct Job : IJob { public NativeArray test; public void Execute() { } } protected override JobHandle OnUpdate(JobHandle inputDeps) { return new Job(){ test = test }.Schedule(inputDeps); } protected override void OnDestroy() { // We expect this to not throw an exception since the jobs scheduled // by this system should be synced before the system is destroyed test.Dispose(); } } [Test] public void Create() { var system = World.CreateSystem(); Assert.AreEqual(system, World.GetExistingSystem()); Assert.IsTrue(system.Created); } #if ENABLE_UNITY_COLLECTIONS_CHECKS [Test] public void ComponentSystem_CheckExistsAfterDestroy_CorrectMessage() { var destroyedSystem = World.CreateSystem(); World.DestroySystem(destroyedSystem); Assert.That(() => { destroyedSystem.ShouldRunSystem(); }, Throws.InvalidOperationException.With.Message.Contains("has already been destroyed")); } #endif #if ENABLE_UNITY_COLLECTIONS_CHECKS [Test] public void ComponentSystem_CheckExistsBeforeCreate_CorrectMessage() { var incompleteSystem = new TestSystem(); Assert.That(() => { incompleteSystem.ShouldRunSystem(); }, Throws.InvalidOperationException.With.Message.Contains("m_systemID is zero")); } #endif [Test] public void CreateAndDestroy() { var system = World.CreateSystem(); World.DestroySystem(system); Assert.AreEqual(null, World.GetExistingSystem()); Assert.IsFalse(system.Created); } [Test] public void GetOrCreateSystemReturnsSameSystem() { var system = World.GetOrCreateSystem(); Assert.AreEqual(system, World.GetOrCreateSystem()); } [Test] public void InheritedSystem() { var system = World.CreateSystem(); Assert.AreEqual(system, World.GetExistingSystem()); Assert.AreEqual(system, World.GetExistingSystem()); World.DestroySystem(system); Assert.AreEqual(null, World.GetExistingSystem()); Assert.AreEqual(null, World.GetExistingSystem()); Assert.IsFalse(system.Created); } #if !UNITY_DOTSPLAYER [Test] public void CreateNonSystemThrows() { Assert.Throws(() => { World.CreateSystem(typeof(Entity)); }); } [Test] public void GetOrCreateNonSystemThrows() { Assert.Throws(() => { World.GetOrCreateSystem(typeof(Entity)); }); } #endif [Test] public void OnCreateThrowRemovesSystem() { Assert.Throws(() => { World.CreateSystem(); }); Assert.AreEqual(null, World.GetExistingSystem()); } [Test] public void DestroySystemWhileJobUsingArrayIsRunningWorks() { var system = World.CreateSystem(); system.Update(); World.DestroySystem(system); } [Test] public void DisposeSystemComponentGroupThrows() { var system = World.CreateSystem(); var group = system.GetEntityQuery(typeof(EcsTestData)); Assert.Throws(() => group.Dispose()); } [Test] public void DestroySystemTwiceThrows() { var system = World.CreateSystem(); World.DestroySystem(system); Assert.Throws(() => World.DestroySystem(system) ); } [Test] public void CreateTwoSystemsOfSameType() { var systemA = World.CreateSystem(); var systemB = World.CreateSystem(); // CreateSystem makes a new system Assert.AreNotEqual(systemA, systemB); // Return first system Assert.AreEqual(systemA, World.GetOrCreateSystem()); } [Test] public void CreateTwoSystemsAfterDestroyReturnSecond() { var systemA = World.CreateSystem(); var systemB = World.CreateSystem(); World.DestroySystem(systemA); Assert.AreEqual(systemB, World.GetExistingSystem());; } [Test] public void CreateTwoSystemsAfterDestroyReturnFirst() { var systemA = World.CreateSystem(); var systemB = World.CreateSystem(); World.DestroySystem(systemB); Assert.AreEqual(systemA, World.GetExistingSystem());; } [Test] public void GetEntityQuery() { ComponentType[] ro_rw = { ComponentType.ReadOnly(), typeof(EcsTestData2) }; ComponentType[] rw_rw = { typeof(EcsTestData), typeof(EcsTestData2) }; ComponentType[] rw = { typeof(EcsTestData) }; var ro_rw0_system = EmptySystem.GetEntityQuery(ro_rw); var rw_rw_system = EmptySystem.GetEntityQuery(rw_rw); var rw_system = EmptySystem.GetEntityQuery(rw); Assert.AreEqual(ro_rw0_system, EmptySystem.GetEntityQuery(ro_rw)); Assert.AreEqual(rw_rw_system, EmptySystem.GetEntityQuery(rw_rw)); Assert.AreEqual(rw_system, EmptySystem.GetEntityQuery(rw)); Assert.AreEqual(3, EmptySystem.EntityQueries.Length); } [Test] public void GetComponentGroupArchetypeQuery() { var query1 = new ComponentType[] { typeof(EcsTestData) }; var query2 = new EntityQueryDesc { All = new ComponentType[] {typeof(EcsTestData)} }; var query3 = new EntityQueryDesc { All = new ComponentType[] {typeof(EcsTestData), typeof(EcsTestData2)} }; var group1 = EmptySystem.GetEntityQuery(query1); var group2 = EmptySystem.GetEntityQuery(query2); var group3 = EmptySystem.GetEntityQuery(query3); Assert.AreEqual(group1, EmptySystem.GetEntityQuery(query1)); Assert.AreEqual(group2, EmptySystem.GetEntityQuery(query2)); Assert.AreEqual(group3, EmptySystem.GetEntityQuery(query3)); Assert.AreEqual(2, EmptySystem.EntityQueries.Length); } [Test] public void GetComponentGroupComponentTypeArchetypeQueryEquality() { var query1 = new ComponentType[] { typeof(EcsTestData) }; var query2 = new EntityQueryDesc { All = new ComponentType[] {typeof(EcsTestData)} }; var query3 = new EntityQueryDesc { All = new [] {ComponentType.ReadWrite()} }; var group1 = EmptySystem.GetEntityQuery(query1); var group2 = EmptySystem.GetEntityQuery(query2); var group3 = EmptySystem.GetEntityQuery(query3); Assert.AreEqual(group1, group2); Assert.AreEqual(group2, group3); Assert.AreEqual(1, EmptySystem.EntityQueries.Length); } [Test] public void GetComponentGroupRespectsRWAccessInequality() { var query1 = new EntityQueryDesc { All = new [] {ComponentType.ReadOnly(), ComponentType.ReadWrite()} }; var query2 = new EntityQueryDesc { All = new [] {ComponentType.ReadOnly(), ComponentType.ReadOnly()} }; var group1 = EmptySystem.GetEntityQuery(query1); var group2 = EmptySystem.GetEntityQuery(query2); Assert.AreNotEqual(group1, group2); Assert.AreEqual(2, EmptySystem.EntityQueries.Length); } [Test] public void GetComponentGroupOrderIndependent() { var query1 = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestData2) }; var query2 = new ComponentType[] { typeof(EcsTestData2), typeof(EcsTestData) }; var group1 = EmptySystem.GetEntityQuery(query1); var group2 = EmptySystem.GetEntityQuery(query2); Assert.AreEqual(group1, group2); Assert.AreEqual(1, EmptySystem.EntityQueries.Length); var query3 = new EntityQueryDesc { All = new ComponentType[] {typeof(EcsTestData2), typeof(EcsTestData3)} }; var query4 = new EntityQueryDesc { All = new ComponentType[] {typeof(EcsTestData3), typeof(EcsTestData2)} }; var group3 = EmptySystem.GetEntityQuery(query3); var group4 = EmptySystem.GetEntityQuery(query4); Assert.AreEqual(group3, group4); Assert.AreEqual(2, EmptySystem.EntityQueries.Length); } //@TODO: Behaviour is a slightly dodgy... Should probably just ignore and return same as single typeof(EcsTestData) [Test] public void GetComponentGroupWithEntityThrows() { ComponentType[] e = { typeof(Entity), typeof(EcsTestData) }; EmptySystem.GetEntityQuery(e); Assert.Throws(() => EmptySystem.GetEntityQuery(e)); } [Test] public void GetComponentGroupWithDuplicates() { // Currently duplicates will create two seperate groups doing the same thing... ComponentType[] dup_1 = { typeof(EcsTestData2) }; ComponentType[] dup_2 = { typeof(EcsTestData2), typeof(EcsTestData3) }; var dup1_system = EmptySystem.GetEntityQuery(dup_1); var dup2_system = EmptySystem.GetEntityQuery(dup_2); Assert.AreEqual(dup1_system, EmptySystem.GetEntityQuery(dup_1)); Assert.AreEqual(dup2_system, EmptySystem.GetEntityQuery(dup_2)); Assert.AreEqual(2, EmptySystem.EntityQueries.Length); } [Test] public void UpdateDestroyedSystemThrows() { var system = EmptySystem; World.DestroySystem(system); Assert.Throws(system.Update); } } class Issue101 : ECSTestsFixture { [BurstCompile(CompileSynchronously = true)] struct Issue101Job : IJob { [WriteOnly] public NativeHashMap.Concurrent hashMap; [WriteOnly] public NativeQueue.Concurrent keys; public int Index; public void Execute() { byte hash = (byte) Index; hashMap.TryAdd(hash, hash); keys.Enqueue(hash); } } [Ignore("Passed off to compute team")] [Test] public void TestIssue101() { var hashMap = new NativeHashMap(100, Allocator.TempJob); var keys = new NativeQueue(Allocator.TempJob); try { var job = new Issue101Job() { hashMap = hashMap.ToConcurrent(), keys = keys.ToConcurrent(), Index = 1, }; job.Schedule(default(JobHandle)).Complete(); } finally { keys.Dispose(); hashMap.Dispose(); } } #if !UNITY_DOTSPLAYER public class NonPreservedTestSystem : ComponentSystem { public string m_Test; public NonPreservedTestSystem() { m_Test = ""; } //This is essentially what removing [Preserve] would accomplish with max code stripping. //public NonPreservedTestSystem(string inputParam) { m_Test = inputParam; } protected override void OnUpdate() {} } [Preserve] public class PreservedTestSystem : ComponentSystem { public string m_Test; public PreservedTestSystem() { m_Test = ""; } public PreservedTestSystem(string inputParam) { m_Test = inputParam; } protected override void OnUpdate() {} } [Test] public void CreateSystemInvalidParameters() { //Non-preserved but valid parameter Assert.That(() => { World.CreateSystem("test"); }, Throws.Exception.With.Message.Contains( "failed because CreateSystem parameters did not match its constructor.")); //Preserved but invalid parameter Assert.That(() => { World.CreateSystem(10); }, Throws.Exception.With.Message.Contains( "failed because CreateSystem parameters did not match its constructor.")); } [Test] public void CreateSystemValidParameters() { Assert.DoesNotThrow(() => { var system = World.CreateSystem("test"); World.Active.DestroySystem(system); }); } #endif } }