Search Unity

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,761
    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).