From 1ee5109eb96f4c89af200ecc97f69cc63024e2c7 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 30 Sep 2026 12:48:36 -0500 Subject: [PATCH 1/3] test: fix waits that could never time out in MessageOrdering and MultiClientConnectionApproval - MessageOrderingTests.RpcOnNetworkSpawn tracked the spawned object by GlobalObjectIdHash instead of NetworkObjectId, and its wait loop set the success flag after one pass because the continue sat inside the foreach. It now waits on the NetworkObjectId through WaitForConditionOrTimeOut. - SpawnChangeOwnership and ConnectionApprovalMismatchTest created a TimeoutHelper that was never passed to WaitForConditionOrTimeOut, so their timeout asserts could never fail. --- .../Assets/Tests/Runtime/MessageOrdering.cs | 38 +++++++++---------- .../Runtime/MultiClientConnectionApproval.cs | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 1a0af87ae2..006358c727 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -99,7 +99,7 @@ public IEnumerator SpawnChangeOwnership() // Wait until all objects have spawned. var timeoutHelper = new TimeoutHelper(); - yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == numClients + 1); + yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == numClients + 1, timeoutHelper); Assert.False(timeoutHelper.TimedOut, "Did not successfully spawn all expected NetworkObjects"); } @@ -203,6 +203,18 @@ public IEnumerator SpawnRpcDespawn() private ulong m_SpawnedNetworkObjectId; + private bool AllClientsSpawnedObject(NetworkManager[] clients) + { + foreach (var client in clients) + { + if (!client.SpawnManager.SpawnedObjects.ContainsKey(m_SpawnedNetworkObjectId)) + { + return false; + } + } + return true; + } + [UnityTest] public IEnumerator RpcOnNetworkSpawn() { @@ -258,28 +270,12 @@ public IEnumerator RpcOnNetworkSpawn() var serverNetworkObject = NetworkObject.InstantiateAndSpawn(m_Prefab, server); - m_SpawnedNetworkObjectId = serverNetworkObject.GlobalObjectIdHash; + m_SpawnedNetworkObjectId = serverNetworkObject.NetworkObjectId; // Make sure everyone spawns the object - var allClientsSpawnedObject = false; - var waitPeriod = new WaitForSeconds(1.0f / server.NetworkConfig.TickRate); - var timeout = Time.realtimeSinceStartup + 4.0f; - while (!allClientsSpawnedObject) - { - if (timeout < Time.realtimeSinceStartup) - { - Assert.Fail($"Timed out waiting for all clients to spawn {serverNetworkObject.name}!"); - } - foreach (var client in clients) - { - if (!client.SpawnManager.SpawnedObjects.ContainsKey(m_SpawnedNetworkObjectId)) - { - yield return waitPeriod; - continue; - } - } - allClientsSpawnedObject = true; - } + var timeoutHelper = new TimeoutHelper(); + yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => AllClientsSpawnedObject(clients), timeoutHelper); + Assert.False(timeoutHelper.TimedOut, $"Timed out waiting for all clients to spawn {serverNetworkObject.name}!"); // Wait until all objects have spawned. const int maxFrames = 240; diff --git a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs index 344090fefb..bec734fcd2 100644 --- a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs +++ b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs @@ -401,7 +401,7 @@ public IEnumerator ConnectionApprovalMismatchTest([Values(true, false)] bool ena } var timeoutHelper = new TimeoutHelper(); - yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => m_ClientDisconnectedInvocations == 3); + yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => m_ClientDisconnectedInvocations == 3, timeoutHelper); Assert.False(timeoutHelper.TimedOut, "Timed out waiting for clients to be disconnected!"); Assert.AreEqual(3, m_ClientDisconnectedInvocations); } From ce556e3d88ad0ca38f7ef92ea48ce09cab529a25 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 30 Sep 2026 16:20:29 -0500 Subject: [PATCH 2/3] test: keep the original wait budgets for the fixed timeouts The default TimeoutHelper waits 2 seconds. RpcOnNetworkSpawn's removed loop waited 4, and SpawnChangeOwnership and ConnectionApprovalMismatchTest previously fell back to the 8 second global helper. --- testproject/Assets/Tests/Runtime/MessageOrdering.cs | 4 ++-- .../Assets/Tests/Runtime/MultiClientConnectionApproval.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 006358c727..800d64b420 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -98,7 +98,7 @@ public IEnumerator SpawnChangeOwnership() serverNetworkObject.ChangeOwnership(clients[0].LocalClientId); // Wait until all objects have spawned. - var timeoutHelper = new TimeoutHelper(); + var timeoutHelper = new TimeoutHelper(8.0f); yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == numClients + 1, timeoutHelper); Assert.False(timeoutHelper.TimedOut, "Did not successfully spawn all expected NetworkObjects"); } @@ -273,7 +273,7 @@ public IEnumerator RpcOnNetworkSpawn() m_SpawnedNetworkObjectId = serverNetworkObject.NetworkObjectId; // Make sure everyone spawns the object - var timeoutHelper = new TimeoutHelper(); + var timeoutHelper = new TimeoutHelper(4.0f); yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => AllClientsSpawnedObject(clients), timeoutHelper); Assert.False(timeoutHelper.TimedOut, $"Timed out waiting for all clients to spawn {serverNetworkObject.name}!"); diff --git a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs index bec734fcd2..949363f82c 100644 --- a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs +++ b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs @@ -400,7 +400,7 @@ public IEnumerator ConnectionApprovalMismatchTest([Values(true, false)] bool ena Assert.Fail("Failed to start instances"); } - var timeoutHelper = new TimeoutHelper(); + var timeoutHelper = new TimeoutHelper(8.0f); yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => m_ClientDisconnectedInvocations == 3, timeoutHelper); Assert.False(timeoutHelper.TimedOut, "Timed out waiting for clients to be disconnected!"); Assert.AreEqual(3, m_ClientDisconnectedInvocations); From e356d55a48af661e0a4a39d64806994ea7b624d9 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Fri, 2 Oct 2026 14:54:39 -0500 Subject: [PATCH 3/3] test: convert MessageOrderingTests to a NetcodeIntegrationTest MessageOrderingTests created and tore down its NetworkManagers with NetcodeIntegrationTestHelpers and replicated logic that NetcodeIntegrationTest already provides, such as waiting for an object to spawn on every client. It now derives from NetcodeIntegrationTest, runs as both a host and a server, and uses WaitForSpawnedOnAllOrTimeOut and WaitForConditionOrTimeOut for every wait, so none of its waits can pass without the condition being met. test: use the NetcodeIntegrationTest spawn helpers in MessageOrderingTests SpawnChangeOwnership counted NetworkObjectTestComponent instances to detect the spawn; it now spawns with SpawnObject, waits with WaitForSpawnedOnAllOrTimeOut, and also checks that the client owns the object. SpawnRpcDespawn spawns with SpawnObject, takes its expected client count from TotalClients, and reports which of its conditions was not met through the WaitForConditionOrTimeOut error log. --- .../Assets/Tests/Runtime/MessageOrdering.cs | 355 +++++------------- 1 file changed, 101 insertions(+), 254 deletions(-) diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 800d64b420..a1fc00acf0 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Text; using NUnit.Framework; using TestProject.RuntimeTests.Support; using Unity.Netcode; @@ -9,319 +10,165 @@ namespace TestProject.RuntimeTests { - /// - /// TODO: This test needs to be converted to an integration test - /// - public class MessageOrderingTests + [TestFixture(HostOrServer.Host)] + [TestFixture(HostOrServer.Server)] + public class MessageOrderingTests : NetcodeIntegrationTest { - private GameObject m_Prefab; + // Must be 1 for these tests. + protected override int NumberOfClients => 1; - private NetworkManager m_ServerNetworkManager; - private NetworkManager[] m_ClientNetworkManagers; + private GameObject m_OwnershipPrefab; + private GameObject m_SpawnRpcDespawnPrefab; - [OneTimeSetUp] - public void OneTimeSetup() - { - // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. - NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); - // Excluding from unified tests. If deemed needed, update test, then remove. - NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); - } + public MessageOrderingTests(HostOrServer hostOrServer) : base(hostOrServer) { } - [UnitySetUp] - public IEnumerator SetUp() + private static void ResetStatics() { - // Make sure these static values are reset Support.SpawnRpcDespawn.ClientUpdateCount = 0; Support.SpawnRpcDespawn.ServerUpdateCount = 0; Support.SpawnRpcDespawn.ClientNetworkSpawnRpcCalled = false; Support.SpawnRpcDespawn.ExecuteClientRpc = false; - yield break; } - [UnityTearDown] - public IEnumerator Teardown() + protected override IEnumerator OnSetup() { - // Shutdown and clean up both of our NetworkManager instances - if (m_Prefab) - { - Object.Destroy(m_Prefab); - m_Prefab = null; - NetcodeIntegrationTestHelpers.Destroy(); - Support.SpawnRpcDespawn.ClientUpdateCount = 0; - Support.SpawnRpcDespawn.ServerUpdateCount = 0; - Support.SpawnRpcDespawn.ClientNetworkSpawnRpcCalled = false; - Support.SpawnRpcDespawn.ExecuteClientRpc = false; - } - yield break; + ResetStatics(); + return base.OnSetup(); } - [UnityTest] - public IEnumerator SpawnChangeOwnership() + protected override IEnumerator OnTearDown() { - const int numClients = 1; - Assert.True(NetcodeIntegrationTestHelpers.Create(numClients, out NetworkManager server, out NetworkManager[] clients)); - m_Prefab = new GameObject("Object"); - var networkObject = m_Prefab.AddComponent(); - m_Prefab.AddComponent(); + ResetStatics(); + return base.OnTearDown(); + } - // Make it a prefab - NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject); + protected override void OnServerAndClientsCreated() + { + m_OwnershipPrefab = CreateNetworkObjectPrefab("OwnershipObject"); - var validNetworkPrefab = new NetworkPrefab - { - Prefab = m_Prefab - }; - server.NetworkConfig.Prefabs.Add(validNetworkPrefab); - foreach (var client in clients) + m_SpawnRpcDespawnPrefab = CreateNetworkObjectPrefab("SpawnRpcDespawnObject"); + m_SpawnRpcDespawnPrefab.AddComponent(); + Support.SpawnRpcDespawn.TestStage = NetworkUpdateStage.EarlyUpdate; + base.OnServerAndClientsCreated(); + } + + /// + /// Adds a unique to each given . + /// + private List AddSpawnRpcDespawnHandlers(IEnumerable networkManagers) + { + var networkObject = m_SpawnRpcDespawnPrefab.GetComponent(); + var handlers = new List(); + foreach (var networkManager in networkManagers) { - client.NetworkConfig.Prefabs.Add(validNetworkPrefab); + var handler = new SpawnRpcDespawnInstanceHandler(networkObject.GlobalObjectIdHash, networkManager); + networkManager.PrefabHandler.AddHandler(networkObject, handler); + handlers.Add(handler); } + return handlers; + } - // Start the instances - if (!NetcodeIntegrationTestHelpers.Start(true, server, clients)) + private static bool AllHandlersSpawned(List handlers) + { + foreach (var handler in handlers) { - Debug.LogError("Failed to start instances"); - Assert.Fail("Failed to start instances"); + if (!handler.WasSpawned) + { + return false; + } } + return true; + } - // [Client-Side] Wait for a connection to the server - yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(clients, null, 512); - - // [Host-Side] Check to make sure all clients are connected - yield return NetcodeIntegrationTestHelpers.WaitForClientsConnectedToServer(server, clients.Length + 1, null, 512); + private static bool AllHandlersDestroyed(List handlers) + { + foreach (var handler in handlers) + { + if (!handler.WasDestroyed) + { + return false; + } + } + return true; + } - var serverObject = Object.Instantiate(m_Prefab, Vector3.zero, Quaternion.identity); - NetworkObject serverNetworkObject = serverObject.GetComponent(); - serverNetworkObject.NetworkManagerOwner = server; - serverNetworkObject.Spawn(); - serverNetworkObject.ChangeOwnership(clients[0].LocalClientId); + [UnityTest] + public IEnumerator SpawnChangeOwnership() + { + var nonAuthority = GetNonAuthorityNetworkManager(); + var authorityInstance = SpawnObject(m_OwnershipPrefab, GetAuthorityNetworkManager()).GetComponent(); + authorityInstance.ChangeOwnership(nonAuthority.LocalClientId); - // Wait until all objects have spawned. var timeoutHelper = new TimeoutHelper(8.0f); - yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == numClients + 1, timeoutHelper); - Assert.False(timeoutHelper.TimedOut, "Did not successfully spawn all expected NetworkObjects"); + yield return WaitForSpawnedOnAllOrTimeOut(authorityInstance, timeoutHelper); + AssertOnTimeout("Did not successfully spawn all expected NetworkObjects", timeoutHelper); + Assert.AreEqual(nonAuthority.LocalClientId, nonAuthority.SpawnManager.SpawnedObjects[authorityInstance.NetworkObjectId].OwnerClientId, + $"[Client-{nonAuthority.LocalClientId}] Does not own {authorityInstance.name}!"); } [UnityTest] public IEnumerator SpawnRpcDespawn() { var frameCountStart = Time.frameCount; - // Must be 1 for this test. - const int numClients = 1; - Assert.True(NetcodeIntegrationTestHelpers.Create(numClients, out m_ServerNetworkManager, out m_ClientNetworkManagers)); - m_Prefab = new GameObject("Object"); - m_Prefab.AddComponent(); - Support.SpawnRpcDespawn.TestStage = NetworkUpdateStage.EarlyUpdate; - var networkObject = m_Prefab.AddComponent(); + var clientHandlers = AddSpawnRpcDespawnHandlers(m_ClientNetworkManagers); - // Make it a prefab - NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject); - var clientHandlers = new List(); - //var handler = new SpawnRpcDespawnInstanceHandler(networkObject.GlobalObjectIdHash); - //server.PrefabHandler.AddHandler(networkObject.GlobalObjectIdHash, handler); - foreach (var client in m_ClientNetworkManagers) - { - var clientHandler = new SpawnRpcDespawnInstanceHandler(networkObject.GlobalObjectIdHash, client); - client.PrefabHandler.AddHandler(networkObject, clientHandler); - clientHandlers.Add(clientHandler); - } - - var validNetworkPrefab = new NetworkPrefab - { - Prefab = m_Prefab - }; - m_ServerNetworkManager.NetworkConfig.Prefabs.Add(validNetworkPrefab); - foreach (var client in m_ClientNetworkManagers) - { - client.NetworkConfig.Prefabs.Add(validNetworkPrefab); - } + SpawnObject(m_SpawnRpcDespawnPrefab, GetAuthorityNetworkManager()).GetComponent().Activate(); - // Start the instances - if (!NetcodeIntegrationTestHelpers.Start(true, m_ServerNetworkManager, m_ClientNetworkManagers)) + // Every client receives the client RPC, including the host's own client. + var expectedCount = Support.SpawnRpcDespawn.ClientUpdateCount + TotalClients; + bool AllClientRpcsReceivedAndHandled(StringBuilder errorLog) { - Debug.LogError("Failed to start instances"); - Assert.Fail("Failed to start instances"); - } - - // [Client-Side] Wait for a connection to the server - yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(m_ClientNetworkManagers, null, 512); - - // [Host-Side] Check to make sure all clients are connected - yield return NetcodeIntegrationTestHelpers.WaitForClientsConnectedToServer(m_ServerNetworkManager, m_ClientNetworkManagers.Length + 1, null, 512); - - var serverObject = Object.Instantiate(m_Prefab, Vector3.zero, Quaternion.identity); - NetworkObject serverNetworkObject = serverObject.GetComponent(); - serverNetworkObject.NetworkManagerOwner = m_ServerNetworkManager; - serverNetworkObject.Spawn(); - - SpawnRpcDespawn srdComponent = serverObject.GetComponent(); - srdComponent.Activate(); - - // Wait until all objects have spawned. - int expectedCount = Support.SpawnRpcDespawn.ClientUpdateCount + numClients + 1; // Clients plus host - int maxFrames = 240 + Time.frameCount; - var doubleCheckTime = Time.realtimeSinceStartup + 5.0f; - var clientCountReached = false; - var allHandlersSpawned = false; - var allHandlersDestroyed = false; - var waitForTick = new WaitForSeconds(1.0f / m_ServerNetworkManager.NetworkConfig.TickRate); - - while (!(allHandlersSpawned && clientCountReached && allHandlersDestroyed)) - { - clientCountReached = (Support.SpawnRpcDespawn.ClientUpdateCount == expectedCount); - foreach (var clientHandler in clientHandlers) + if (Support.SpawnRpcDespawn.ClientUpdateCount != expectedCount) { - allHandlersSpawned = clientHandler.WasSpawned; - allHandlersDestroyed = clientHandler.WasDestroyed; - if (!allHandlersSpawned || !allHandlersDestroyed) - { - break; - } + errorLog.Append($"Client count ({Support.SpawnRpcDespawn.ClientUpdateCount}) did not match the expected count ({expectedCount})!"); + return false; } - - if (Time.frameCount > maxFrames) + if (!AllHandlersSpawned(clientHandlers)) { - // This is here in the event a platform is running at a higher - // frame rate than expected - if (doubleCheckTime < Time.realtimeSinceStartup) - { - Assert.Fail("Did not successfully call all expected client RPCs"); - break; - } + errorLog.Append("Not all client-side handlers were spawned!"); + return false; } - - yield return waitForTick; - } - - Assert.True(allHandlersSpawned, $"Not all client-side handlers were spawned!"); - Assert.True(allHandlersDestroyed, $"Not all client-side handlers were destroyed!"); - Assert.True(clientCountReached, $"Client count ({Support.SpawnRpcDespawn.ClientUpdateCount}) did not match the expected count ({expectedCount})"); - - Debug.Log($"It took {Time.frameCount - frameCountStart} frames to process the MessageOrdering.SpawnRpcDespawn integration test."); - } - - private ulong m_SpawnedNetworkObjectId; - - private bool AllClientsSpawnedObject(NetworkManager[] clients) - { - foreach (var client in clients) - { - if (!client.SpawnManager.SpawnedObjects.ContainsKey(m_SpawnedNetworkObjectId)) + if (!AllHandlersDestroyed(clientHandlers)) { + errorLog.Append("Not all client-side handlers were destroyed!"); return false; } + return true; } - return true; + var timeoutHelper = new TimeoutHelper(5.0f); + yield return WaitForConditionOrTimeOut(AllClientRpcsReceivedAndHandled, timeoutHelper); + AssertOnTimeout("Did not successfully call all expected client RPCs!", timeoutHelper); + + Debug.Log($"It took {Time.frameCount - frameCountStart} frames to process the MessageOrdering.SpawnRpcDespawn integration test."); } [UnityTest] public IEnumerator RpcOnNetworkSpawn() { Support.SpawnRpcDespawn.ExecuteClientRpc = true; - // Must be 1 for this test. - const int numClients = 1; - Assert.True(NetcodeIntegrationTestHelpers.Create(numClients, out NetworkManager server, out NetworkManager[] clients)); - m_Prefab = new GameObject("Object"); - m_Prefab.AddComponent(); - Support.SpawnRpcDespawn.TestStage = NetworkUpdateStage.EarlyUpdate; - var networkObject = m_Prefab.AddComponent(); - - // Make it a prefab - NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject); - var handlers = new List(); - var handler = new SpawnRpcDespawnInstanceHandler(networkObject.GlobalObjectIdHash, server); + var authority = GetAuthorityNetworkManager(); // We *must* always add a unique handler to both the server and the clients - server.PrefabHandler.AddHandler(networkObject, handler); - handlers.Add(handler); - foreach (var client in clients) - { - // Create a unique SpawnRpcDespawnInstanceHandler per client - handler = new SpawnRpcDespawnInstanceHandler(networkObject.GlobalObjectIdHash, client); - handlers.Add(handler); - client.PrefabHandler.AddHandler(networkObject, handler); - } - - var validNetworkPrefab = new NetworkPrefab - { - Prefab = m_Prefab - }; - server.NetworkConfig.Prefabs.Add(validNetworkPrefab); - foreach (var client in clients) - { - client.NetworkConfig.Prefabs.Add(validNetworkPrefab); - } - - var waitForTickInterval = new WaitForSeconds(1.0f / server.NetworkConfig.TickRate); - - // Start the instances - if (!NetcodeIntegrationTestHelpers.Start(false, server, clients)) - { - Debug.LogError("Failed to start instances"); - Assert.Fail("Failed to start instances"); - } - - // [Client-Side] Wait for a connection to the server - yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(clients, null, 512); - - // [Host-Side] Check to make sure all clients are connected - yield return NetcodeIntegrationTestHelpers.WaitForClientsConnectedToServer(server, clients.Length, null, 512); - - var serverNetworkObject = NetworkObject.InstantiateAndSpawn(m_Prefab, server); + var handlers = AddSpawnRpcDespawnHandlers(new[] { authority }); + handlers.AddRange(AddSpawnRpcDespawnHandlers(m_ClientNetworkManagers)); - m_SpawnedNetworkObjectId = serverNetworkObject.NetworkObjectId; + var serverNetworkObject = NetworkObject.InstantiateAndSpawn(m_SpawnRpcDespawnPrefab, authority); // Make sure everyone spawns the object var timeoutHelper = new TimeoutHelper(4.0f); - yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => AllClientsSpawnedObject(clients), timeoutHelper); - Assert.False(timeoutHelper.TimedOut, $"Timed out waiting for all clients to spawn {serverNetworkObject.name}!"); + yield return WaitForSpawnedOnAllOrTimeOut(serverNetworkObject, timeoutHelper); + AssertOnTimeout($"Timed out waiting for all clients to spawn {serverNetworkObject.name}!", timeoutHelper); - // Wait until all objects have spawned. - const int maxFrames = 240; - var doubleCheckTime = Time.realtimeSinceStartup + 5.0f; - while (!Support.SpawnRpcDespawn.ClientNetworkSpawnRpcCalled) - { - if (Time.frameCount > maxFrames) - { - // This is here in the event a platform is running at a higher - // frame rate than expected - if (doubleCheckTime < Time.realtimeSinceStartup) - { - Assert.Fail("Did not successfully call all expected client RPCs"); - break; - } - } - var nextFrameNumber = Time.frameCount + 1; - yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); - } - - Assert.True(handler.WasSpawned); - Assert.True(Support.SpawnRpcDespawn.ClientNetworkSpawnRpcCalled); + timeoutHelper = new TimeoutHelper(5.0f); + yield return WaitForConditionOrTimeOut(() => Support.SpawnRpcDespawn.ClientNetworkSpawnRpcCalled, timeoutHelper); + AssertOnTimeout("Did not successfully call all expected client RPCs", timeoutHelper); + Assert.True(AllHandlersSpawned(handlers), "Not all handlers were spawned!"); // Despawning the server-side NetworkObject will invoke the handler's OnDestroy method serverNetworkObject.Despawn(); - yield return waitForTickInterval; - - var hasTimedOut = false; - var timeOutPeriod = Time.realtimeSinceStartup + 2.0f; - var allHandlersDestroyed = false; - while (!allHandlersDestroyed && !hasTimedOut) - { - allHandlersDestroyed = true; - foreach (var handlerInstance in handlers) - { - if (!handlerInstance.WasDestroyed) - { - allHandlersDestroyed = false; - break; - } - } - hasTimedOut = timeOutPeriod < Time.realtimeSinceStartup; - yield return waitForTickInterval; - } - - Assert.False(hasTimedOut, "Timed out waiting for handlers to be destroyed"); + timeoutHelper = new TimeoutHelper(2.0f); + yield return WaitForConditionOrTimeOut(() => AllHandlersDestroyed(handlers), timeoutHelper); + AssertOnTimeout("Timed out waiting for handlers to be destroyed", timeoutHelper); } } }