Skip to content

Commit 35b47a5

Browse files
test refactor
This update collapses all of the time out code into the BaseMultiInstanceTest with an added method WaitForConditionOrTimeOut that leverages from an added TimeOutHelper class.
1 parent 73340d6 commit 35b47a5

File tree

6 files changed

+329
-413
lines changed

6 files changed

+329
-413
lines changed

com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,70 @@ public abstract class BaseMultiInstanceTest
1818

1919
protected bool m_BypassStartAndWaitForClients = false;
2020

21+
static protected TimeOutHelper s_GloabalTimeOutHelper = new TimeOutHelper();
22+
23+
protected const uint k_DefaultTickRate = 30;
24+
protected WaitForSeconds m_DefaultWaitForTick = new WaitForSeconds(1.0f / k_DefaultTickRate);
25+
26+
/// <summary>
27+
/// An update to the original MultiInstanceHelpers.WaitForCondition that:
28+
/// -operates at the current tick rate
29+
/// -provides a value of type T to be passed into the checkForCondition function
30+
/// -allows for a unique TimeOutHelper handler (if none then uses the default)
31+
/// -adjusts its yield period to the settings of the m_ServerNetworkManager.NetworkConfig.TickRate
32+
/// Notes: This method provides more stability when running integration tests that could
33+
/// be impacted by:
34+
/// -how the integration test is being executed (i.e. in editor or in a stand alone build)
35+
/// -potential platform performance issues (i.e. VM is throttled or maxed)
36+
/// </summary>
37+
/// <typeparam name="T">type of the value to compare</typeparam>
38+
/// <param name="checkForCondition">condition checking function that is passed valueToCompare to compare against</param>
39+
/// <param name="valueToCompare">the value to compare against</param>
40+
/// <param name="timeOutHelper">optional custom time out helper-handler</param>
41+
/// <returns></returns>
42+
protected IEnumerator WaitForConditionOrTimeOut<T>(Func<T, bool> checkForCondition, T valueToCompare, TimeOutHelper timeOutHelper = null)
43+
{
44+
if (checkForCondition == null)
45+
{
46+
throw new ArgumentNullException($"checkForCondition cannot be null!");
47+
}
48+
49+
if (valueToCompare == null)
50+
{
51+
throw new ArgumentNullException($"The value to be compared cannot be null!");
52+
}
53+
54+
// If none is provided we use the default global time out helper
55+
if (timeOutHelper == null)
56+
{
57+
timeOutHelper = s_GloabalTimeOutHelper;
58+
}
59+
60+
// Start checking for a timeout
61+
timeOutHelper.Start();
62+
while (!timeOutHelper.HasTimedOut())
63+
{
64+
// Update and check to see if the condition has been met
65+
if (checkForCondition.Invoke(valueToCompare))
66+
{
67+
break;
68+
}
69+
70+
// Otherwise wait for 1 tick interval
71+
yield return m_DefaultWaitForTick;
72+
}
73+
// Stop checking for a timeout
74+
timeOutHelper.Stop();
75+
}
76+
2177
[UnitySetUp]
2278
public virtual IEnumerator Setup()
2379
{
2480
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, _ => { });
81+
if (m_ServerNetworkManager != null)
82+
{
83+
m_DefaultWaitForTick = new WaitForSeconds(1.0f / m_ServerNetworkManager.NetworkConfig.TickRate);
84+
}
2585
}
2686

2787
[UnityTearDown]
@@ -54,9 +114,12 @@ public virtual IEnumerator Teardown()
54114
Object.DestroyImmediate(networkObject);
55115
}
56116

57-
// wait for next frame so everything is destroyed, so following tests can execute from clean environment
58-
int nextFrameNumber = Time.frameCount + 1;
59-
yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
117+
// wait for 1 tick interval so everything is destroyed and any following tests
118+
// can execute from clean environment
119+
yield return m_DefaultWaitForTick;
120+
121+
// reset the m_ServerWaitForTick for the next test to initialize
122+
m_DefaultWaitForTick = new WaitForSeconds(1.0f / k_DefaultTickRate);
60123
}
61124

62125
/// <summary>
@@ -170,4 +233,42 @@ public IEnumerator StartSomeClientsAndServerWithPlayers(bool useHost, int nbClie
170233
}
171234
}
172235
}
236+
237+
/// <summary>
238+
/// Can be used independently or a assigned to <see cref="BaseMultiInstanceTest.WaitForConditionOrTimeOut"></see> in the
239+
/// event the default timeout period needs to be adjusted
240+
/// </summary>
241+
public class TimeOutHelper
242+
{
243+
private const float k_DefaultTimeOutWaitPeriod = 2.0f;
244+
245+
private float m_MaximumTimeBeforeTimeOut;
246+
private float m_TimeOutPeriod;
247+
248+
private bool m_IsStarted;
249+
public bool TimedOut { get; internal set; }
250+
251+
public void Start()
252+
{
253+
m_MaximumTimeBeforeTimeOut = Time.realtimeSinceStartup + m_TimeOutPeriod;
254+
m_IsStarted = true;
255+
TimedOut = false;
256+
}
257+
258+
public void Stop()
259+
{
260+
TimedOut = HasTimedOut();
261+
m_IsStarted = false;
262+
}
263+
264+
public bool HasTimedOut()
265+
{
266+
return m_IsStarted ? m_MaximumTimeBeforeTimeOut < Time.realtimeSinceStartup : TimedOut;
267+
}
268+
269+
public TimeOutHelper(float timeOutPeriod = k_DefaultTimeOutWaitPeriod)
270+
{
271+
m_TimeOutPeriod = timeOutPeriod;
272+
}
273+
}
173274
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourUpdaterTests.cs

