Skip to content

test: add utils for multi instance tests #914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions com.unity.multiplayer.mlapi/Tests/Runtime/BaseMultiInstanceTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;

namespace MLAPI.RuntimeTests
{
public abstract class BaseMultiInstanceTest
{
protected GameObject m_PlayerPrefab;
protected NetworkManager m_ServerNetworkManager;
protected NetworkManager[] m_ClientNetworkManagers;

protected abstract int NbClients { get; }

[UnitySetUp]
public virtual IEnumerator Setup()
{
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, _ => { });
}

[UnityTearDown]
public virtual IEnumerator Teardown()
{
// Shutdown and clean up both of our NetworkManager instances
try
{
MultiInstanceHelpers.Destroy();
}
catch (Exception e) { throw e; }
finally
{
if (m_PlayerPrefab != null)
{
Object.Destroy(m_PlayerPrefab);
m_PlayerPrefab = null;
}
}

// wait for next frame so everything is destroyed, so following tests can execute from clean environment
int nextFrameNumber = Time.frameCount + 1;
yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
}

/// <summary>
/// Utility to spawn some clients and a server and set them up
/// </summary>
/// <param name="nbClients"></param>
/// <param name="updatePlayerPrefab">Update the prefab with whatever is needed before players spawn</param>
/// <returns></returns>
public IEnumerator StartSomeClientsAndServerWithPlayers(bool useHost, int nbClients, Action<GameObject> updatePlayerPrefab)
{
// Create multiple NetworkManager instances
if (!MultiInstanceHelpers.Create(nbClients, out NetworkManager server, out NetworkManager[] clients))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}

m_ClientNetworkManagers = clients;
m_ServerNetworkManager = server;

// Create playerPrefab
m_PlayerPrefab = new GameObject("Player");
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();

/*
* Normally we would only allow player prefabs to be set to a prefab. Not runtime created objects.
* In order to prevent having a Resource folder full of a TON of prefabs that we have to maintain,
* MultiInstanceHelper has a helper function that lets you mark a runtime created object to be
* treated as a prefab by the MLAPI. That's how we can get away with creating the player prefab
* at runtime without it being treated as a SceneObject or causing other conflicts with the MLAPI.
*/
// Make it a prefab
MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject);

updatePlayerPrefab(m_PlayerPrefab); // update player prefab with whatever is needed before players are spawned

// Set the player prefab
server.NetworkConfig.PlayerPrefab = m_PlayerPrefab;

for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.PlayerPrefab = m_PlayerPrefab;
}

// Start the instances
if (!MultiInstanceHelpers.Start(useHost, server, clients))
{
Debug.LogError("Failed to start instances");
Assert.Fail("Failed to start instances");
}

// Wait for connection on client side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));

// Wait for connection on server side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnectedToServer(server, clientCount: useHost ? nbClients + 1 : nbClients));
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

namespace MLAPI.RuntimeTests
{
public class NetworkObjectOnSpawnTests
public class NetworkObjectOnSpawnTests : BaseMultiInstanceTest
{
private GameObject m_Prefab;
protected override int NbClients => 2;


/// <summary>
/// Tests that instantiating a <see cref="NetworkObject"/> and destroying without spawning it
Expand Down Expand Up @@ -45,58 +46,34 @@ public override void OnNetworkDespawn()
}
}

[UnitySetUp]
public override IEnumerator Setup()
{
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab =>
{
// add test component
playerPrefab.AddComponent<TrackOnSpawnFunctions>();
});
}

/// <summary>
/// Test that callbacks are run for playerobject spawn, despawn, regular spawn, destroy on server.
/// </summary>
/// <returns></returns>
[UnityTest]
public IEnumerator TestOnNetworkSpawnCallbacks()
{
// Create Host and (numClients) clients
Assert.True(MultiInstanceHelpers.Create(2, out NetworkManager server, out NetworkManager[] clients));

// Create a default player GameObject to use
m_Prefab = new GameObject("TestObject");
var networkObject = m_Prefab.AddComponent<NetworkObject>();

// add test component
m_Prefab.AddComponent<TrackOnSpawnFunctions>();

// Make it a prefab
MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject);

// Set the player prefab
server.NetworkConfig.PlayerPrefab = m_Prefab;

// Set all of the client's player prefab
for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.PlayerPrefab = m_Prefab;
}

// Start the instances
if (!MultiInstanceHelpers.Start(true, server, clients))
{
Assert.Fail("Failed to start instances");
}

// [Client-Side] Wait for a connection to the server
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients, null, 512));

// [Host-Side] Check to make sure all clients are connected
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnectedToServer(server, clients.Length + 1, null, 512));

// [Host-Side] Get the Host owned instance
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == clients[0].LocalClientId), server, serverClientPlayerResult));
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult));

var serverInstance = serverClientPlayerResult.Result.GetComponent<TrackOnSpawnFunctions>();

var clientInstances = new List<TrackOnSpawnFunctions>();
foreach (var client in clients)
foreach (var client in m_ClientNetworkManagers)
{
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == clients[0].LocalClientId), client, clientClientPlayerResult));
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), client, clientClientPlayerResult));
var clientRpcTests = clientClientPlayerResult.Result.GetComponent<TrackOnSpawnFunctions>();
Assert.IsNotNull(clientRpcTests);
clientInstances.Add(clientRpcTests);
Expand Down Expand Up @@ -171,9 +148,6 @@ public IEnumerator TestOnNetworkSpawnCallbacks()
{
Assert.AreEqual(1, clientInstance.OnNetworkDespawnCalledCount);
}

