Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Resolved Call to CompleteAllJobs() necessary to avoid error when integration testing

Discussion in 'Physics for ECS' started by florianhanke, Jan 2, 2021.

  1. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Hi Physics team

    When trying to test my systems that perform ray casts (see https://forum.unity.com/threads/testing-systems-performing-raycasts.1031335/), I noticed that while the test ran successfully, an error was thrown in TearDown. To avoid the error, I need to run EntityManager#CompleteAllJobs(), to Complete() all jobs.

    The error is: "TearDown : System.InvalidOperationException : The previously scheduled job Solver: parallelBuildJacobiansJob reads from the Unity.Collections.NativeArray`1[Unity.Physics.RigidBody] ParallelBuildJacobiansJob.World.CollisionWorld.m_Bodies. You must call JobHandle.Complete() on the job Solver: parallelBuildJacobiansJob, before you can deallocate the Unity.Collections.NativeArray`1[Unity.Physics.RigidBody] safely."

    I believe the test should run without me having to explicitly call EntityManager#CompleteAllJobs. It's possible I am not setting up the Physics systems correctly, too or may be making a wrong assumption, so may be my bad, of course. I did not find any tests in the Physics package that perform such a type of test.

    In the test, I call
    World.Update()
    to run a single update cycle. See the TearDown method for the issue:
    Code (CSharp):
    1.  
    2. [TestFixture]
    3. public class SelectAimSystemTest : BaseTest<SelectAimSystem>
    4. {
    5.     [TearDown]
    6.     public override void TearDown()
    7.     {
    8.         // Needed otherwise this error is thrown:
    9.         // TearDown : System.InvalidOperationException : The previously scheduled job Solver:ParallelBuildJacobiansJob reads from the Unity.Collections.NativeArray`1[Unity.Physics.RigidBody] ParallelBuildJacobiansJob.World.CollisionWorld.m_Bodies. You must call JobHandle.Complete() on the job Solver:ParallelBuildJacobiansJob, before you can deallocate the Unity.Collections.NativeArray`1[Unity.Physics.RigidBody] safely.
    10.         em.CompleteAllJobs();
    11.    
    12.         base.TearDown();
    13.     }
    14.  
    15.     [Test]
    16.     public void selectsClosestTargetTest()
    17.     {
    18.         var aimingEntity = em.CreateEntity(
    19.                                            typeof(Translation),
    20.                                            typeof(Rotation),
    21.                                            typeof(LocalToWorld),
    22.                                            typeof(FactionComponent),
    23.                                            typeof(Aiming),
    24.                                            typeof(PhysicsVelocity),
    25.                                            typeof(PhysicsCollider)
    26.                                           );
    27.  
    28.         var aimingTranslation = new float3(10, 0, 0);
    29.         em.SetComponentData(
    30.                             aimingEntity,
    31.                             new Translation
    32.                             {
    33.                                 Value = aimingTranslation
    34.                             }
    35.                            );
    36.         em.SetComponentData(
    37.                             aimingEntity,
    38.                             new FactionComponent
    39.                             {
    40.                                 Value = (byte) Factions.Id.Allies
    41.                             }
    42.                            );
    43.         em.SetComponentData(
    44.                             aimingEntity,
    45.                             new LocalToWorld
    46.                             {
    47.                                 Value = new float4x4(
    48.                                                      quaternion.identity,
    49.                                                      aimingTranslation
    50.                                                     )
    51.                             }
    52.                            );
    53.         em.SetComponentData(
    54.                             aimingEntity,
    55.                             new PhysicsCollider
    56.                             {
    57.                                 Value = SphereCollider.Create(
    58.                                                               new SphereGeometry
    59.                                                               {
    60.                                                                   Center = new float3(0),
    61.                                                                   Radius = 0.5f
    62.                                                               }
    63.                                                              )
    64.                             }
    65.                            );
    66.  
    67.         var targetEntity1 = em.CreateEntity(
    68.                                             typeof(Translation),
    69.                                             typeof(Rotation),
    70.                                             typeof(LocalToWorld),
    71.                                             typeof(FactionComponent),
    72.                                             typeof(PhysicsVelocity),
    73.                                             typeof(PhysicsCollider)
    74.                                            );
    75.         var targetTranslation1 = new float3(10, 0, 10);
    76.         em.SetComponentData(
    77.                             targetEntity1,
    78.                             new Translation
    79.                             {
    80.                                 Value = targetTranslation1
    81.                             }
    82.                            );
    83.         em.SetComponentData(
    84.                             targetEntity1,
    85.                             new FactionComponent
    86.                             {
    87.                                 Value = (byte) Factions.Id.Axis
    88.                             }
    89.                            );
    90.         em.SetComponentData(
    91.                             targetEntity1,
    92.                             new LocalToWorld
    93.                             {
    94.                                 Value = new float4x4(
    95.                                                      quaternion.identity,
    96.                                                      targetTranslation1
    97.                                                     )
    98.                             }
    99.                            );
    100.         var targetCollider1 = SphereCollider.Create(
    101.                                                     new SphereGeometry
    102.                                                     {
    103.                                                         Center = new float3(0),
    104.                                                         Radius = 0.5f
    105.                                                     }
    106.                                                    );
    107.         em.SetComponentData(
    108.                             targetEntity1,
    109.                             new PhysicsCollider
    110.                             {
    111.                                 Value = targetCollider1
    112.                             }
    113.                            );
    114.  
    115.         var aiming = em.GetComponentData<Aiming>(aimingEntity);
    116.         Assert.AreEqual(new float3(0), aiming.globalDirection);
    117.  
    118.         World.Update();
    119.  
    120.         aiming = em.GetComponentData<Aiming>(aimingEntity);
    121.         Assert.AreEqual(new float3(0, 0, 10), aiming.globalDirection);
    122.     }
    123. }
    124.  

    This is the test base class where I set up the basic systems:
    Code (CSharp):
    1. public class BaseTest<T> : ECSTestsFixture where T : ComponentSystemBase
    2. {
    3.     protected EntityManager em;
    4.  
    5.     [SetUp]
    6.     public override void Setup()
    7.     {
    8.         base.Setup();
    9.  
    10.         em = m_Manager;
    11.  
    12.         var systems = new List<Type>
    13.         {
    14.             // Defaults are added in DefaultWorldInitialization.
    15.             // var initializationSystemGroup = world.GetOrCreateSystem<InitializationSystemGroup>();
    16.             // var simulationSystemGroup = world.GetOrCreateSystem<SimulationSystemGroup>();
    17.             // var presentationSystemGroup = world.GetOrCreateSystem<PresentationSystemGroup>();
    18.        
    19.             typeof(BeginInitializationEntityCommandBufferSystem),
    20.             typeof(EndInitializationEntityCommandBufferSystem),
    21.        
    22.             typeof(BeginFixedStepSimulationEntityCommandBufferSystem),
    23.             typeof(FixedStepSimulationSystemGroup),
    24.             typeof(EndFixedStepSimulationEntityCommandBufferSystem),
    25.        
    26.             typeof(BeginSimulationEntityCommandBufferSystem),
    27.             typeof(EndSimulationEntityCommandBufferSystem),
    28.        
    29.             typeof(LateSimulationSystemGroup),
    30.        
    31.             typeof(BeginPresentationEntityCommandBufferSystem),
    32.        
    33.             typeof(BuildPhysicsWorld),
    34.             typeof(StepPhysicsWorld),
    35.             typeof(ExportPhysicsWorld),
    36.             typeof(EndFramePhysicsSystem),
    37.        
    38.             typeof(ServerPerformPhysicsGroup),
    39.        
    40.             typeof(T),
    41.         };
    42.         DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(World, systems);
    43.  
    44.         World.SetTime(new TimeData(1.234, 0.016f));
    45.     }
    46. }

    P.S: Unity.Physics 0.5.1-preview.2
     
    Last edited: Jan 2, 2021
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,536
    When unit testing you always have to call a Complete on something. Either a specific system dependency, a buffer system or CompleteAllJobs.

    World.Update() doesn't complete any jobs.
     
  3. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Ah, yes, thanks! Slightly annoyed at myself, since I know this – somehow believed it was an issue of Physics.

    I do believe though, that it is the library's ECSTestsFixture#TearDown method's job to ensure all jobs are completed, since it also destroys the world (as a nice side effect helping people avoid losing time).