using LobbyRelaySample;
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.TestTools;
namespace Test
{
///
/// Testing some edge cases with the UpdateSlow.
///
public class UpdateSlowTests
{
private GameObject m_updateSlowObj;
private List m_activeSubscribers = new List(); // For cleaning up, in case an Assert prevents a Subscriber from taking care of itself.
/// Trivial Subscriber to do some action every UpdateSlow.
private class Subscriber : IDisposable
{
private Action m_thingToDo;
public float prevDt;
public Subscriber(Action thingToDo, float period)
{
Locator.Get.UpdateSlow.Subscribe(OnUpdate, period);
m_thingToDo = thingToDo;
}
public void Dispose()
{
Locator.Get.UpdateSlow.Unsubscribe(OnUpdate);
}
private void OnUpdate(float dt)
{
m_thingToDo?.Invoke();
prevDt = dt;
}
}
[OneTimeSetUp]
public void Setup()
{
m_updateSlowObj = new GameObject("UpdateSlowTest");
m_updateSlowObj.AddComponent();
}
[OneTimeTearDown]
public void Teardown()
{
GameObject.Destroy(m_updateSlowObj);
}
[UnityTearDown]
public IEnumerator PerTestTeardown()
{
foreach (Subscriber sub in m_activeSubscribers)
sub.Dispose();
m_activeSubscribers.Clear();
yield break;
}
[UnityTest]
public IEnumerator BasicBehavior_MultipleSubs()
{
int updateCount = 0;
float period = 1.5f;
Subscriber sub = new Subscriber(() => { updateCount++; }, period);
m_activeSubscribers.Add(sub);
yield return null;
Assert.AreEqual(0, updateCount, "Update loop should be slow and not update per-frame.");
yield return new WaitForSeconds(period - 0.1f);
Assert.AreEqual(0, updateCount, "Assuming a default period of 1.5s and a reasonable frame rate, the slow update should still not have hit.");
yield return new WaitForSeconds(0.1f);
Assert.AreEqual(1, updateCount, "Slow update period should have passed.");
Assert.AreNotEqual(period, sub.prevDt, "Slow update should have received the actual amount of time that passed, not necessarily its period.");
Assert.True(sub.prevDt - period < 0.05f && sub.prevDt - period > 0, "The time delta received by slow update should match the actual time since their previous update.");
yield return new WaitForSeconds(period);
Assert.AreEqual(2, updateCount, "Did the slow update again.");
Assert.AreNotEqual(period, sub.prevDt, "Slow update should have received the full time delta, not just its period, again.");
Assert.True(sub.prevDt - period < 0.05f && sub.prevDt - period > 0, "The time delta received by slow update should match the actual time since their previous update, again.");
float period2 = period - 0.2f;
Subscriber sub2 = new Subscriber(() => { updateCount += 7; }, period2);
m_activeSubscribers.Add(sub2);
yield return new WaitForSeconds(period);
Assert.AreEqual(10, updateCount, "There are two subscribers now.");
Assert.True(sub.prevDt - period < 0.05f && sub.prevDt - period > 0, "Slow update on the first subscriber should have received the full time delta with two subscribers.");
Assert.True(sub2.prevDt - period2 < 0.05f && sub2.prevDt - period2 > 0, "Slow update on the second subscriber should receive the actual time, even if its period is shorter.");
sub2.Dispose();
yield return new WaitForSeconds(period);
Assert.AreEqual(11, updateCount, "Should have unsubscribed just the one subscriber.");
sub.Dispose();
yield return new WaitForSeconds(period);
Assert.AreEqual(11, updateCount, "Should have unsubscribed the remaining subscriber.");
}
[UnityTest]
public IEnumerator BasicBehavior_UpdateEveryFrame()
{
int updateCount = 0;
Subscriber sub = new Subscriber(() => { updateCount++; }, 0);
m_activeSubscribers.Add(sub);
yield return null;
Assert.AreEqual(1, updateCount, "Update loop should update per-frame if a subscriber opts for that (#1).");
yield return null;
Assert.AreEqual(2, updateCount, "Update loop should update per-frame if a subscriber opts for that (#2).");
Assert.AreEqual(sub.prevDt, Time.deltaTime, "Subscriber should receive the correct update time since their previous update.");
sub.Dispose();
yield return new WaitForSeconds(0.5f);
Assert.AreEqual(2, updateCount, "Should have unsubscribed the subscriber.");
}
[UnityTest]
public IEnumerator HandleLambda()
{
int updateCount = 0;
float period = 0.5f;
Locator.Get.UpdateSlow.Subscribe((dt) => { updateCount++; }, period);
LogAssert.Expect(LogType.Error, new Regex(".*Removed anonymous.*"));
yield return new WaitForSeconds(period + 0.1f);
Assert.AreEqual(0, updateCount, "Lambdas should not be permitted, since they can't be Unsubscribed.");
Locator.Get.UpdateSlow.Subscribe(ThisIsALocalFunction, period);
LogAssert.Expect(LogType.Error, new Regex(".*Removed local function.*"));
yield return new WaitForSeconds(period + 0.1f);
Assert.AreEqual(0, updateCount, "Local functions should not be permitted, since they can't be Unsubscribed.");
void ThisIsALocalFunction(float dt) { }
}
[UnityTest]
public IEnumerator SubscribeNoDuplicates()
{
dummyOnUpdateCalls = 0;
Locator.Get.UpdateSlow.Subscribe(DummyOnUpdate, 1);
Locator.Get.UpdateSlow.Subscribe(DummyOnUpdate, 0.1f);
yield return new WaitForSeconds(0.9f);
Assert.AreEqual(0, dummyOnUpdateCalls, "The second Subscribe call should not have gone through.");
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(1, dummyOnUpdateCalls, "The first Subscribe call should have gone through.");
Locator.Get.UpdateSlow.Unsubscribe(DummyOnUpdate);
yield return new WaitForSeconds(1);
Assert.AreEqual(1, dummyOnUpdateCalls, "Unsubscribe should work as expected.");
}
private int dummyOnUpdateCalls = 0;
private void DummyOnUpdate(float dt) { dummyOnUpdateCalls++; }
[UnityTest]
public IEnumerator WhatIfASubscriberIsVerySlow()
{
int updateCount = 0;
string inefficientString = "";
float period = 1.5f;
Subscriber sub = new Subscriber(() =>
{ for (int n = 0; n < 12345; n++)
inefficientString += n.ToString();
updateCount++;
}, period);
m_activeSubscribers.Add(sub);
LogAssert.Expect(LogType.Error, new Regex(".*took too long.*"));
yield return new WaitForSeconds(period + 0.1f);
Assert.AreEqual(1, updateCount, "Executed the slow update.");
yield return new WaitForSeconds(period);
Assert.AreEqual(1, updateCount, "Should have removed the offending subscriber.");
}
}
}