// Shutdown and clean up both of our NetworkManager instances
MultiInstanceHelpers.Destroy();
}

private class TrackOnSpawnFunctions : NetworkBehaviour
Expand All @@ -191,15 +165,5 @@ public override void OnNetworkDespawn()
OnNetworkDespawnCalledCount++;
}
}

[TearDown]
public void TearDown()
{
if (m_Prefab != null)
{
Object.Destroy(m_Prefab);
m_Prefab = null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,13 @@

namespace MLAPI.RuntimeTests
{
public class NetworkSpawnManagerTests
public class NetworkSpawnManagerTests : BaseMultiInstanceTest
{
private NetworkManager m_ServerNetworkManager;
private NetworkManager[] m_ClientNetworkManagers;
private GameObject m_PlayerPrefab;

private ulong serverSideClientId => m_ServerNetworkManager.ServerClientId;
private ulong clientSideClientId => m_ClientNetworkManagers[0].LocalClientId;
private ulong otherClientSideClientId => m_ClientNetworkManagers[1].LocalClientId;

[UnitySetUp]
public IEnumerator Setup()
{
// Create multiple NetworkManager instances
if (!MultiInstanceHelpers.Create(2, out NetworkManager server, out NetworkManager[] clients))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}

m_ServerNetworkManager = server;
m_ClientNetworkManagers = clients;

// Create playerPrefab
m_PlayerPrefab = new GameObject("Player");
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();

// Make it a prefab
MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject);

// Set the player prefab
server.NetworkConfig.PlayerPrefab = m_PlayerPrefab;

for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.PlayerPrefab = m_PlayerPrefab;
}

// Start the instances
if (!MultiInstanceHelpers.Start(true, server, clients))
{
Assert.Fail("Failed to start instances");
}

// Wait for connection on client side
for (int i = 0; i < clients.Length; i++)
{
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnected(clients[i]));
}

// Wait for connection on server side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnectedToServer(server, clientCount: 3));
}
protected override int NbClients => 2;

[Test]
public void TestServerCanAccessItsOwnPlayer()
Expand Down Expand Up @@ -168,20 +122,9 @@ public IEnumerator TestConnectAndDisconnect()
var nbConnectedClients = m_ServerNetworkManager.ConnectedClients.Count;
MultiInstanceHelpers.StopOneClient(newClientNetworkManager);
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => m_ServerNetworkManager.ConnectedClients.Count == nbConnectedClients - 1));

serverSideNewClientPlayer = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(newClientLocalClientId);
Assert.Null(serverSideNewClientPlayer);
}

[UnityTearDown]
public IEnumerator Teardown()
{
// Shutdown and clean up both of our NetworkManager instances
MultiInstanceHelpers.Destroy();
Object.Destroy(m_PlayerPrefab);

// wait for next frame so everything is destroyed, so following tests can execute from clean environment
int nextFrameNumber = Time.frameCount + 1;
yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
}
}
}
64 changes: 14 additions & 50 deletions com.unity.multiplayer.mlapi/Tests/Runtime/RpcTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace MLAPI.RuntimeTests
{
public class RpcTests
public class RpcTests : BaseMultiInstanceTest
{
public class RpcTestNB : NetworkBehaviour
{
Expand All @@ -28,60 +28,27 @@ public void MyClientRpc()
}
}

[UnityTest]
public IEnumerator TestRpcs()
{
// Create multiple NetworkManager instances
if (!MultiInstanceHelpers.Create(1, out NetworkManager server, out NetworkManager[] clients))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}

/*
* Normally we would only allow player prefabs to be set to a prefab. Not runtime created objects.
* In order to prevent having a Resource folder full of a TON of prefabs that we have to maintain,
* MultiInstanceHelper has a helper function that lets you mark a runtime created object to be
* treated as a prefab by the MLAPI. That's how we can get away with creating the player prefab
* at runtime without it being treated as a SceneObject or causing other conflicts with the MLAPI.
*/

// Create playerPrefab
var playerPrefab = new GameObject("Player");
NetworkObject networkObject = playerPrefab.AddComponent<NetworkObject>();
playerPrefab.AddComponent<RpcTestNB>();

// Make it a prefab
MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject);

// Set the player prefab
server.NetworkConfig.PlayerPrefab = playerPrefab;

for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.PlayerPrefab = playerPrefab;
}
protected override int NbClients => 1;

// Start the instances
if (!MultiInstanceHelpers.Start(true, server, clients))
[UnitySetUp]
public override IEnumerator Setup()
{
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab =>
{
Assert.Fail("Failed to start instances");
}


// Wait for connection on client side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));

// Wait for connection on server side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnectedToServer(server));
playerPrefab.AddComponent<RpcTestNB>();
});
}

[UnityTest]
public IEnumerator TestRpcs()
{
// This is the *SERVER VERSION* of the *CLIENT PLAYER*
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == clients[0].LocalClientId), server, serverClientPlayerResult));
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult));

// This is the *CLIENT VERSION* of the *CLIENT PLAYER*
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == clients[0].LocalClientId), clients[0], clientClientPlayerResult));
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult));

// Setup state
bool hasReceivedServerRpc = false;
Expand Down Expand Up @@ -125,9 +92,6 @@ public IEnumerator TestRpcs()
Assert.True(hasReceivedServerRpc, "ServerRpc was not received");
Assert.True(hasReceivedClientRpcLocally, "ClientRpc was not locally received on the server");
Assert.True(hasReceivedClientRpcRemotely, "ClientRpc was not remotely received on the client");

// Cleanup
MultiInstanceHelpers.Destroy();
}
}
}
Loading