using System.Collections; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; namespace Unity.Netcode.RuntimeTests { /// /// Runtime tests to test the network time system with the Unity player loop. /// public class NetworkTimeSystemTests { private MonoBehaviourTest m_MonoBehaviourTest; // cache for teardown [SetUp] public void Setup() { // Create, instantiate, and host Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _)); } /// /// Tests whether time is accessible and has correct values inside Update/FixedUpdate. /// /// [UnityTest] public IEnumerator PlayerLoopTimeTest() { m_MonoBehaviourTest = new MonoBehaviourTest(); yield return m_MonoBehaviourTest; } /// /// Tests whether the time system invokes the correct amount of ticks over a period of time. /// Note we cannot test against Time.Time directly because of floating point precision. Our time is more precise leading to different results. /// /// [UnityTest] public IEnumerator CorrectAmountTicksTest() { var tickSystem = NetworkManager.Singleton.NetworkTickSystem; var delta = tickSystem.LocalTime.FixedDeltaTime; while (tickSystem.LocalTime.Time < 3f) { yield return null; Assert.AreEqual(Mathf.FloorToInt((tickSystem.LocalTime.TimeAsFloat / delta)), NetworkManager.Singleton.LocalTime.Tick); Assert.AreEqual(Mathf.FloorToInt((tickSystem.ServerTime.TimeAsFloat / delta)), NetworkManager.Singleton.ServerTime.Tick); Assert.True(Mathf.Approximately((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time)); } } [TearDown] public void TearDown() { // Stop, shutdown, and destroy NetworkManagerHelper.ShutdownNetworkManager(); if (m_MonoBehaviourTest != null) { Object.DestroyImmediate(m_MonoBehaviourTest.gameObject); } } } public class PlayerLoopTimeTestComponent : MonoBehaviour, IMonoBehaviourTest { public const int Passes = 100; private int m_UpdatePasses = 0; private int m_LastFixedUpdateTick = 0; private int m_TickOffset = -1; private NetworkTime m_LocalTimePreviousUpdate; private NetworkTime m_ServerTimePreviousUpdate; private NetworkTime m_LocalTimePreviousFixedUpdate; public void Start() { // Run fixed update at same rate as network tick Time.fixedDeltaTime = NetworkManager.Singleton.LocalTime.FixedDeltaTime; // Uncap fixed time else we might skip fixed updates Time.maximumDeltaTime = float.MaxValue; } public void Update() { // This must run first else it wont run if there is an exception m_UpdatePasses++; var localTime = NetworkManager.Singleton.LocalTime; var serverTime = NetworkManager.Singleton.ServerTime; // time should have advanced on the host/server Assert.True(m_LocalTimePreviousUpdate.Time < localTime.Time); Assert.True(m_ServerTimePreviousUpdate.Time < serverTime.Time); // time should be further then last fixed step in update Assert.True(m_LocalTimePreviousFixedUpdate.FixedTime < localTime.Time); // we should be in same or further tick then fixed update Assert.True(m_LocalTimePreviousFixedUpdate.Tick <= localTime.Tick); // fixed update should result in same amounts of tick as network time if (m_TickOffset == -1) { m_TickOffset = serverTime.Tick - m_LastFixedUpdateTick; } else { // offset of 1 is ok, this happens due to different tick duration offsets Assert.True(Mathf.Abs(serverTime.Tick - m_TickOffset - m_LastFixedUpdateTick) <= 1); } m_LocalTimePreviousUpdate = localTime; } public void FixedUpdate() { var time = NetworkManager.Singleton.LocalTime; m_LocalTimePreviousFixedUpdate = time; Assert.AreEqual(Time.fixedDeltaTime, time.FixedDeltaTime); Assert.True(Mathf.Approximately((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time)); m_LastFixedUpdateTick++; } public bool IsTestFinished => m_UpdatePasses >= Passes; } }