Lines changed: 7 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using NUnit.Framework;
45
using UnityEngine;
56
using UnityEngine.TestTools;
@@ -153,9 +154,7 @@ public class NetworkBehaviourUpdaterTests : BaseMultiInstanceTest
153154
{
154155
// Go ahead and create maximum number of clients (not all tests will use them)
155156
protected override int NbClients => 2;
156-
private const float k_TimeOutWaitPeriod = 2.0f;
157157
public const int NetVarValueToSet = 1;
158-
private static float s_TimeOutPeriod;
159158
private static List<GameObject> s_ClientSpawnedNetworkObjects = new List<GameObject>();
160159
private List<NetworkManager> m_ActiveClientsForCurrentTest;
161160

@@ -168,31 +167,9 @@ public static void ClientSideNotifyObjectSpawned(GameObject objectSpaned)
168167
if (!s_ClientSpawnedNetworkObjects.Contains(objectSpaned))
169168
{
170169
s_ClientSpawnedNetworkObjects.Add(objectSpaned);
171-
// As long as we are getting notified the clients are spawning objects
172-
// then bump up the timeout period
173-
AdvanceTimeOutPeriod();
174170
}
175171
}
176172

177-
/// <summary>
178-
/// This will simply advance the timeout period
179-
/// Note: When ClientSideNotifyObjectSpawned is invoked this will get
180-
/// called to handle any potential latencies due to poor performance or
181-
/// the like.
182-
/// </summary>
183-
private static void AdvanceTimeOutPeriod()
184-
{
185-
s_TimeOutPeriod = Time.realtimeSinceStartup + k_TimeOutWaitPeriod;
186-
}
187-
188-
/// <summary>
189-
/// Checks if the timeout period has elapsed
190-
/// </summary>
191-
private static bool HasTimedOut()
192-
{
193-
return s_TimeOutPeriod <= Time.realtimeSinceStartup;
194-
}
195-
196173
public override IEnumerator Setup()
197174
{
198175
// This makes sure the server and client are not started during the setup
@@ -315,22 +292,10 @@ public IEnumerator BehaviourUpdaterAllTests([Values] bool useHost,
315292
// wait until all objects are spawned on the clients
316293
if (numberOfObjectsToSpawnOnClients > 0)
317294
{
318-
var allClientsSpawnedObjects = false;
319-
// Reset the time out to be k_TimeOutWaitPeriod + the current time
320-
AdvanceTimeOutPeriod();
321-
322295
// Waits for all clients to spawn the NetworkObjects
323-
while (!allClientsSpawnedObjects && !HasTimedOut())
324-
{
325-
allClientsSpawnedObjects = numberOfObjectsToSpawnOnClients == s_ClientSpawnedNetworkObjects.Count;
326-
yield return new WaitForSeconds(tickInterval);
327-
}
328-
329-
Assert.True(!HasTimedOut(), $"Timed out waiting for clients to report spawning objects! " +
296+
yield return WaitForConditionOrTimeOut((c) => c == s_ClientSpawnedNetworkObjects.Count, numberOfObjectsToSpawnOnClients);
297+
Assert.IsFalse(s_GloabalTimeOutHelper.TimedOut, $"Timed out waiting for clients to report spawning objects! " +
330298
$"Total reported client-side spawned objects {s_ClientSpawnedNetworkObjects.Count}");
331-
332-
// This really should never fail as it should timeout first
333-
Assert.True(allClientsSpawnedObjects, "Not all clients spawned their objects!");
334299
}
335300

336301
// Once all clients have spawned the NetworkObjects, set the network variables for
@@ -371,30 +336,10 @@ public IEnumerator BehaviourUpdaterAllTests([Values] bool useHost,
371336
}
372337
}
373338

374-
var allClientsCompleted = false;
375-
AdvanceTimeOutPeriod();
376-
377-
// Wait until all clients have had their NetworkVariables updated
378-
while (!allClientsCompleted && !HasTimedOut())
379-
{
380-
var completedCount = 0;
381-
foreach (var clientNetVarContainer in clientSideNetVarContainers)
382-
{
383-
if (clientNetVarContainer.HaveAllValuesChanged(NetVarValueToSet))
384-
{
385-
completedCount++;
386-
}
387-
}
388-
389-
allClientsCompleted = completedCount == clientSideNetVarContainers.Count;
390-
391-
yield return new WaitForSeconds(tickInterval);
392-
}
393-
394-
Assert.True(!HasTimedOut(), $"Timed out waiting for client side NetVarContainers to report all NetworkVariables have been updated!");
395-
396-
// This really should never fail as it should timeout first
397-
Assert.True(allClientsCompleted, "Not all client side NetworkVariables reported they were updated with target value!");
339+
yield return WaitForConditionOrTimeOut((c) =>
340+
clientSideNetVarContainers.Where(d =>
341+
d.HaveAllValuesChanged(c)).Count() == clientSideNetVarContainers.Count, NetVarValueToSet);
342+
Assert.IsFalse(s_GloabalTimeOutHelper.TimedOut, $"Timed out waiting for client side NetVarContainers to report all NetworkVariables have been updated!");
398343
}
399344

400345
Object.DestroyImmediate(prefabToSpawn);

0 commit comments

Comments
 (0)