Added Tests

This commit is contained in:
MrGadget 2022-01-19 07:20:57 -05:00
parent 7d974fbdbe
commit 140691e744
455 changed files with 31948 additions and 0 deletions

8
Assets/Mirror/Tests.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4de157ac7e1594c758ce6dc401674f5c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dd4f310377c5a4ad39bd31546c95c2a2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,112 @@
fileFormatVersion: 2
guid: 739a02265856e4e4b8a3ae587a908479
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: LinuxUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
using System;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
namespace Mirror.Tests
{
/// <summary>
/// Used by both runtime and edit time tests
/// </summary>
[TestFixture]
public abstract class ClientSceneTestsBase : MirrorEditModeTest
{
// use guid to find asset so that the path does not matter
protected const string ValidPrefabAssetGuid = "33169286da0313d45ab5bfccc6cf3775";
protected const string PrefabWithChildrenAssetGuid = "a78e009e3f2dee44e8859516974ede43";
protected const string InvalidPrefabAssetGuid = "78f0a3f755d35324e959f3ecdd993fb0";
// random guid, not used anywhere
protected const string AnotherGuidString = "5794128cdfda04542985151f82990d05";
protected GameObject validPrefab;
protected NetworkIdentity validPrefabNetworkIdentity;
protected GameObject prefabWithChildren;
protected GameObject invalidPrefab;
protected Guid validPrefabGuid;
protected Guid anotherGuid;
static GameObject LoadPrefab(string guid)
{
return AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(guid));
}
[OneTimeSetUp]
public void OneTimeSetUp()
{
validPrefab = LoadPrefab(ValidPrefabAssetGuid);
validPrefabNetworkIdentity = validPrefab.GetComponent<NetworkIdentity>();
prefabWithChildren = LoadPrefab(PrefabWithChildrenAssetGuid);
invalidPrefab = LoadPrefab(InvalidPrefabAssetGuid);
validPrefabGuid = new Guid(ValidPrefabAssetGuid);
anotherGuid = new Guid(AnotherGuidString);
}
[TearDown]
public override void TearDown()
{
// reset asset id in case they are changed by tests
validPrefabNetworkIdentity.assetId = validPrefabGuid;
base.TearDown();
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
validPrefab = null;
prefabWithChildren = null;
invalidPrefab = null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf741fb5970e5e048aea5ff3e396d24c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,212 @@
using System;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
/// <summary>
/// Used by both runtime and edit time tests
/// </summary>
[TestFixture]
public abstract class ClientSceneTests_RegisterPrefabBase : ClientSceneTestsBase
{
[Test]
[TestCase(RegisterPrefabOverload.Prefab, false)]
[TestCase(RegisterPrefabOverload.Prefab_NewAssetId, true)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate, false)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId, true)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate, false)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId, true)]
public void CheckOverloadWithAssetId(RegisterPrefabOverload overload, bool expected)
{
// test to make sure OverloadWithAssetId correctly works with flags
Assert.That(OverloadWithAssetId(overload), Is.EqualTo(expected));
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab, false)]
[TestCase(RegisterPrefabOverload.Prefab_NewAssetId, false)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate, true)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId, true)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate, true)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId, true)]
public void CheckOverloadWithHandler(RegisterPrefabOverload overload, bool expected)
{
// test to make sure OverloadWithHandler correctly works with flags
Assert.That(OverloadWithHandler(overload), Is.EqualTo(expected));
}
/// <summary>
/// Allows TestCases to call different overloads for RegisterPrefab.
/// Without this we would need duplicate tests for each overload
/// </summary>
[Flags]
public enum RegisterPrefabOverload
{
Prefab = 1,
Prefab_NewAssetId = 2,
Prefab_SpawnDelegate = 4,
Prefab_SpawnDelegate_NewAssetId = 8,
Prefab_SpawnHandlerDelegate = 16,
Prefab_SpawnHandlerDelegate_NewAssetId = 32,
WithAssetId = Prefab_NewAssetId | Prefab_SpawnDelegate_NewAssetId | Prefab_SpawnHandlerDelegate_NewAssetId,
WithHandler = Prefab_SpawnDelegate | Prefab_SpawnDelegate_NewAssetId | Prefab_SpawnHandlerDelegate | Prefab_SpawnHandlerDelegate_NewAssetId
}
protected static bool OverloadWithAssetId(RegisterPrefabOverload overload)
{
return (overload & RegisterPrefabOverload.WithAssetId) != 0;
}
protected static bool OverloadWithHandler(RegisterPrefabOverload overload)
{
return (overload & RegisterPrefabOverload.WithHandler) != 0;
}
protected void CallRegisterPrefab(GameObject prefab, RegisterPrefabOverload overload)
{
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
SpawnHandlerDelegate spawnHandlerDelegate = new SpawnHandlerDelegate(x => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
switch (overload)
{
case RegisterPrefabOverload.Prefab:
NetworkClient.RegisterPrefab(prefab);
break;
case RegisterPrefabOverload.Prefab_NewAssetId:
NetworkClient.RegisterPrefab(prefab, anotherGuid);
break;
case RegisterPrefabOverload.Prefab_SpawnDelegate:
NetworkClient.RegisterPrefab(prefab, spawnHandler, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, anotherGuid, spawnHandler, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate:
NetworkClient.RegisterPrefab(prefab, spawnHandlerDelegate, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, anotherGuid, spawnHandlerDelegate, unspawnHandler);
break;
default:
Debug.LogError("Overload not found");
break;
}
}
protected void CallRegisterPrefab(GameObject prefab, RegisterPrefabOverload overload, Guid guid)
{
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
SpawnHandlerDelegate spawnHandlerDelegate = new SpawnHandlerDelegate(x => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
switch (overload)
{
case RegisterPrefabOverload.Prefab_NewAssetId:
NetworkClient.RegisterPrefab(prefab, guid);
break;
case RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, guid, spawnHandler, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, guid, spawnHandlerDelegate, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab:
case RegisterPrefabOverload.Prefab_SpawnDelegate:
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate:
Debug.LogError("Overload did not have guid parameter");
break;
default:
Debug.LogError("Overload not found");
break;
}
}
protected void CallRegisterPrefab(GameObject prefab, RegisterPrefabOverload overload, SpawnDelegate spawnHandler)
{
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
switch (overload)
{
case RegisterPrefabOverload.Prefab_SpawnDelegate:
NetworkClient.RegisterPrefab(prefab, spawnHandler, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, anotherGuid, spawnHandler, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab:
case RegisterPrefabOverload.Prefab_NewAssetId:
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate:
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId:
Debug.LogError("Overload did not have SpawnDelegate parameter");
break;
default:
Debug.LogError("Overload not found");
break;
}
}
protected void CallRegisterPrefab(GameObject prefab, RegisterPrefabOverload overload, SpawnHandlerDelegate spawnHandlerDelegate)
{
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
switch (overload)
{
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate:
NetworkClient.RegisterPrefab(prefab, spawnHandlerDelegate, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, anotherGuid, spawnHandlerDelegate, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab:
case RegisterPrefabOverload.Prefab_NewAssetId:
case RegisterPrefabOverload.Prefab_SpawnDelegate:
case RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId:
Debug.LogError("Overload did not have SpawnHandlerDelegate parameter");
break;
default:
Debug.LogError("Overload not found");
break;
}
}
protected void CallRegisterPrefab(GameObject prefab, RegisterPrefabOverload overload, UnSpawnDelegate unspawnHandler)
{
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
SpawnHandlerDelegate spawnHandlerDelegate = new SpawnHandlerDelegate(x => null);
switch (overload)
{
case RegisterPrefabOverload.Prefab_SpawnDelegate:
NetworkClient.RegisterPrefab(prefab, spawnHandler, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, anotherGuid, spawnHandler, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate:
NetworkClient.RegisterPrefab(prefab, spawnHandlerDelegate, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId:
NetworkClient.RegisterPrefab(prefab, anotherGuid, spawnHandlerDelegate, unspawnHandler);
break;
case RegisterPrefabOverload.Prefab:
case RegisterPrefabOverload.Prefab_NewAssetId:
Debug.LogError("Overload did not have UnSpawnDelegate parameter");
break;
default:
Debug.LogError("Overload not found");
break;
}
}
protected Guid GuidForOverload(RegisterPrefabOverload overload) => OverloadWithAssetId(overload) ? anotherGuid : validPrefabGuid;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8eb53689226946640bc49bb962b13638
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using System;
namespace Mirror.Tests
{
public class FakeNetworkConnection : NetworkConnectionToClient
{
public FakeNetworkConnection() : base(1) {}
public override string address => "Test";
public override void Disconnect() {}
internal override void Send(ArraySegment<byte> segment, int channelId = 0) {}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 935b1eb49c500674eaaf88982952a69e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,208 @@
// memory transport for easier testing
// note: file needs to be outside of Editor folder, otherwise AddComponent
// can't be called with MemoryTransport
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Mirror.Tests
{
public class MemoryTransport : Transport
{
public enum EventType { Connected, Data, Disconnected }
public struct Message
{
public int connectionId;
public EventType eventType;
public byte[] data;
public Message(int connectionId, EventType eventType, byte[] data)
{
this.connectionId = connectionId;
this.eventType = eventType;
this.data = data;
}
}
bool clientConnected;
public Queue<Message> clientIncoming = new Queue<Message>();
bool serverActive;
public Queue<Message> serverIncoming = new Queue<Message>();
public override bool Available() => true;
// limit max size to something reasonable so pool doesn't allocate
// int.MaxValue = 2GB each time.
public override int GetMaxPacketSize(int channelId) => ushort.MaxValue;
// 1400 max batch size
// -> need something != GetMaxPacketSize for testing
// -> MTU aka 1400 is used a lot anyway
public override int GetBatchThreshold(int channelId) => 1400;
public override void Shutdown() {}
public override bool ClientConnected() => clientConnected;
public override void ClientConnect(string address)
{
// only if server is running
if (serverActive)
{
// add server connected message with connId=1 because 0 is reserved
serverIncoming.Enqueue(new Message(1, EventType.Connected, null));
// add client connected message
clientIncoming.Enqueue(new Message(0, EventType.Connected, null));
clientConnected = true;
}
}
public override void ClientSend(ArraySegment<byte> segment, int channelId)
{
// only if client connected
if (clientConnected)
{
// a real transport fails for > max sized messages.
// mirror checks it, but let's guarantee that we catch > max
// sized message send attempts just like a real transport would.
// => helps to cover packet size issues i.e. for timestamp
// batching tests
int max = GetMaxPacketSize(channelId);
if (segment.Count > max)
throw new Exception($"MemoryTransport ClientSend of {segment.Count} bytes exceeds max of {max} bytes");
// copy segment data because it's only valid until return
byte[] data = new byte[segment.Count];
Array.Copy(segment.Array, segment.Offset, data, 0, segment.Count);
// add server data message with connId=1 because 0 is reserved
serverIncoming.Enqueue(new Message(1, EventType.Data, data));
}
}
public override void ClientDisconnect()
{
// only if client connected
if (clientConnected)
{
// clear all pending messages that we may have received.
// over the wire, we wouldn't receive any more pending messages
// ether after calling disconnect.
clientIncoming.Clear();
// add server disconnected message with connId=1 because 0 is reserved
serverIncoming.Enqueue(new Message(1, EventType.Disconnected, null));
// add client disconnected message
clientIncoming.Enqueue(new Message(0, EventType.Disconnected, null));
// not connected anymore
clientConnected = false;
}
}
// messages should always be processed in early update
public override void ClientEarlyUpdate()
{
// note: process even if not connected because when calling
// Disconnect, we add a Disconnected event which still needs to be
// processed here.
while (clientIncoming.Count > 0)
{
Message message = clientIncoming.Dequeue();
switch (message.eventType)
{
case EventType.Connected:
Debug.Log("MemoryTransport Client Message: Connected");
OnClientConnected.Invoke();
break;
case EventType.Data:
Debug.Log($"MemoryTransport Client Message: Data: {BitConverter.ToString(message.data)}");
OnClientDataReceived.Invoke(new ArraySegment<byte>(message.data), 0);
break;
case EventType.Disconnected:
Debug.Log("MemoryTransport Client Message: Disconnected");
OnClientDisconnected.Invoke();
break;
}
}
}
public override bool ServerActive() => serverActive;
public override Uri ServerUri() => throw new NotImplementedException();
public override void ServerStart() { serverActive = true; }
public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
{
// only if server is running and client is connected
if (serverActive && clientConnected)
{
// a real transport fails for > max sized messages.
// mirror checks it, but let's guarantee that we catch > max
// sized message send attempts just like a real transport would.
// => helps to cover packet size issues i.e. for timestamp
// batching tests
int max = GetMaxPacketSize(channelId);
if (segment.Count > max)
throw new Exception($"MemoryTransport ServerSend of {segment.Count} bytes exceeds max of {max} bytes");
// copy segment data because it's only valid until return
byte[] data = new byte[segment.Count];
Array.Copy(segment.Array, segment.Offset, data, 0, segment.Count);
// add client data message
clientIncoming.Enqueue(new Message(0, EventType.Data, data));
}
}
public override void ServerDisconnect(int connectionId)
{
// clear all pending messages that we may have received.
// over the wire, we wouldn't receive any more pending messages
// ether after calling disconnect.
serverIncoming.Clear();
// add client disconnected message with connectionId
clientIncoming.Enqueue(new Message(connectionId, EventType.Disconnected, null));
// add server disconnected message with connectionId
serverIncoming.Enqueue(new Message(connectionId, EventType.Disconnected, null));
// not active anymore
serverActive = false;
}
public override string ServerGetClientAddress(int connectionId) => string.Empty;
public override void ServerStop()
{
// clear all pending messages that we may have received.
// over the wire, we wouldn't receive any more pending messages
// ether after calling stop.
serverIncoming.Clear();
// add client disconnected message
clientIncoming.Enqueue(new Message(0, EventType.Disconnected, null));
// add server disconnected message with connId=1 because 0 is reserved
serverIncoming.Enqueue(new Message(1, EventType.Disconnected, null));
// not active anymore
serverActive = false;
}
// messages should always be processed in early update
public override void ServerEarlyUpdate()
{
while (serverIncoming.Count > 0)
{
Message message = serverIncoming.Dequeue();
switch (message.eventType)
{
case EventType.Connected:
Debug.Log("MemoryTransport Server Message: Connected");
OnServerConnected.Invoke(message.connectionId);
break;
case EventType.Data:
Debug.Log($"MemoryTransport Server Message: Data: {BitConverter.ToString(message.data)}");
OnServerDataReceived.Invoke(message.connectionId, new ArraySegment<byte>(message.data), 0);
break;
case EventType.Disconnected:
Debug.Log("MemoryTransport Server Message: Disconnected");
OnServerDisconnected.Invoke(message.connectionId);
break;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6131cf1e8a1c14ef5b5253f86f3fc9c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
{
"name": "Mirror.Tests.Common",
"references": [
"Mirror"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4e9aca8a359ab48de906aedbfa1ffe21
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
// base class for networking tests to make things easier.
using NUnit.Framework;
namespace Mirror.Tests
{
public abstract class MirrorEditModeTest : MirrorTest
{
[SetUp]
public override void SetUp() => base.SetUp();
[TearDown]
public override void TearDown() => base.TearDown();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6110480a9c07423290301aedafb2a93
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
// base class for networking tests to make things easier.
using System.Collections;
using UnityEngine.TestTools;
namespace Mirror.Tests
{
public abstract class MirrorPlayModeTest : MirrorTest
{
// when overwriting, call it like this:
// yield return base.UnitySetUp();
[UnitySetUp]
public virtual IEnumerator UnitySetUp()
{
base.SetUp();
yield return null;
}
// when overwriting, call it like this:
// yield return base.UnityTearDown();
[UnityTearDown]
public virtual IEnumerator UnityTearDown()
{
base.TearDown();
yield return null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be3f9e24bcdb748728f846e88eea29f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,557 @@
// base class for networking tests to make things easier.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
// inherited by MirrorEditModeTest / MirrorPlayModeTest
// to call SetUp/TearDown by [SetUp]/[UnitySetUp] as needed
public abstract class MirrorTest
{
// keep track of networked GameObjects so we don't have to clean them
// up manually each time.
// CreateNetworked() adds to the list automatically.
public List<GameObject> instantiated;
// we usually need the memory transport
public MemoryTransport transport;
public virtual void SetUp()
{
instantiated = new List<GameObject>();
// need a transport to send & receive
Transport.activeTransport = transport = new GameObject().AddComponent<MemoryTransport>();
}
public virtual void TearDown()
{
NetworkClient.Shutdown();
NetworkServer.Shutdown();
// some tests might modify NetworkServer.connections without ever
// starting the server.
// NetworkServer.Shutdown() only clears connections if it was started.
// so let's do it manually for proper test cleanup here.
NetworkServer.connections.Clear();
foreach (GameObject go in instantiated)
if (go != null)
GameObject.DestroyImmediate(go);
GameObject.DestroyImmediate(transport.gameObject);
Transport.activeTransport = null;
}
// create a tracked GameObject for tests without Networkidentity
// add to tracker list if needed (useful for cleanups afterwards)
protected void CreateGameObject(out GameObject go)
{
go = new GameObject();
// track
instantiated.Add(go);
}
// create GameObject + MonoBehaviour<T>
// add to tracker list if needed (useful for cleanups afterwards)
protected void CreateGameObject<T>(out GameObject go, out T component)
where T : MonoBehaviour
{
CreateGameObject(out go);
component = go.AddComponent<T>();
}
// create GameObject + NetworkIdentity
// add to tracker list if needed (useful for cleanups afterwards)
protected void CreateNetworked(out GameObject go, out NetworkIdentity identity)
{
go = new GameObject();
identity = go.AddComponent<NetworkIdentity>();
// Awake is only called in play mode.
// call manually for initialization.
identity.Awake();
// track
instantiated.Add(go);
}
// create GameObject + NetworkIdentity + NetworkBehaviour<T>
// add to tracker list if needed (useful for cleanups afterwards)
protected void CreateNetworked<T>(out GameObject go, out NetworkIdentity identity, out T component)
where T : NetworkBehaviour
{
go = new GameObject();
identity = go.AddComponent<NetworkIdentity>();
component = go.AddComponent<T>();
// always set syncinterval = 0 for immediate testing
component.syncInterval = 0;
// Awake is only called in play mode.
// call manually for initialization.
identity.Awake();
// track
instantiated.Add(go);
}
// create GameObject + NetworkIdentity + 2x NetworkBehaviour<T>
// add to tracker list if needed (useful for cleanups afterwards)
protected void CreateNetworked<T, U>(out GameObject go, out NetworkIdentity identity, out T componentA, out U componentB)
where T : NetworkBehaviour
where U : NetworkBehaviour
{
go = new GameObject();
identity = go.AddComponent<NetworkIdentity>();
componentA = go.AddComponent<T>();
componentB = go.AddComponent<U>();
// always set syncinterval = 0 for immediate testing
componentA.syncInterval = 0;
componentB.syncInterval = 0;
// Awake is only called in play mode.
// call manually for initialization.
identity.Awake();
// track
instantiated.Add(go);
}
// create GameObject + NetworkIdentity + 2x NetworkBehaviour<T>
// add to tracker list if needed (useful for cleanups afterwards)
protected void CreateNetworked<T, U, V>(out GameObject go, out NetworkIdentity identity, out T componentA, out U componentB, out V componentC)
where T : NetworkBehaviour
where U : NetworkBehaviour
where V : NetworkBehaviour
{
go = new GameObject();
identity = go.AddComponent<NetworkIdentity>();
componentA = go.AddComponent<T>();
componentB = go.AddComponent<U>();
componentC = go.AddComponent<V>();
// always set syncinterval = 0 for immediate testing
componentA.syncInterval = 0;
componentB.syncInterval = 0;
componentC.syncInterval = 0;
// Awake is only called in play mode.
// call manually for initialization.
identity.Awake();
// track
instantiated.Add(go);
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
protected void CreateNetworkedAndSpawn(out GameObject go, out NetworkIdentity identity, NetworkConnection ownerConnection = null)
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
CreateNetworked(out go, out identity);
// spawn
NetworkServer.Spawn(go, ownerConnection);
ProcessMessages();
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
// => returns objects from client and from server.
// will be same in host mode.
protected void CreateNetworkedAndSpawn(
out GameObject serverGO, out NetworkIdentity serverIdentity,
out GameObject clientGO, out NetworkIdentity clientIdentity,
NetworkConnection ownerConnection = null)
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create one on server, one on client
// (spawning has to find it on client, it doesn't create it)
CreateNetworked(out serverGO, out serverIdentity);
CreateNetworked(out clientGO, out clientIdentity);
// give both a scene id and register it on client for spawnables
clientIdentity.sceneId = serverIdentity.sceneId = (ulong)serverGO.GetHashCode();
NetworkClient.spawnableObjects[clientIdentity.sceneId] = clientIdentity;
// spawn
NetworkServer.Spawn(serverGO, ownerConnection);
ProcessMessages();
// make sure the client really spawned it.
Assert.That(NetworkClient.spawned.ContainsKey(serverIdentity.netId));
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
protected void CreateNetworkedAndSpawn<T>(out GameObject go, out NetworkIdentity identity, out T component, NetworkConnection ownerConnection = null)
where T : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
CreateNetworked(out go, out identity, out component);
// spawn
NetworkServer.Spawn(go, ownerConnection);
ProcessMessages();
// double check that we have authority if we passed an owner connection
if (ownerConnection != null)
Debug.Assert(component.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
// => returns objects from client and from server.
// will be same in host mode.
protected void CreateNetworkedAndSpawn<T>(
out GameObject serverGO, out NetworkIdentity serverIdentity, out T serverComponent,
out GameObject clientGO, out NetworkIdentity clientIdentity, out T clientComponent,
NetworkConnection ownerConnection = null)
where T : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create one on server, one on client
// (spawning has to find it on client, it doesn't create it)
CreateNetworked(out serverGO, out serverIdentity, out serverComponent);
CreateNetworked(out clientGO, out clientIdentity, out clientComponent);
// give both a scene id and register it on client for spawnables
clientIdentity.sceneId = serverIdentity.sceneId = (ulong)serverGO.GetHashCode();
NetworkClient.spawnableObjects[clientIdentity.sceneId] = clientIdentity;
// spawn
NetworkServer.Spawn(serverGO, ownerConnection);
ProcessMessages();
// double check that we have authority if we passed an owner connection
if (ownerConnection != null)
Debug.Assert(serverComponent.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
// make sure the client really spawned it.
Assert.That(NetworkClient.spawned.ContainsKey(serverIdentity.netId));
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
protected void CreateNetworkedAndSpawn<T, U>(out GameObject go, out NetworkIdentity identity, out T componentA, out U componentB, NetworkConnection ownerConnection = null)
where T : NetworkBehaviour
where U : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
CreateNetworked(out go, out identity, out componentA, out componentB);
// spawn
NetworkServer.Spawn(go, ownerConnection);
ProcessMessages();
// double check that we have authority if we passed an owner connection
if (ownerConnection != null)
{
Debug.Assert(componentA.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
Debug.Assert(componentB.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
}
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
// => returns objects from client and from server.
// will be same in host mode.
protected void CreateNetworkedAndSpawn<T, U>(
out GameObject serverGO, out NetworkIdentity serverIdentity, out T serverComponentA, out U serverComponentB,
out GameObject clientGO, out NetworkIdentity clientIdentity, out T clientComponentA, out U clientComponentB,
NetworkConnection ownerConnection = null)
where T : NetworkBehaviour
where U : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create one on server, one on client
// (spawning has to find it on client, it doesn't create it)
CreateNetworked(out serverGO, out serverIdentity, out serverComponentA, out serverComponentB);
CreateNetworked(out clientGO, out clientIdentity, out clientComponentA, out clientComponentB);
// give both a scene id and register it on client for spawnables
clientIdentity.sceneId = serverIdentity.sceneId = (ulong)serverGO.GetHashCode();
NetworkClient.spawnableObjects[clientIdentity.sceneId] = clientIdentity;
// spawn
NetworkServer.Spawn(serverGO, ownerConnection);
ProcessMessages();
// double check that we have authority if we passed an owner connection
if (ownerConnection != null)
{
Debug.Assert(serverComponentA.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
Debug.Assert(serverComponentB.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
}
// make sure the client really spawned it.
Assert.That(NetworkClient.spawned.ContainsKey(serverIdentity.netId));
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
protected void CreateNetworkedAndSpawn<T, U, V>(out GameObject go, out NetworkIdentity identity, out T componentA, out U componentB, out V componentC, NetworkConnection ownerConnection = null)
where T : NetworkBehaviour
where U : NetworkBehaviour
where V : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
CreateNetworked(out go, out identity, out componentA, out componentB, out componentC);
// spawn
NetworkServer.Spawn(go, ownerConnection);
ProcessMessages();
// double check that we have authority if we passed an owner connection
if (ownerConnection != null)
{
Debug.Assert(componentA.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
Debug.Assert(componentB.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
Debug.Assert(componentC.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
}
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN
// => ownerConnection can be NetworkServer.localConnection if needed.
// => returns objects from client and from server.
// will be same in host mode.
protected void CreateNetworkedAndSpawn<T, U, V>(
out GameObject serverGO, out NetworkIdentity serverIdentity, out T serverComponentA, out U serverComponentB, out V serverComponentC,
out GameObject clientGO, out NetworkIdentity clientIdentity, out T clientComponentA, out U clientComponentB, out V clientComponentC,
NetworkConnection ownerConnection = null)
where T : NetworkBehaviour
where U : NetworkBehaviour
where V : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create one on server, one on client
// (spawning has to find it on client, it doesn't create it)
CreateNetworked(out serverGO, out serverIdentity, out serverComponentA, out serverComponentB, out serverComponentC);
CreateNetworked(out clientGO, out clientIdentity, out clientComponentA, out clientComponentB, out clientComponentC);
// give both a scene id and register it on client for spawnables
clientIdentity.sceneId = serverIdentity.sceneId = (ulong)serverGO.GetHashCode();
NetworkClient.spawnableObjects[clientIdentity.sceneId] = clientIdentity;
// spawn
NetworkServer.Spawn(serverGO, ownerConnection);
ProcessMessages();
// double check that we have authority if we passed an owner connection
if (ownerConnection != null)
{
Debug.Assert(serverComponentA.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
Debug.Assert(serverComponentB.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
Debug.Assert(serverComponentC.hasAuthority == true, $"Behaviour Had Wrong Authority when spawned, This means that the test is broken and will give the wrong results");
}
// make sure the client really spawned it.
Assert.That(NetworkClient.spawned.ContainsKey(serverIdentity.netId));
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN PLAYER.
// often times, we really need a player object for the client to receive
// certain messages.
protected void CreateNetworkedAndSpawnPlayer(out GameObject go, out NetworkIdentity identity, NetworkConnection ownerConnection)
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create a networked object
CreateNetworked(out go, out identity);
// add as player & process spawn message on client.
NetworkServer.AddPlayerForConnection(ownerConnection, go);
ProcessMessages();
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN PLAYER.
// often times, we really need a player object for the client to receive
// certain messages.
// => returns objects from client and from server.
// will be same in host mode.
protected void CreateNetworkedAndSpawnPlayer(
out GameObject serverGO, out NetworkIdentity serverIdentity,
out GameObject clientGO, out NetworkIdentity clientIdentity,
NetworkConnection ownerConnection)
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create one on server, one on client
// (spawning has to find it on client, it doesn't create it)
CreateNetworked(out serverGO, out serverIdentity);
CreateNetworked(out clientGO, out clientIdentity);
// give both a scene id and register it on client for spawnables
clientIdentity.sceneId = serverIdentity.sceneId = (ulong)serverGO.GetHashCode();
NetworkClient.spawnableObjects[clientIdentity.sceneId] = clientIdentity;
// add as player & process spawn message on client.
NetworkServer.AddPlayerForConnection(ownerConnection, serverGO);
ProcessMessages();
// make sure the client really spawned it.
Assert.That(NetworkClient.spawned.ContainsKey(serverIdentity.netId));
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN PLAYER.
// often times, we really need a player object for the client to receive
// certain messages.
protected void CreateNetworkedAndSpawnPlayer<T>(out GameObject go, out NetworkIdentity identity, out T component, NetworkConnection ownerConnection)
where T : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create a networked object
CreateNetworked(out go, out identity, out component);
// add as player & process spawn message on client.
NetworkServer.AddPlayerForConnection(ownerConnection, go);
ProcessMessages();
}
// create GameObject + NetworkIdentity + NetworkBehaviour & SPAWN PLAYER.
// often times, we really need a player object for the client to receive
// certain messages.
// => returns objects from client and from server.
// will be same in host mode.
protected void CreateNetworkedAndSpawnPlayer<T>(
out GameObject serverGO, out NetworkIdentity serverIdentity, out T serverComponent,
out GameObject clientGO, out NetworkIdentity clientIdentity, out T clientComponent,
NetworkConnection ownerConnection)
where T : NetworkBehaviour
{
// server & client need to be active before spawning
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// create one on server, one on client
// (spawning has to find it on client, it doesn't create it)
CreateNetworked(out serverGO, out serverIdentity, out serverComponent);
CreateNetworked(out clientGO, out clientIdentity, out clientComponent);
// give both a scene id and register it on client for spawnables
clientIdentity.sceneId = serverIdentity.sceneId = (ulong)serverGO.GetHashCode();
NetworkClient.spawnableObjects[clientIdentity.sceneId] = clientIdentity;
// add as player & process spawn message on client.
NetworkServer.AddPlayerForConnection(ownerConnection, serverGO);
ProcessMessages();
// make sure the client really spawned it.
Assert.That(NetworkClient.spawned.ContainsKey(serverIdentity.netId));
}
// fully connect client to local server
// gives out the server's connection to client for convenience if needed
protected void ConnectClientBlocking(out NetworkConnectionToClient connectionToClient)
{
NetworkClient.Connect("127.0.0.1");
UpdateTransport();
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
connectionToClient = NetworkServer.connections.Values.First();
}
// fully connect client to local server & authenticate
protected void ConnectClientBlockingAuthenticated(out NetworkConnectionToClient connectionToClient)
{
ConnectClientBlocking(out connectionToClient);
// authenticate server & client connections
connectionToClient.isAuthenticated = true;
NetworkClient.connection.isAuthenticated = true;
}
// fully connect client to local server & authenticate & set read
protected void ConnectClientBlockingAuthenticatedAndReady(out NetworkConnectionToClient connectionToClient)
{
ConnectClientBlocking(out connectionToClient);
// authenticate server & client connections
connectionToClient.isAuthenticated = true;
NetworkClient.connection.isAuthenticated = true;
// set ready
NetworkClient.Ready();
ProcessMessages();
Assert.That(connectionToClient.isReady, Is.True);
}
// fully connect HOST client to local server
// sets NetworkServer.localConnection / NetworkClient.connection.
protected void ConnectHostClientBlocking()
{
NetworkClient.ConnectHost();
NetworkClient.ConnectLocalServer();
UpdateTransport();
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
}
// fully connect client to local server & authenticate & set read
protected void ConnectHostClientBlockingAuthenticatedAndReady()
{
ConnectHostClientBlocking();
// authenticate server & client connections
NetworkServer.localConnection.isAuthenticated = true;
NetworkClient.connection.isAuthenticated = true;
// set ready
NetworkClient.Ready();
ProcessMessages();
Assert.That(NetworkServer.localConnection.isReady, Is.True);
}
protected void UpdateTransport()
{
transport.ClientEarlyUpdate();
transport.ServerEarlyUpdate();
}
protected void ProcessMessages()
{
// server & client need to be active
Debug.Assert(NetworkClient.active, "NetworkClient needs to be active before spawning.");
Debug.Assert(NetworkServer.active, "NetworkServer needs to be active before spawning.");
// update server & client so batched messages are flushed
NetworkClient.NetworkLateUpdate();
NetworkServer.NetworkLateUpdate();
// update transport so sent messages are received
UpdateTransport();
}
// helper function to create local connection pair
protected void CreateLocalConnectionPair(out LocalConnectionToClient connectionToClient, out LocalConnectionToServer connectionToServer)
{
connectionToClient = new LocalConnectionToClient();
connectionToServer = new LocalConnectionToServer();
connectionToClient.connectionToServer = connectionToServer;
connectionToServer.connectionToClient = connectionToClient;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67c5177a4b35749b8b9c4ca7107d8c25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,112 @@
fileFormatVersion: 2
guid: 601589975b5ca43179df797ffa3b468d
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: LinuxUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,117 @@
fileFormatVersion: 2
guid: 00713e0dc7f8d4ed2906179677779859
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: LinuxUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
WebGL: WebGL
second:
enabled: 0
settings: {}
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1d3db979f8f114547977d68180a77080
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6d113040314b4578b93a6f140f064f09
timeCreated: 1623240703

View File

@ -0,0 +1,230 @@
using System;
using System.Linq;
using NUnit.Framework;
namespace Mirror.Tests.Batching
{
public class BatcherTests
{
Batcher batcher;
const int Threshold = 8 + 4; // 8 bytes timestamp + 4 bytes message
NetworkWriter writer;
// timestamp and serialized timestamp for convenience
const double TimeStamp = Math.PI;
[SetUp]
public void SetUp()
{
batcher = new Batcher(Threshold);
writer = new NetworkWriter();
}
// helper function to create a batch prefixed by timestamp
public static byte[] ConcatTimestamp(double tickTimeStamp, byte[] data)
{
NetworkWriter writer = new NetworkWriter();
writer.WriteDouble(tickTimeStamp);
writer.WriteBytes(data, 0, data.Length);
return writer.ToArray();
}
[Test]
public void AddMessage()
{
byte[] message = {0x01, 0x02};
batcher.AddMessage(new ArraySegment<byte>(message));
}
[Test]
public void MakeNextBatch_OnlyAcceptsFreshWriter()
{
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x01}));
writer.WriteByte(0);
Assert.Throws<ArgumentException>(() => {
batcher.MakeNextBatch(writer, TimeStamp);
});
}
[Test]
public void MakeNextBatch_NoMessage()
{
// make batch with no message
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(false));
}
[Test]
public void MakeNextBatch_OneMessage()
{
// add message
byte[] message = {0x01, 0x02};
batcher.AddMessage(new ArraySegment<byte>(message));
// make batch
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, message)));
}
[Test]
public void MakeNextBatch_MultipleMessages_AlmostFullBatch()
{
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x01, 0x02}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x03}));
// make batch
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x01, 0x02, 0x03})));
// there should be no more batches to make
Assert.That(batcher.MakeNextBatch(writer, TimeStamp), Is.False);
}
[Test]
public void MakeNextBatch_MultipleMessages_ExactlyFullBatch()
{
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x01, 0x02}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x03, 0x04}));
// make batch
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x01, 0x02, 0x03, 0x04})));
// there should be no more batches to make
Assert.That(batcher.MakeNextBatch(writer, TimeStamp), Is.False);
}
[Test]
public void MakeNextBatch_MultipleMessages_MoreThanOneBatch()
{
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x01, 0x02}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x03, 0x04}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x05}));
// first batch
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x01, 0x02, 0x03, 0x04})));
// reset writer
writer.Position = 0;
// second batch
result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x05})));
}
[Test]
public void MakeNextBatch_MultipleMessages_Small_Giant_Small()
{
// small, too big to include in batch, small
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x01}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x02, 0x03, 0x04, 0x05}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x06, 0x07}));
// first batch
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x01})));
// reset writer
writer.Position = 0;
// second batch
result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x02, 0x03, 0x04, 0x05})));
// reset writer
writer.Position = 0;
// third batch
result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
// check result: <<tickTimeStamp:8, message>>
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x06, 0x07})));
}
// messages > threshold should simply be single batches.
// those need to be supported too, for example:
// kcp prefers MTU sized batches
// but we still allow up to 144 KB max message size
[Test]
public void MakeNextBatch_LargerThanThreshold()
{
// make a larger than threshold message
byte[] large = new byte[Threshold + 1];
for (int i = 0; i < Threshold + 1; ++i)
large[i] = (byte)i;
batcher.AddMessage(new ArraySegment<byte>(large));
// result should be only the large message
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, large)));
}
// messages > threshold should simply be single batches.
// those need to be supported too, for example:
// kcp prefers MTU sized batches
// but we still allow up to 144 KB max message size
[Test]
public void MakeNextBatch_LargerThanThreshold_BetweenSmallerMessages()
{
// make a larger than threshold message
byte[] large = new byte[Threshold + 1];
for (int i = 0; i < Threshold + 1; ++i)
large[i] = (byte)i;
// add two small, one large, two small messages.
// to make sure everything around it is still batched,
// and the large one is a separate batch.
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x01}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x02}));
batcher.AddMessage(new ArraySegment<byte>(large));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x03}));
batcher.AddMessage(new ArraySegment<byte>(new byte[]{0x04}));
// first batch should be the two small messages
bool result = batcher.MakeNextBatch(writer, TimeStamp);
Assert.That(result, Is.EqualTo(true));
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, new byte[]{0x01, 0x02})));
// reset writer
writer.Position = 0;
// second batch should be only the large message
result = batcher.MakeNextBatch(writer, TimeStamp + 1);
Assert.That(result, Is.EqualTo(true));
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp + 1, large)));
// reset writer
writer.Position = 0;
// third batch be the two small messages
result = batcher.MakeNextBatch(writer, TimeStamp + 2);
Assert.That(result, Is.EqualTo(true));
Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp + 2, new byte[]{0x03, 0x04})));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 787d83b7e2ca4547aca251617d91f7d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,138 @@
using System;
using NUnit.Framework;
namespace Mirror.Tests.Batching
{
public class UnbatcherTests
{
Unbatcher unbatcher;
const double TimeStamp = Math.PI;
[SetUp]
public void SetUp()
{
unbatcher = new Unbatcher();
}
[Test]
public void GetNextMessage_NoBatches()
{
bool result = unbatcher.GetNextMessage(out _, out _);
Assert.That(result, Is.False);
}
// test for nimoyd bug, where calling getnextmessage after the previous
// call already returned false would cause an InvalidOperationException.
[Test]
public void GetNextMessage_True_False_False_InvalidOperationException()
{
// add batch
byte[] batch = BatcherTests.ConcatTimestamp(TimeStamp, new byte[2]);
unbatcher.AddBatch(new ArraySegment<byte>(batch));
// get next message, pretend we read the whole thing
bool result = unbatcher.GetNextMessage(out NetworkReader reader, out _);
Assert.That(result, Is.True);
reader.Position = reader.Length;
// shouldn't get another one
result = unbatcher.GetNextMessage(out reader, out _);
Assert.That(result, Is.False);
// calling it again was causing "InvalidOperationException: Queue empty"
result = unbatcher.GetNextMessage(out reader, out _);
Assert.That(result, Is.False);
}
[Test]
public void GetNextMessage_OneBatch()
{
// add one batch
byte[] batch = BatcherTests.ConcatTimestamp(TimeStamp, new byte[]{0x01, 0x02});
unbatcher.AddBatch(new ArraySegment<byte>(batch));
// get next message, read first byte
bool result = unbatcher.GetNextMessage(out NetworkReader reader, out double remoteTimeStamp);
Assert.That(result, Is.True);
Assert.That(reader.ReadByte(), Is.EqualTo(0x01));
Assert.That(remoteTimeStamp, Is.EqualTo(TimeStamp));
// get next message, read last byte
result = unbatcher.GetNextMessage(out reader, out remoteTimeStamp);
Assert.That(result, Is.True);
Assert.That(reader.ReadByte(), Is.EqualTo(0x02));
Assert.That(remoteTimeStamp, Is.EqualTo(TimeStamp));
// there should be no more messages
result = unbatcher.GetNextMessage(out _, out _);
Assert.That(result, Is.False);
}
[Test]
public void GetNextMessage_MultipleBatches()
{
// add first batch
byte[] firstBatch = BatcherTests.ConcatTimestamp(TimeStamp, new byte[]{0x01, 0x02});
unbatcher.AddBatch(new ArraySegment<byte>(firstBatch));
// add second batch
byte[] secondBatch = BatcherTests.ConcatTimestamp(TimeStamp + 1, new byte[]{0x03, 0x04});
unbatcher.AddBatch(new ArraySegment<byte>(secondBatch));
// get next message, read everything
bool result = unbatcher.GetNextMessage(out NetworkReader reader, out double remoteTimeStamp);
Assert.That(result, Is.True);
Assert.That(reader.ReadByte(), Is.EqualTo(0x01));
Assert.That(reader.ReadByte(), Is.EqualTo(0x02));
Assert.That(remoteTimeStamp, Is.EqualTo(TimeStamp));
// get next message, should point to next batch at Timestamp + 1
result = unbatcher.GetNextMessage(out reader, out remoteTimeStamp);
Assert.That(result, Is.True);
Assert.That(reader.ReadByte(), Is.EqualTo(0x03));
Assert.That(reader.ReadByte(), Is.EqualTo(0x04));
Assert.That(remoteTimeStamp, Is.EqualTo(TimeStamp + 1));
// there should be no more messages
result = unbatcher.GetNextMessage(out _, out _);
Assert.That(result, Is.False);
}
// make sure that retiring a batch, then adding a new batch works.
// previously there was a bug where the batch was retired,
// the reader still pointed to the old batch with pos=len,
// a new batch was added
// GetNextMessage() still returned false because reader still pointed to
// the old batch with pos=len.
[Test]
public void RetireBatchAndTryNewBatch()
{
// add first batch
byte[] firstBatch = BatcherTests.ConcatTimestamp(TimeStamp, new byte[]{0x01, 0x02});
unbatcher.AddBatch(new ArraySegment<byte>(firstBatch));
// read everything
bool result = unbatcher.GetNextMessage(out NetworkReader reader, out double remoteTimeStamp);
Assert.That(result, Is.True);
Assert.That(reader.ReadByte(), Is.EqualTo(0x01));
Assert.That(reader.ReadByte(), Is.EqualTo(0x02));
Assert.That(remoteTimeStamp, Is.EqualTo(TimeStamp));
// try to read again.
// reader will be at limit, which should retire the batch.
result = unbatcher.GetNextMessage(out _, out _);
Assert.That(result, Is.False);
// add new batch
byte[] secondBatch = BatcherTests.ConcatTimestamp(TimeStamp + 1, new byte[]{0x03, 0x04});
unbatcher.AddBatch(new ArraySegment<byte>(secondBatch));
// read everything
result = unbatcher.GetNextMessage(out reader, out remoteTimeStamp);
Assert.That(result, Is.True);
Assert.That(reader.ReadByte(), Is.EqualTo(0x03));
Assert.That(reader.ReadByte(), Is.EqualTo(0x04));
Assert.That(remoteTimeStamp, Is.EqualTo(TimeStamp + 1));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ccc928bb22f5469886cef8c6132aa717
timeCreated: 1623240730

View File

@ -0,0 +1,134 @@
using System;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests.RemoteAttrributeTest
{
class VirtualClientRpc : NetworkBehaviour
{
public event Action<int> onVirtualSendInt;
[ClientRpc]
public virtual void RpcSendInt(int someInt) =>
onVirtualSendInt?.Invoke(someInt);
}
class VirtualNoOverrideClientRpc : VirtualClientRpc {}
class VirtualOverrideClientRpc : VirtualClientRpc
{
public event Action<int> onOverrideSendInt;
[ClientRpc]
public override void RpcSendInt(int someInt) =>
onOverrideSendInt?.Invoke(someInt);
}
class VirtualOverrideClientRpcWithBase : VirtualClientRpc
{
public event Action<int> onOverrideSendInt;
[ClientRpc]
public override void RpcSendInt(int someInt)
{
base.RpcSendInt(someInt);
onOverrideSendInt?.Invoke(someInt);
}
}
public class ClientRpcOverrideTest : RemoteTestBase
{
[Test]
public void VirtualRpcIsCalled()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualClientRpc hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.RpcSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(1));
}
[Test]
public void VirtualCommandWithNoOverrideIsCalled()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualNoOverrideClientRpc hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.RpcSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(1));
}
[Test]
public void OverrideVirtualRpcIsCalled()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualOverrideClientRpc hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
int overrideCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
};
hostBehaviour.onOverrideSendInt += incomingInt =>
{
overrideCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.RpcSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(0));
Assert.That(overrideCallCount, Is.EqualTo(1));
}
[Test]
public void OverrideVirtualWithBaseCallsBothVirtualAndBase()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualOverrideClientRpcWithBase hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
int overrideCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.onOverrideSendInt += incomingInt =>
{
overrideCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.RpcSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(1));
Assert.That(overrideCallCount, Is.EqualTo(1));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9759f0eddb9731c4b881ac30bacc0de0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,155 @@
using System;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests.RemoteAttrributeTest
{
class ClientRpcBehaviour : NetworkBehaviour
{
public event Action<int> onSendInt;
[ClientRpc]
public void SendInt(int someInt) =>
onSendInt?.Invoke(someInt);
}
class ExcludeOwnerBehaviour : NetworkBehaviour
{
public event Action<int> onSendInt;
[ClientRpc(includeOwner = false)]
public void RpcSendInt(int someInt) =>
onSendInt?.Invoke(someInt);
}
class AbstractNetworkBehaviourClientRpcBehaviour : NetworkBehaviour
{
public abstract class MockMonsterBase : NetworkBehaviour {}
public class MockZombie : MockMonsterBase {}
public class MockWolf : MockMonsterBase {}
public event Action<MockMonsterBase> onSendMonsterBase;
[ClientRpc]
public void RpcSendMonster(MockMonsterBase someMonster) =>
onSendMonsterBase?.Invoke(someMonster);
}
class RpcOverloads : NetworkBehaviour
{
public int firstCalled = 0;
public int secondCalled = 0;
[ClientRpc]
public void RpcTest(int _) => ++firstCalled;
[ClientRpc]
public void RpcTest(string _) => ++secondCalled;
}
public class ClientRpcTest : RemoteTestBase
{
[Test]
public void RpcIsCalled()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out ClientRpcBehaviour hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int called = 0;
hostBehaviour.onSendInt += incomingInt =>
{
called++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.SendInt(someInt);
ProcessMessages();
Assert.That(called, Is.EqualTo(1));
}
[Test]
public void RpcIsCalledForNotOwner()
{
// spawn without owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out ExcludeOwnerBehaviour hostBehaviour);
const int someInt = 20;
int called = 0;
hostBehaviour.onSendInt += incomingInt =>
{
called++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.RpcSendInt(someInt);
ProcessMessages();
Assert.That(called, Is.EqualTo(1));
}
[Test]
public void RpcNotCalledForOwner()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out ExcludeOwnerBehaviour hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int called = 0;
hostBehaviour.onSendInt += incomingInt =>
{
called++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.RpcSendInt(someInt);
ProcessMessages();
Assert.That(called, Is.EqualTo(0));
}
[Test]
public void RpcIsCalledWithAbstractNetworkBehaviourParameter()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out AbstractNetworkBehaviourClientRpcBehaviour hostBehaviour, NetworkServer.localConnection);
// spawn clientrpc parameter targets
CreateNetworkedAndSpawn(out _, out _, out AbstractNetworkBehaviourClientRpcBehaviour.MockWolf wolf, NetworkServer.localConnection);
CreateNetworkedAndSpawn(out _, out _, out AbstractNetworkBehaviourClientRpcBehaviour.MockZombie zombie, NetworkServer.localConnection);
AbstractNetworkBehaviourClientRpcBehaviour.MockMonsterBase currentMonster = null;
int called = 0;
hostBehaviour.onSendMonsterBase += incomingMonster =>
{
called++;
Assert.That(incomingMonster, Is.EqualTo(currentMonster));
};
currentMonster = wolf;
hostBehaviour.RpcSendMonster(currentMonster);
ProcessMessages();
Assert.That(called, Is.EqualTo(1));
currentMonster = zombie;
hostBehaviour.RpcSendMonster(currentMonster);
ProcessMessages();
Assert.That(called, Is.EqualTo(2));
}
// RemoteCalls uses md.FullName which gives us the full command/rpc name
// like "System.Void Mirror.Tests.RemoteAttrributeTest.AuthorityBehaviour::SendInt(System.Int32)"
// which means overloads with same name but different types should work.
[Test]
public void RpcOverload()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out RpcOverloads hostBehaviour, NetworkServer.localConnection);
hostBehaviour.RpcTest(42);
hostBehaviour.RpcTest("A");
ProcessMessages();
Assert.That(hostBehaviour.firstCalled, Is.EqualTo(1));
Assert.That(hostBehaviour.secondCalled, Is.EqualTo(1));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2928376e56382b646975dd00b68c2287
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
using System;
using NUnit.Framework;
namespace Mirror.Tests.ClientSceneTests
{
public class ClientSceneTests_ClearSpawners : ClientSceneTestsBase
{
[Test]
public void RemovesAllPrefabsFromDictionary()
{
NetworkClient.prefabs.Add(Guid.NewGuid(), null);
NetworkClient.prefabs.Add(Guid.NewGuid(), null);
NetworkClient.ClearSpawners();
Assert.IsEmpty(NetworkClient.prefabs);
}
[Test]
public void RemovesAllSpawnHandlersFromDictionary()
{
NetworkClient.spawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.spawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.ClearSpawners();
Assert.IsEmpty(NetworkClient.spawnHandlers);
}
[Test]
public void RemovesAllUnspawnHandlersFromDictionary()
{
NetworkClient.unspawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.unspawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.ClearSpawners();
Assert.IsEmpty(NetworkClient.unspawnHandlers);
}
[Test]
public void ClearsAllDictionary()
{
NetworkClient.prefabs.Add(Guid.NewGuid(), null);
NetworkClient.prefabs.Add(Guid.NewGuid(), null);
NetworkClient.spawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.spawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.unspawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.unspawnHandlers.Add(Guid.NewGuid(), null);
NetworkClient.ClearSpawners();
Assert.IsEmpty(NetworkClient.prefabs);
Assert.IsEmpty(NetworkClient.spawnHandlers);
Assert.IsEmpty(NetworkClient.unspawnHandlers);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad2fe5633a9652045a5f7eb2643b6956
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
using System;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests.ClientSceneTests
{
public class ClientSceneTests_GetPrefab : ClientSceneTestsBase
{
[Test]
public void ReturnsFalseForEmptyGuid()
{
bool result = NetworkClient.GetPrefab(new Guid(), out GameObject prefab);
Assert.IsFalse(result);
Assert.IsNull(prefab);
}
[Test]
public void ReturnsFalseForPrefabNotFound()
{
Guid guid = Guid.NewGuid();
bool result = NetworkClient.GetPrefab(guid, out GameObject prefab);
Assert.IsFalse(result);
Assert.IsNull(prefab);
}
[Test]
public void ReturnsFalseForPrefabIsNull()
{
Guid guid = Guid.NewGuid();
NetworkClient.prefabs.Add(guid, null);
bool result = NetworkClient.GetPrefab(guid, out GameObject prefab);
Assert.IsFalse(result);
Assert.IsNull(prefab);
}
[Test]
public void ReturnsTrueWhenPrefabIsFound()
{
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
bool result = NetworkClient.GetPrefab(validPrefabGuid, out GameObject prefab);
Assert.IsTrue(result);
Assert.NotNull(prefab);
}
[Test]
public void HasOutPrefabWithCorrectGuid()
{
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
NetworkClient.GetPrefab(validPrefabGuid, out GameObject prefab);
Assert.NotNull(prefab);
NetworkIdentity networkID = prefab.GetComponent<NetworkIdentity>();
Assert.AreEqual(networkID.assetId, validPrefabGuid);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 27cbff3906928974bb20c3f50eacbbb0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,736 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Mirror.Tests.ClientSceneTests
{
public class PayloadTestBehaviour : NetworkBehaviour
{
public int value;
public Vector3 direction;
public event Action OnDeserializeCalled;
public event Action OnSerializeCalled;
public override bool OnSerialize(NetworkWriter writer, bool initialState)
{
base.OnSerialize(writer, initialState);
writer.WriteInt(value);
writer.WriteVector3(direction);
OnSerializeCalled?.Invoke();
return true;
}
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
base.OnDeserialize(reader, initialState);
value = reader.ReadInt();
direction = reader.ReadVector3();
OnDeserializeCalled?.Invoke();
}
}
public class BehaviourWithEvents : NetworkBehaviour
{
public event Action OnStartAuthorityCalled;
public event Action OnStartClientCalled;
public event Action OnStartLocalPlayerCalled;
public override void OnStartAuthority()
{
OnStartAuthorityCalled?.Invoke();
}
public override void OnStartClient()
{
OnStartClientCalled?.Invoke();
}
public override void OnStartLocalPlayer()
{
OnStartLocalPlayerCalled?.Invoke();
}
}
public class ClientSceneTests_OnSpawn : ClientSceneTestsBase
{
Dictionary<uint, NetworkIdentity> spawned => NetworkClient.spawned;
[TearDown]
public override void TearDown()
{
spawned.Clear();
base.TearDown();
}
[Test]
public void FindOrSpawnObject_FindExistingObject()
{
CreateNetworked(out _, out NetworkIdentity existing);
const uint netId = 1000;
existing.netId = netId;
spawned.Add(netId, existing);
SpawnMessage msg = new SpawnMessage
{
netId = netId
};
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity found);
Assert.IsTrue(success);
Assert.That(found, Is.EqualTo(existing));
}
[Test]
public void FindOrSpawnObject_ErrorWhenNoExistingAndAssetIdAndSceneIdAreBothEmpty()
{
const uint netId = 1001;
SpawnMessage msg = new SpawnMessage
{
assetId = new Guid(),
sceneId = 0,
netId = netId
};
LogAssert.Expect(LogType.Error, $"OnSpawn message with netId '{netId}' has no AssetId or sceneId");
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsFalse(success);
Assert.IsNull(networkIdentity);
}
[Test]
public void FindOrSpawnObject_SpawnsFromPrefabDictionary()
{
const uint netId = 1002;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
assetId = validPrefabGuid
};
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsTrue(success);
Assert.IsNotNull(networkIdentity);
Assert.That(networkIdentity.name, Is.EqualTo($"{validPrefab.name}(Clone)"));
// cleanup
GameObject.DestroyImmediate(networkIdentity.gameObject);
}
[Test]
public void FindOrSpawnObject_ErrorWhenPrefabInNullInDictionary()
{
const uint netId = 1002;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
assetId = validPrefabGuid
};
// could happen if the prefab is destroyed or unloaded
NetworkClient.prefabs.Add(validPrefabGuid, null);
LogAssert.Expect(LogType.Error, $"Failed to spawn server object, did you forget to add it to the NetworkManager? assetId={msg.assetId} netId={msg.netId}");
LogAssert.Expect(LogType.Error, $"Could not spawn assetId={msg.assetId} scene={msg.sceneId:X} netId={msg.netId}");
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsFalse(success);
Assert.IsNull(networkIdentity);
}
[Test]
public void FindOrSpawnObject_SpawnsFromPrefabIfBothPrefabAndHandlerExists()
{
const uint netId = 1003;
int handlerCalled = 0;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
assetId = validPrefabGuid
};
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
NetworkClient.spawnHandlers.Add(validPrefabGuid, x =>
{
handlerCalled++;
CreateNetworked(out GameObject go, out NetworkIdentity _);
return go;
});
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsTrue(success);
Assert.IsNotNull(networkIdentity);
Assert.That(networkIdentity.name, Is.EqualTo($"{validPrefab.name}(Clone)"));
Assert.That(handlerCalled, Is.EqualTo(0), "Handler should not have been called");
}
[Test]
public void FindOrSpawnObject_SpawnHandlerCalledFromDictionary()
{
const uint netId = 1003;
int handlerCalled = 0;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
assetId = validPrefabGuid
};
GameObject createdInhandler = null;
NetworkClient.spawnHandlers.Add(validPrefabGuid, x =>
{
handlerCalled++;
Assert.That(x, Is.EqualTo(msg));
CreateNetworked(out createdInhandler, out NetworkIdentity _);
return createdInhandler;
});
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsTrue(success);
Assert.IsNotNull(networkIdentity);
Assert.That(handlerCalled, Is.EqualTo(1));
Assert.That(networkIdentity.gameObject, Is.EqualTo(createdInhandler), "Object returned should be the same object created by the spawn handler");
}
[Test]
public void FindOrSpawnObject_ErrorWhenSpawnHanlderReturnsNull()
{
const uint netId = 1003;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
assetId = validPrefabGuid
};
NetworkClient.spawnHandlers.Add(validPrefabGuid, (x) => null);
LogAssert.Expect(LogType.Error, $"Spawn Handler returned null, Handler assetId '{msg.assetId}'");
LogAssert.Expect(LogType.Error, $"Could not spawn assetId={msg.assetId} scene={msg.sceneId:X} netId={msg.netId}");
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsFalse(success);
Assert.IsNull(networkIdentity);
}
[Test]
public void FindOrSpawnObject_ErrorWhenSpawnHanlderReturnsWithoutNetworkIdentity()
{
const uint netId = 1003;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
assetId = validPrefabGuid
};
NetworkClient.spawnHandlers.Add(validPrefabGuid, (x) =>
{
CreateGameObject(out GameObject go);
return go;
});
LogAssert.Expect(LogType.Error, $"Object Spawned by handler did not have a NetworkIdentity, Handler assetId '{validPrefabGuid}'");
LogAssert.Expect(LogType.Error, $"Could not spawn assetId={msg.assetId} scene={msg.sceneId:X} netId={msg.netId}");
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsFalse(success);
Assert.IsNull(networkIdentity);
}
NetworkIdentity CreateSceneObject(ulong sceneId)
{
CreateNetworked(out _, out NetworkIdentity identity);
// set sceneId to zero as it is set in onvalidate (does not set id at runtime)
identity.sceneId = sceneId;
NetworkClient.spawnableObjects.Add(sceneId, identity);
return identity;
}
[Test]
public void FindOrSpawnObject_UsesSceneIdToSpawnFromSpawnableObjectsDictionary()
{
const uint netId = 1003;
const int sceneId = 100020;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
sceneId = sceneId
};
NetworkIdentity sceneObject = CreateSceneObject(sceneId);
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsTrue(success);
Assert.IsNotNull(networkIdentity);
Assert.That(networkIdentity, Is.EqualTo(sceneObject));
}
[Test]
public void FindOrSpawnObject_SpawnsUsingSceneIdInsteadOfAssetId()
{
const uint netId = 1003;
const int sceneId = 100020;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
sceneId = sceneId,
assetId = validPrefabGuid
};
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
NetworkIdentity sceneObject = CreateSceneObject(sceneId);
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsTrue(success);
Assert.IsNotNull(networkIdentity);
Assert.That(networkIdentity, Is.EqualTo(sceneObject));
}
[Test]
public void FindOrSpawnObject_ErrorWhenSceneIdIsNotInSpawnableObjectsDictionary()
{
const uint netId = 1004;
const int sceneId = 100021;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
sceneId = sceneId,
};
LogAssert.Expect(LogType.Error, $"Spawn scene object not found for {msg.sceneId:X}. Make sure that client and server use exactly the same project. This only happens if the hierarchy gets out of sync.");
LogAssert.Expect(LogType.Error, $"Could not spawn assetId={msg.assetId} scene={msg.sceneId:X} netId={msg.netId}");
bool success = NetworkClient.FindOrSpawnObject(msg, out NetworkIdentity networkIdentity);
Assert.IsFalse(success);
Assert.IsNull(networkIdentity);
}
[Test]
public void ApplyPayload_AppliesTransform()
{
const uint netId = 1000;
CreateNetworked(out GameObject _, out NetworkIdentity identity);
Vector3 position = new Vector3(10, 0, 20);
Quaternion rotation = Quaternion.Euler(0, 45, 0);
Vector3 scale = new Vector3(1.5f, 1.5f, 1.5f);
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = false,
isOwner = false,
sceneId = 0,
assetId = Guid.Empty,
// use local values for VR support
position = position,
rotation = rotation,
scale = scale,
payload = default,
};
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.That(identity.transform.position, Is.EqualTo(position));
// use angle because of floating point numbers
// only need to check if rotations are approximately equal
Assert.That(Quaternion.Angle(identity.transform.rotation, rotation), Is.LessThan(0.0001f));
Assert.That(identity.transform.localScale, Is.EqualTo(scale));
}
[Test]
public void ApplyPayload_AppliesLocalValuesToTransform()
{
const uint netId = 1000;
CreateGameObject(out GameObject parent);
parent.transform.position = new Vector3(100, 20, 0);
parent.transform.rotation = Quaternion.LookRotation(Vector3.left);
parent.transform.localScale = Vector3.one * 2;
CreateNetworked(out GameObject go, out NetworkIdentity identity);
go.transform.parent = parent.transform;
Vector3 position = new Vector3(10, 0, 20);
Quaternion rotation = Quaternion.Euler(0, 45, 0);
Vector3 scale = new Vector3(1.5f, 1.5f, 1.5f);
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = false,
isOwner = false,
sceneId = 0,
assetId = Guid.Empty,
// use local values for VR support
position = position,
rotation = rotation,
scale = scale,
payload = default,
};
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.That(identity.transform.localPosition, Is.EqualTo(position));
// use angle because of floating point numbers
// only need to check if rotations are approximately equal
Assert.That(Quaternion.Angle(identity.transform.localRotation, rotation), Is.LessThan(0.0001f));
Assert.That(identity.transform.localScale, Is.EqualTo(scale));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void ApplyPayload_AppliesAuthority(bool isOwner)
{
const uint netId = 1000;
CreateNetworked(out _, out NetworkIdentity identity);
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = false,
isOwner = isOwner,
sceneId = 0,
assetId = Guid.Empty,
// use local values for VR support
position = Vector3.zero,
rotation = Quaternion.identity,
scale = Vector3.one,
payload = default
};
// set to opposite to make sure it is changed
identity.hasAuthority = !isOwner;
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.That(identity.hasAuthority, Is.EqualTo(isOwner));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void ApplyPayload_EnablesObject(bool startActive)
{
const uint netId = 1000;
CreateNetworked(out GameObject go, out NetworkIdentity identity);
go.SetActive(startActive);
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = false,
isOwner = false,
sceneId = 0,
assetId = Guid.Empty,
// use local values for VR support
position = Vector3.zero,
rotation = Quaternion.identity,
scale = Vector3.one,
payload = default,
};
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.IsTrue(identity.gameObject.activeSelf);
}
[Test]
public void ApplyPayload_SetsAssetId()
{
const uint netId = 1000;
CreateNetworked(out _, out NetworkIdentity identity);
Guid guid = Guid.NewGuid();
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = false,
isOwner = false,
sceneId = 0,
assetId = guid,
// use local values for VR support
position = Vector3.zero,
rotation = Quaternion.identity,
scale = Vector3.one,
payload = default
};
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.IsTrue(identity.gameObject.activeSelf);
Assert.That(identity.assetId, Is.EqualTo(guid));
}
[Test]
public void ApplyPayload_DoesNotSetAssetIdToEmpty()
{
const uint netId = 1000;
CreateNetworked(out _, out NetworkIdentity identity);
Guid guid = Guid.NewGuid();
identity.assetId = guid;
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = false,
isOwner = false,
sceneId = 0,
assetId = Guid.Empty,
// use local values for VR support
position = Vector3.zero,
rotation = Quaternion.identity,
scale = Vector3.one,
payload = default
};
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.That(identity.assetId, Is.EqualTo(guid), "AssetId should not have changed");
}
[Test]
public void ApplyPayload_SendsDataToNetworkBehaviourDeserialize()
{
const int value = 12;
Vector3 direction = new Vector3(0, 1, 1);
const uint netId = 1000;
// server object
CreateNetworked(out _, out NetworkIdentity serverIdentity, out PayloadTestBehaviour serverPayloadBehaviour);
// client object
CreateNetworked(out _, out NetworkIdentity clientIdentity, out PayloadTestBehaviour clientPayloadBehaviour);
int onSerializeCalled = 0;
serverPayloadBehaviour.OnSerializeCalled += () => { onSerializeCalled++; };
int onDeserializeCalled = 0;
clientPayloadBehaviour.OnDeserializeCalled += () => { onDeserializeCalled++; };
serverPayloadBehaviour.value = value;
serverPayloadBehaviour.direction = direction;
NetworkWriter ownerWriter = new NetworkWriter();
NetworkWriter observersWriter = new NetworkWriter();
serverIdentity.OnSerializeAllSafely(true, ownerWriter, observersWriter);
// check that Serialize was called
Assert.That(onSerializeCalled, Is.EqualTo(1));
// create spawn message
SpawnMessage msg = new SpawnMessage
{
netId = netId,
payload = ownerWriter.ToArraySegment(),
};
// check values start default
Assert.That(onDeserializeCalled, Is.EqualTo(0));
Assert.That(clientPayloadBehaviour.value, Is.EqualTo(0));
Assert.That(clientPayloadBehaviour.direction, Is.EqualTo(Vector3.zero));
NetworkClient.ApplySpawnPayload(clientIdentity, msg);
// check values have been set by payload
Assert.That(onDeserializeCalled, Is.EqualTo(1));
Assert.That(clientPayloadBehaviour.value, Is.EqualTo(value));
Assert.That(clientPayloadBehaviour.direction, Is.EqualTo(direction));
}
[Test]
public void ApplyPayload_LocalPlayerAddsIdentityToConnection()
{
Debug.Assert(NetworkClient.localPlayer == null, "LocalPlayer should be null before this test");
const uint netId = 1000;
CreateNetworked(out _, out NetworkIdentity identity);
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = true,
isOwner = true,
sceneId = 0,
assetId = Guid.Empty,
// use local values for VR support
position = Vector3.zero,
rotation = Quaternion.identity,
scale = Vector3.one,
payload = default,
};
NetworkClient.connection = new FakeNetworkConnection();
NetworkClient.ready = true;
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.That(NetworkClient.localPlayer, Is.EqualTo(identity));
Assert.That(NetworkClient.connection.identity, Is.EqualTo(identity));
}
[Test]
public void ApplyPayload_LocalPlayerWarningWhenNoReadyConnection()
{
Debug.Assert(NetworkClient.localPlayer == null, "LocalPlayer should be null before this test");
const uint netId = 1000;
CreateNetworked(out _, out NetworkIdentity identity);
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = true,
isOwner = true,
sceneId = 0,
assetId = Guid.Empty,
// use local values for VR support
position = Vector3.zero,
rotation = Quaternion.identity,
scale = Vector3.one,
payload = default,
};
LogAssert.Expect(LogType.Warning, "No ready connection found for setting player controller during InternalAddPlayer");
NetworkClient.ApplySpawnPayload(identity, msg);
Assert.That(NetworkClient.localPlayer, Is.EqualTo(identity));
}
[Flags]
public enum SpawnFinishedState
{
isSpawnFinished = 1,
hasAuthority = 2,
isLocalPlayer = 4
}
[Test]
[TestCase(0)]
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(5)]
[TestCase(6)]
[TestCase(7)]
public void ApplyPayload_isSpawnFinished(SpawnFinishedState flag)
{
bool isSpawnFinished = flag.HasFlag(SpawnFinishedState.isSpawnFinished);
bool hasAuthority = flag.HasFlag(SpawnFinishedState.hasAuthority);
bool isLocalPlayer = flag.HasFlag(SpawnFinishedState.isLocalPlayer);
if (isSpawnFinished)
{
NetworkClient.OnObjectSpawnFinished(new ObjectSpawnFinishedMessage());
}
const uint netId = 1000;
CreateNetworked(out _, out NetworkIdentity identity, out BehaviourWithEvents events);
int onStartAuthorityCalled = 0;
int onStartClientCalled = 0;
int onStartLocalPlayerCalled = 0;
events.OnStartAuthorityCalled += () => { onStartAuthorityCalled++; };
events.OnStartClientCalled += () => { onStartClientCalled++; };
events.OnStartLocalPlayerCalled += () => { onStartLocalPlayerCalled++; };
SpawnMessage msg = new SpawnMessage
{
netId = netId,
isLocalPlayer = isLocalPlayer,
isOwner = hasAuthority,
};
NetworkClient.ApplySpawnPayload(identity, msg);
if (isSpawnFinished)
{
Assert.That(onStartClientCalled, Is.EqualTo(1));
Assert.That(onStartAuthorityCalled, Is.EqualTo(hasAuthority ? 1 : 0));
Assert.That(onStartLocalPlayerCalled, Is.EqualTo(isLocalPlayer ? 1 : 0));
}
else
{
Assert.That(onStartAuthorityCalled, Is.Zero);
Assert.That(onStartClientCalled, Is.Zero);
Assert.That(onStartLocalPlayerCalled, Is.Zero);
}
}
[Test]
public void OnSpawn_SpawnsAndAppliesPayload()
{
const int netId = 1;
Debug.Assert(spawned.Count == 0, "There should be no spawned objects before test");
Vector3 position = new Vector3(30, 20, 10);
Quaternion rotation = Quaternion.Euler(0, 0, 90);
SpawnMessage msg = new SpawnMessage
{
netId = netId,
assetId = validPrefabGuid,
position = position,
rotation = rotation
};
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
NetworkClient.OnSpawn(msg);
Assert.That(spawned.Count, Is.EqualTo(1));
Assert.IsTrue(spawned.ContainsKey(netId));
NetworkIdentity identity = spawned[netId];
Assert.IsNotNull(identity);
Assert.That(identity.name, Is.EqualTo($"{validPrefab.name}(Clone)"));
Assert.That(identity.transform.position, Is.EqualTo(position));
// use angle because of floating point numbers
// only need to check if rotations are approximately equal
Assert.That(Quaternion.Angle(identity.transform.rotation, rotation), Is.LessThan(0.0001f));
}
[Test]
public void OnSpawn_GiveNoExtraErrorsWhenPrefabIsntSpawned()
{
const int netId = 20033;
Debug.Assert(spawned.Count == 0, "There should be no spawned objects before test");
SpawnMessage msg = new SpawnMessage
{
netId = netId,
};
// Check for log that FindOrSpawnObject gives, and make sure there are no other error logs
LogAssert.Expect(LogType.Error, $"OnSpawn message with netId '{netId}' has no AssetId or sceneId");
NetworkClient.OnSpawn(msg);
Assert.That(spawned, Is.Empty);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b85d949dccc6ab4498da187264323dcc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,104 @@
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests.ClientSceneTests
{
public class ClientSceneTests_PrepareToSpawnSceneObjects : ClientSceneTestsBase
{
NetworkIdentity CreateSceneObject(ulong sceneId)
{
CreateNetworked(out GameObject gameObject, out NetworkIdentity identity);
gameObject.name = "Runtime GameObject";
// set sceneId to zero as it is set in onvalidate (does not set id at runtime)
identity.sceneId = sceneId;
return identity;
}
[Test]
public void AddsAllInactiveIdentitiesInSceneWithSceneIdToDictionary()
{
NetworkIdentity obj1 = CreateSceneObject(10);
NetworkIdentity obj2 = CreateSceneObject(11);
obj1.gameObject.SetActive(false);
obj2.gameObject.SetActive(false);
NetworkClient.PrepareToSpawnSceneObjects();
Assert.That(NetworkClient.spawnableObjects, Has.Count.EqualTo(2));
Assert.IsTrue(NetworkClient.spawnableObjects.ContainsValue(obj1));
Assert.IsTrue(NetworkClient.spawnableObjects.ContainsValue(obj2));
}
[Test]
public void DoesNotAddActiveObjectsToDictionary()
{
NetworkIdentity active = CreateSceneObject(30);
NetworkIdentity inactive = CreateSceneObject(32);
active.gameObject.SetActive(true);
inactive.gameObject.SetActive(false);
NetworkClient.PrepareToSpawnSceneObjects();
Assert.That(NetworkClient.spawnableObjects, Has.Count.EqualTo(1));
Assert.IsTrue(NetworkClient.spawnableObjects.ContainsValue(inactive));
Assert.IsFalse(NetworkClient.spawnableObjects.ContainsValue(active));
}
[Test]
public void DoesNotAddObjectsWithNoSceneId()
{
NetworkIdentity noId = CreateSceneObject(0);
NetworkIdentity hasId = CreateSceneObject(40);
noId.gameObject.SetActive(false);
hasId.gameObject.SetActive(false);
NetworkClient.PrepareToSpawnSceneObjects();
Assert.IsTrue(NetworkClient.spawnableObjects.ContainsValue(hasId));
Assert.IsFalse(NetworkClient.spawnableObjects.ContainsValue(noId));
}
[Test]
public void AddsIdentitiesToDictionaryUsingSceneId()
{
NetworkIdentity obj1 = CreateSceneObject(20);
NetworkIdentity obj2 = CreateSceneObject(21);
obj1.gameObject.SetActive(false);
obj2.gameObject.SetActive(false);
NetworkClient.PrepareToSpawnSceneObjects();
Assert.IsTrue(NetworkClient.spawnableObjects.ContainsKey(20));
Assert.That(NetworkClient.spawnableObjects[20], Is.EqualTo(obj1));
Assert.IsTrue(NetworkClient.spawnableObjects.ContainsKey(21));
Assert.That(NetworkClient.spawnableObjects[21], Is.EqualTo(obj2));
}
[Test]
public void ClearsExistingItemsFromDictionary()
{
// destroyed objects from old scene
NetworkClient.spawnableObjects.Add(60, null);
NetworkClient.spawnableObjects.Add(62, null);
// active object
NetworkIdentity obj1 = CreateSceneObject(61);
NetworkClient.spawnableObjects.Add(61, obj1);
// new disabled object
NetworkIdentity obj2 = CreateSceneObject(63);
obj2.gameObject.SetActive(false);
NetworkClient.PrepareToSpawnSceneObjects();
Assert.That(NetworkClient.spawnableObjects, Has.Count.EqualTo(1));
Assert.IsFalse(NetworkClient.spawnableObjects.ContainsValue(null));
Assert.IsTrue(NetworkClient.spawnableObjects.ContainsValue(obj2));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 546931b1b1c38d5498f2c189e68b63aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,300 @@
using System;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Mirror.Tests.ClientSceneTests
{
public class ClientSceneTests_RegisterPrefab : ClientSceneTests_RegisterPrefabBase
{
[Test]
[TestCase(RegisterPrefabOverload.Prefab)]
public void Prefab_AddsPrefabToDictionary(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
CallRegisterPrefab(validPrefab, overload);
Assert.IsTrue(NetworkClient.prefabs.ContainsKey(guid));
Assert.AreEqual(NetworkClient.prefabs[guid], validPrefab);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_NewAssetId)]
public void PrefabNewGuid_ErrorDoesNotChangePrefabsAssetId(RegisterPrefabOverload overload)
{
Guid guid = anotherGuid;
LogAssert.Expect(LogType.Error, $"Could not register '{validPrefab.name}' to {guid} because it already had an AssetId, Existing assetId {validPrefabGuid}");
CallRegisterPrefab(validPrefab, overload);
Assert.IsFalse(NetworkClient.prefabs.ContainsKey(guid));
NetworkIdentity netId = validPrefab.GetComponent<NetworkIdentity>();
Assert.AreEqual(netId.assetId, validPrefabGuid);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId)]
public void HandlerNewGuid_ErrorDoesNotChangePrefabsAssetId(RegisterPrefabOverload overload)
{
Guid guid = anotherGuid;
LogAssert.Expect(LogType.Error, $"Could not register Handler for '{validPrefab.name}' to {guid} because it already had an AssetId, Existing assetId {validPrefabGuid}");
CallRegisterPrefab(validPrefab, overload);
Assert.IsFalse(NetworkClient.spawnHandlers.ContainsKey(guid));
Assert.IsFalse(NetworkClient.unspawnHandlers.ContainsKey(guid));
NetworkIdentity netId = validPrefab.GetComponent<NetworkIdentity>();
Assert.AreEqual(netId.assetId, validPrefabGuid);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_NewAssetId)]
public void PrefabNewGuid_NoErrorWhenNewAssetIdIsSameAsCurrentPrefab(RegisterPrefabOverload overload)
{
Guid guid = validPrefabGuid;
CallRegisterPrefab(validPrefab, overload, guid);
Assert.IsTrue(NetworkClient.prefabs.ContainsKey(guid));
NetworkIdentity netId = validPrefab.GetComponent<NetworkIdentity>();
Assert.AreEqual(netId.assetId, validPrefabGuid);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId)]
public void HandlerNewGuid_NoErrorWhenAssetIdIsSameAsCurrentPrefab(RegisterPrefabOverload overload)
{
Guid guid = validPrefabGuid;
CallRegisterPrefab(validPrefab, overload, guid);
Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(guid));
Assert.IsTrue(NetworkClient.unspawnHandlers.ContainsKey(guid));
NetworkIdentity netId = validPrefab.GetComponent<NetworkIdentity>();
Assert.AreEqual(netId.assetId, validPrefabGuid);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab)]
[TestCase(RegisterPrefabOverload.Prefab_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId)]
public void ErrorForNullPrefab(RegisterPrefabOverload overload)
{
string msg = OverloadWithHandler(overload)
? "Could not register handler for prefab because the prefab was null"
: "Could not register prefab because it was null";
LogAssert.Expect(LogType.Error, msg);
CallRegisterPrefab(null, overload);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab)]
[TestCase(RegisterPrefabOverload.Prefab_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId)]
public void ErrorForPrefabWithoutNetworkIdentity(RegisterPrefabOverload overload)
{
string msg = OverloadWithHandler(overload)
? $"Could not register handler for '{invalidPrefab.name}' since it contains no NetworkIdentity component"
: $"Could not register '{invalidPrefab.name}' since it contains no NetworkIdentity component";
LogAssert.Expect(LogType.Error, msg);
CallRegisterPrefab(invalidPrefab, overload);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId)]
public void NewGuid_ErrorForEmptyGuid(RegisterPrefabOverload overload)
{
string msg = OverloadWithHandler(overload)
? $"Could not register handler for '{validPrefab.name}' with new assetId because the new assetId was empty"
: $"Could not register '{validPrefab.name}' with new assetId because the new assetId was empty";
LogAssert.Expect(LogType.Error, msg);
CallRegisterPrefab(validPrefab, overload, new Guid());
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
public void ErrorIfPrefabHadSceneId(RegisterPrefabOverload overload)
{
GameObject clone = GameObject.Instantiate(validPrefab);
NetworkIdentity netId = clone.GetComponent<NetworkIdentity>();
// Scene Id needs to not be zero for this test
netId.sceneId = 20;
LogAssert.Expect(LogType.Error, $"Can not Register '{clone.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene.");
CallRegisterPrefab(clone, overload);
GameObject.DestroyImmediate(clone);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
public void ErrorForNetworkIdentityInChildren(RegisterPrefabOverload overload)
{
LogAssert.Expect(LogType.Error, $"Prefab '{prefabWithChildren.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object.");
CallRegisterPrefab(prefabWithChildren, overload);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab)]
public void Prefab_WarningForAssetIdAlreadyExistingInPrefabsDictionary(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
NetworkClient.prefabs.Add(guid, validPrefab);
LogAssert.Expect(LogType.Warning, $"Replacing existing prefab with assetId '{guid}'. Old prefab '{validPrefab.name}', New prefab '{validPrefab.name}'");
CallRegisterPrefab(validPrefab, overload);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
public void Handler_ErrorForAssetIdAlreadyExistingInPrefabsDictionary(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
NetworkClient.prefabs.Add(guid, validPrefab);
LogAssert.Expect(LogType.Error, $"assetId '{guid}' is already used by prefab '{validPrefab.name}', unregister the prefab first before trying to add handler");
CallRegisterPrefab(validPrefab, overload);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
public void WarningForAssetIdAlreadyExistingInHandlersDictionary(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
NetworkClient.spawnHandlers.Add(guid, x => null);
NetworkClient.unspawnHandlers.Add(guid, x => {});
string msg = OverloadWithHandler(overload)
? $"Replacing existing spawnHandlers for prefab '{validPrefab.name}' with assetId '{guid}'"
: $"Adding prefab '{validPrefab.name}' with assetId '{guid}' when spawnHandlers with same assetId already exists.";
LogAssert.Expect(LogType.Warning, msg);
CallRegisterPrefab(validPrefab, overload);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
public void SpawnDelegate_AddsHandlerToSpawnHandlers(RegisterPrefabOverload overload)
{
int handlerCalled = 0;
Guid guid = GuidForOverload(overload);
SpawnDelegate handler = new SpawnDelegate((pos, rot) =>
{
handlerCalled++;
return null;
});
CallRegisterPrefab(validPrefab, overload, handler);
Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(guid));
// check spawnHandler above is called
SpawnHandlerDelegate handlerInDictionary = NetworkClient.spawnHandlers[guid];
handlerInDictionary.Invoke(default);
Assert.That(handlerCalled, Is.EqualTo(1));
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
public void SpawnDelegate_AddsHandlerToSpawnHandlersWithCorrectArguments(RegisterPrefabOverload overload)
{
int handlerCalled = 0;
Vector3 somePosition = new Vector3(10, 20, 3);
Guid guid = GuidForOverload(overload);
SpawnDelegate handler = new SpawnDelegate((pos, assetId) =>
{
handlerCalled++;
Assert.That(pos, Is.EqualTo(somePosition));
Assert.That(assetId, Is.EqualTo(guid));
return null;
});
CallRegisterPrefab(validPrefab, overload, handler);
Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(guid));
// check spawnHandler above is called
SpawnHandlerDelegate handlerInDictionary = NetworkClient.spawnHandlers[guid];
handlerInDictionary.Invoke(new SpawnMessage { position = somePosition, assetId = guid });
Assert.That(handlerCalled, Is.EqualTo(1));
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)]
public void SpawnDelegate_ErrorWhenSpawnHandlerIsNull(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
LogAssert.Expect(LogType.Error, $"Can not Register null SpawnHandler for {guid}");
CallRegisterPrefab(validPrefab, overload, spawnHandler: null);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
public void SpawnHandleDelegate_AddsHandlerToSpawnHandlers(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
SpawnHandlerDelegate handler = new SpawnHandlerDelegate(x => null);
CallRegisterPrefab(validPrefab, overload, handler);
Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(guid));
Assert.AreEqual(NetworkClient.spawnHandlers[guid], handler);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
public void SpawnHandleDelegate_ErrorWhenSpawnHandlerIsNull(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
LogAssert.Expect(LogType.Error, $"Can not Register null SpawnHandler for {guid}");
CallRegisterPrefab(validPrefab, overload, spawnHandlerDelegate: null);
}
[Test]
[TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)]
[TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)]
public void Handler_ErrorWhenUnSpawnHandlerIsNull(RegisterPrefabOverload overload)
{
Guid guid = GuidForOverload(overload);
LogAssert.Expect(LogType.Error, $"Can not Register null UnSpawnHandler for {guid}");
CallRegisterPrefab(validPrefab, overload, unspawnHandler: null);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bdfdcfafb85b3be418f2085e38663006
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,224 @@
using System;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Mirror.Tests.ClientSceneTests
{
public class ClientSceneTests_RegisterSpawnHandler : ClientSceneTestsBase
{
[Test]
public void SpawnDelegate_AddsHandlerToSpawnHandlers()
{
int handlerCalled = 0;
Guid guid = Guid.NewGuid();
SpawnDelegate spawnHandler = new SpawnDelegate((pos, rot) =>
{
handlerCalled++;
return null;
});
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(guid));
// check spawnHandler above is called
SpawnHandlerDelegate handler = NetworkClient.spawnHandlers[guid];
handler.Invoke(default);
Assert.That(handlerCalled, Is.EqualTo(1));
}
[Test]
public void SpawnDelegate_AddsHandlerToSpawnHandlersWithCorrectArguments()
{
int handlerCalled = 0;
Vector3 somePosition = new Vector3(10, 20, 3);
Guid guid = Guid.NewGuid();
SpawnDelegate spawnHandler = new SpawnDelegate((pos, assetId) =>
{
handlerCalled++;
Assert.That(pos, Is.EqualTo(somePosition));
Assert.That(assetId, Is.EqualTo(guid));
return null;
});
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(guid));
// check spawnHandler above is called
SpawnHandlerDelegate handler = NetworkClient.spawnHandlers[guid];
handler.Invoke(new SpawnMessage { position = somePosition, assetId = guid });
Assert.That(handlerCalled, Is.EqualTo(1));
}
[Test]
public void SpawnDelegate_AddsHandlerToUnSpawnHandlers()
{
Guid guid = Guid.NewGuid();
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
Assert.IsTrue(NetworkClient.unspawnHandlers.ContainsKey(guid));
Assert.AreEqual(NetworkClient.unspawnHandlers[guid], unspawnHandler);
}
[Test]
public void SpawnDelegate_ErrorWhenSpawnHandlerIsNull()
{
Guid guid = Guid.NewGuid();
SpawnDelegate spawnHandler = null;
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
LogAssert.Expect(LogType.Error, $"Can not Register null SpawnHandler for {guid}");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
[Test]
public void SpawnDelegate_ErrorWhenUnSpawnHandlerIsNull()
{
Guid guid = Guid.NewGuid();
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
UnSpawnDelegate unspawnHandler = null;
LogAssert.Expect(LogType.Error, $"Can not Register null UnSpawnHandler for {guid}");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
[Test]
public void SpawnDelegate_ErrorWhenAssetIdIsEmpty()
{
Guid guid = new Guid();
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
LogAssert.Expect(LogType.Error, "Can not Register SpawnHandler for empty Guid");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
[Test]
public void SpawnDelegate_WarningWhenHandlerForGuidAlreadyExistsInHandlerDictionary()
{
Guid guid = Guid.NewGuid();
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
SpawnDelegate spawnHandler2 = new SpawnDelegate((x, y) => new GameObject());
UnSpawnDelegate unspawnHandler2 = new UnSpawnDelegate(x => UnityEngine.Object.Destroy(x));
LogAssert.Expect(LogType.Warning, $"Replacing existing spawnHandlers for {guid}");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler2, unspawnHandler2);
}
[Test]
public void SpawnDelegate_ErrorWhenHandlerForGuidAlreadyExistsInPrefabDictionary()
{
Guid guid = Guid.NewGuid();
NetworkClient.prefabs.Add(guid, validPrefab);
SpawnDelegate spawnHandler = new SpawnDelegate((x, y) => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
LogAssert.Expect(LogType.Error, $"assetId '{guid}' is already used by prefab '{validPrefab.name}'");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
[Test]
public void SpawnHandlerDelegate_AddsHandlerToSpawnHandlers()
{
Guid guid = Guid.NewGuid();
SpawnHandlerDelegate spawnHandler = new SpawnHandlerDelegate(x => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(guid));
Assert.AreEqual(NetworkClient.spawnHandlers[guid], spawnHandler);
}
[Test]
public void SpawnHandlerDelegate_AddsHandlerToUnSpawnHandlers()
{
Guid guid = Guid.NewGuid();
SpawnHandlerDelegate spawnHandler = new SpawnHandlerDelegate(x => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
Assert.IsTrue(NetworkClient.unspawnHandlers.ContainsKey(guid));
Assert.AreEqual(NetworkClient.unspawnHandlers[guid], unspawnHandler);
}
[Test]
public void SpawnHandlerDelegate_ErrorWhenSpawnHandlerIsNull()
{
Guid guid = Guid.NewGuid();
SpawnHandlerDelegate spawnHandler = null;
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
LogAssert.Expect(LogType.Error, $"Can not Register null SpawnHandler for {guid}");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
[Test]
public void SpawnHandlerDelegate_ErrorWhenUnSpawnHandlerIsNull()
{
Guid guid = Guid.NewGuid();
SpawnHandlerDelegate spawnHandler = new SpawnHandlerDelegate(x => null);
UnSpawnDelegate unspawnHandler = null;
LogAssert.Expect(LogType.Error, $"Can not Register null UnSpawnHandler for {guid}");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
[Test]
public void SpawnHandlerDelegate_ErrorWhenAssetIdIsEmpty()
{
Guid guid = new Guid();
SpawnHandlerDelegate spawnHandler = new SpawnHandlerDelegate(x => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
LogAssert.Expect(LogType.Error, "Can not Register SpawnHandler for empty Guid");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
[Test]
public void SpawnHandlerDelegate_WarningWhenHandlerForGuidAlreadyExistsInHandlerDictionary()
{
Guid guid = Guid.NewGuid();
SpawnHandlerDelegate spawnHandler = new SpawnHandlerDelegate(x => null);
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => {});
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
SpawnHandlerDelegate spawnHandler2 = new SpawnHandlerDelegate(x => new GameObject());
UnSpawnDelegate unspawnHandler2 = new UnSpawnDelegate(x => UnityEngine.Object.Destroy(x));
LogAssert.Expect(LogType.Warning, $"Replacing existing spawnHandlers for {guid}");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler2, unspawnHandler2);
}
[Test]
public void SpawnHandlerDelegate_ErrorWhenHandlerForGuidAlreadyExistsInPrefabDictionary()
{
Guid guid = Guid.NewGuid();
NetworkClient.prefabs.Add(guid, validPrefab);
SpawnHandlerDelegate spawnHandler = new SpawnHandlerDelegate(x => new GameObject());
UnSpawnDelegate unspawnHandler = new UnSpawnDelegate(x => UnityEngine.Object.Destroy(x));
LogAssert.Expect(LogType.Error, $"assetId '{guid}' is already used by prefab '{validPrefab.name}'");
NetworkClient.RegisterSpawnHandler(guid, spawnHandler, unspawnHandler);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7e6577e8c2e64e41ae255edc61e91a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,48 @@
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Mirror.Tests.ClientSceneTests
{
public class ClientSceneTests_UnregisterPrefab : ClientSceneTestsBase
{
[Test]
public void RemovesPrefabFromDictionary()
{
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
NetworkClient.UnregisterPrefab(validPrefab);
Assert.IsFalse(NetworkClient.prefabs.ContainsKey(validPrefabGuid));
}
[Test]
public void RemovesSpawnHandlerFromDictionary()
{
NetworkClient.spawnHandlers.Add(validPrefabGuid, new SpawnHandlerDelegate(x => null));
NetworkClient.UnregisterPrefab(validPrefab);
Assert.IsFalse(NetworkClient.spawnHandlers.ContainsKey(validPrefabGuid));
}
[Test]
public void RemovesUnSpawnHandlerFromDictionary()
{
NetworkClient.unspawnHandlers.Add(validPrefabGuid, new UnSpawnDelegate(x => {}));
NetworkClient.UnregisterPrefab(validPrefab);
Assert.IsFalse(NetworkClient.unspawnHandlers.ContainsKey(validPrefabGuid));
}
[Test]
public void ErrorWhenPrefabIsNull()
{
LogAssert.Expect(LogType.Error, "Could not unregister prefab because it was null");
NetworkClient.UnregisterPrefab(null);
}
[Test]
public void ErrorWhenPrefabHasNoNetworkIdentity()
{
LogAssert.Expect(LogType.Error, $"Could not unregister '{invalidPrefab.name}' since it contains no NetworkIdentity component");
NetworkClient.UnregisterPrefab(invalidPrefab);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aa6fa692d80eaf8419e559c35034f016
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
using NUnit.Framework;
namespace Mirror.Tests.ClientSceneTests
{
public class ClientSceneTests_UnregisterSpawnHandler : ClientSceneTestsBase
{
[Test]
public void RemovesSpawnHandlersFromDictionary()
{
NetworkClient.spawnHandlers.Add(validPrefabGuid, new SpawnHandlerDelegate(x => null));
NetworkClient.UnregisterSpawnHandler(validPrefabGuid);
Assert.IsFalse(NetworkClient.unspawnHandlers.ContainsKey(validPrefabGuid));
}
[Test]
public void RemovesUnSpawnHandlersFromDictionary()
{
NetworkClient.unspawnHandlers.Add(validPrefabGuid, new UnSpawnDelegate(x => {}));
NetworkClient.UnregisterSpawnHandler(validPrefabGuid);
Assert.IsFalse(NetworkClient.unspawnHandlers.ContainsKey(validPrefabGuid));
}
[Test]
public void DoesNotRemovePrefabDictionary()
{
NetworkClient.prefabs.Add(validPrefabGuid, validPrefab);
NetworkClient.UnregisterSpawnHandler(validPrefabGuid);
// Should not be removed
Assert.IsTrue(NetworkClient.prefabs.ContainsKey(validPrefabGuid));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49ef76b8883ba3845942503683c9a9b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,182 @@
using System;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests.RemoteAttrributeTest
{
class VirtualCommand : NetworkBehaviour
{
public event Action<int> onVirtualSendInt;
[Command]
public virtual void CmdSendInt(int someInt) =>
onVirtualSendInt?.Invoke(someInt);
}
class VirtualNoOverrideCommand : VirtualCommand {}
class VirtualOverrideCommand : VirtualCommand
{
public event Action<int> onOverrideSendInt;
[Command]
public override void CmdSendInt(int someInt) =>
onOverrideSendInt?.Invoke(someInt);
}
class VirtualOverrideCommandWithBase : VirtualCommand
{
public event Action<int> onOverrideSendInt;
[Command]
public override void CmdSendInt(int someInt)
{
base.CmdSendInt(someInt);
onOverrideSendInt?.Invoke(someInt);
}
}
// test for 2 overrides
class VirtualOverrideCommandWithBase2 : VirtualOverrideCommandWithBase
{
public event Action<int> onOverrideSendInt2;
[Command]
public override void CmdSendInt(int someInt)
{
base.CmdSendInt(someInt);
onOverrideSendInt2?.Invoke(someInt);
}
}
public class CommandOverrideTest : RemoteTestBase
{
[Test]
public void VirtualCommandIsCalled()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualCommand hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(1));
}
[Test]
public void VirtualCommandWithNoOverrideIsCalled()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualNoOverrideCommand hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(1));
}
[Test]
public void OverrideVirtualCommandIsCalled()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualOverrideCommand hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
int overrideCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
};
hostBehaviour.onOverrideSendInt += incomingInt =>
{
overrideCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(0));
Assert.That(overrideCallCount, Is.EqualTo(1));
}
[Test]
public void OverrideVirtualWithBaseCallsBothVirtualAndBase()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualOverrideCommandWithBase hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
int overrideCallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.onOverrideSendInt += incomingInt =>
{
overrideCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(1));
Assert.That(overrideCallCount, Is.EqualTo(1));
}
[Test]
public void OverrideVirtualWithBaseCallsAllMethodsThatCallBase()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualOverrideCommandWithBase2 hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int virtualCallCount = 0;
int overrideCallCount = 0;
int override2CallCount = 0;
hostBehaviour.onVirtualSendInt += incomingInt =>
{
virtualCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.onOverrideSendInt += incomingInt =>
{
overrideCallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.onOverrideSendInt2 += incomingInt =>
{
override2CallCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(virtualCallCount, Is.EqualTo(1));
Assert.That(overrideCallCount, Is.EqualTo(1));
Assert.That(override2CallCount, Is.EqualTo(1));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 881c801c26e90df43b3558a23c96e0ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,247 @@
using System;
using System.Text.RegularExpressions;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Mirror.Tests.RemoteAttrributeTest
{
class AuthorityBehaviour : NetworkBehaviour
{
public event Action<int> onSendInt;
[Command]
public void SendInt(int someInt) =>
onSendInt?.Invoke(someInt);
}
class IgnoreAuthorityBehaviour : NetworkBehaviour
{
public event Action<int> onSendInt;
[Command(requiresAuthority = false)]
public void CmdSendInt(int someInt) =>
onSendInt?.Invoke(someInt);
}
class SenderConnectionBehaviour : NetworkBehaviour
{
public event Action<int, NetworkConnection> onSendInt;
[Command]
public void CmdSendInt(int someInt, NetworkConnectionToClient conn = null) =>
onSendInt?.Invoke(someInt, conn);
}
class SenderConnectionIgnoreAuthorityBehaviour : NetworkBehaviour
{
public event Action<int, NetworkConnection> onSendInt;
[Command(requiresAuthority = false)]
public void CmdSendInt(int someInt, NetworkConnectionToClient conn = null) =>
onSendInt?.Invoke(someInt, conn);
}
class ThrowBehaviour : NetworkBehaviour
{
public const string ErrorMessage = "Bad things happened";
[Command]
public void SendThrow(int _) => throw new Exception(ErrorMessage);
}
class CommandOverloads : NetworkBehaviour
{
public int firstCalled = 0;
public int secondCalled = 0;
[Command]
public void TheCommand(int _) => ++firstCalled;
[Command]
public void TheCommand(string _) => ++secondCalled;
}
public class CommandTest : RemoteTestBase
{
[Test]
public void CommandIsSentWithAuthority()
{
// spawn with owner
CreateNetworkedAndSpawn(out _, out _, out AuthorityBehaviour hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int callCount = 0;
hostBehaviour.onSendInt += incomingInt =>
{
callCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.SendInt(someInt);
ProcessMessages();
Assert.That(callCount, Is.EqualTo(1));
}
[Test]
public void WarningForCommandSentWithoutAuthority()
{
// spawn without owner
CreateNetworkedAndSpawn(out _, out _, out AuthorityBehaviour hostBehaviour);
const int someInt = 20;
int callCount = 0;
hostBehaviour.onSendInt += incomingInt =>
{
callCount++;
};
LogAssert.Expect(LogType.Warning, $"Trying to send command for object without authority. System.Void Mirror.Tests.RemoteAttrributeTest.AuthorityBehaviour::SendInt(System.Int32)");
hostBehaviour.SendInt(someInt);
ProcessMessages();
Assert.That(callCount, Is.Zero);
}
[Test]
public void CommandIsSentWithAuthorityWhenIgnoringAuthority()
{
// spawn with owner
CreateNetworkedAndSpawn(out _, out _, out IgnoreAuthorityBehaviour hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
int callCount = 0;
hostBehaviour.onSendInt += incomingInt =>
{
callCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(callCount, Is.EqualTo(1));
}
[Test]
public void CommandIsSentWithoutAuthorityWhenIgnoringAuthority()
{
// spawn without owner
CreateNetworkedAndSpawn(out _, out _, out IgnoreAuthorityBehaviour hostBehaviour);
const int someInt = 20;
int callCount = 0;
hostBehaviour.onSendInt += incomingInt =>
{
callCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(callCount, Is.EqualTo(1));
}
// test to prevent https://github.com/vis2k/Mirror/issues/2629
// from happening again in the future
// -> [Command]s can be called on other objects with requiresAuthority=false.
// -> those objects don't have a .connectionToServer
// -> we broke it when using .connectionToServer instead of
// NetworkClient.connection in SendCommandInternal.
[Test]
public void Command_RequiresAuthorityFalse_ForOtherObjectWithoutConnectionToServer()
{
// spawn without owner (= without connectionToClient)
CreateNetworkedAndSpawn(out _, out _, out IgnoreAuthorityBehaviour comp);
// setup callback
int called = 0;
comp.onSendInt += _ => { ++called; };
// call command. don't require authority.
// the object doesn't have a .connectionToServer (like a scene object)
Assert.That(comp.connectionToServer, Is.Null);
comp.CmdSendInt(0);
Assert.That(called, Is.EqualTo(1));
}
[Test]
public void SenderConnectionIsSetWhenCommandIsRecieved()
{
// spawn with owner
CreateNetworkedAndSpawn(out _, out _, out SenderConnectionBehaviour hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
NetworkConnectionToClient connectionToClient = NetworkServer.connections[0];
Debug.Assert(connectionToClient != null, $"connectionToClient was null, This means that the test is broken and will give the wrong results");
int callCount = 0;
hostBehaviour.onSendInt += (incomingInt, incomingConn) =>
{
callCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
Assert.That(incomingConn, Is.EqualTo(connectionToClient));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(callCount, Is.EqualTo(1));
}
[Test]
public void SenderConnectionIsSetWhenCommandIsRecievedWithIgnoreAuthority()
{
// spawn without owner
CreateNetworkedAndSpawn(out _, out _, out SenderConnectionIgnoreAuthorityBehaviour hostBehaviour);
const int someInt = 20;
NetworkConnectionToClient connectionToClient = NetworkServer.connections[0];
Debug.Assert(connectionToClient != null, $"connectionToClient was null, This means that the test is broken and will give the wrong results");
int callCount = 0;
hostBehaviour.onSendInt += (incomingInt, incomingConn) =>
{
callCount++;
Assert.That(incomingInt, Is.EqualTo(someInt));
Assert.That(incomingConn, Is.EqualTo(connectionToClient));
};
hostBehaviour.CmdSendInt(someInt);
ProcessMessages();
Assert.That(callCount, Is.EqualTo(1));
}
[Test]
public void CommandThatThrowsShouldBeCaught()
{
// spawn with owner
CreateNetworkedAndSpawn(out _, out _, out ThrowBehaviour hostBehaviour, NetworkServer.localConnection);
const int someInt = 20;
NetworkConnectionToClient connectionToClient = NetworkServer.connections[0];
Debug.Assert(connectionToClient != null, $"connectionToClient was null, This means that the test is broken and will give the wrong results");
LogAssert.Expect(LogType.Error, new Regex($".*{ThrowBehaviour.ErrorMessage}.*"));
Assert.DoesNotThrow(() =>
{
hostBehaviour.SendThrow(someInt);
ProcessMessages();
}, "Processing new message should not throw, the exception from SendThrow should be caught");
}
// RemoteCalls uses md.FullName which gives us the full command/rpc name
// like "System.Void Mirror.Tests.RemoteAttrributeTest.AuthorityBehaviour::SendInt(System.Int32)"
// which means overloads with same name but different types should work.
[Test]
public void CommandOverloads()
{
// spawn with owner
CreateNetworkedAndSpawn(out _, out _, out CommandOverloads comp, NetworkServer.localConnection);
// call both overloads once
comp.TheCommand(42);
comp.TheCommand("A");
ProcessMessages();
Assert.That(comp.firstCalled, Is.EqualTo(1));
Assert.That(comp.secondCalled, Is.EqualTo(1));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1dd3f8a95eee6f74997bc8abcd43a401
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,257 @@
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
public class CompressionTests
{
[Test]
public void LargestAbsoluteComponentIndex()
{
// positive value & xyw smallest
Vector4 value = new Vector4(1, 3, 4, 2);
int index = Compression.LargestAbsoluteComponentIndex(value, out float largest, out Vector3 withoutLargest);
Assert.That(index, Is.EqualTo(2));
Assert.That(largest, Is.EqualTo(Mathf.Abs(value.z)));
Assert.That(withoutLargest, Is.EqualTo(new Vector3(value.x, value.y, value.w)));
// negative value should use abs & xzw smallest
value = new Vector4(1, -5, 4, 0);
index = Compression.LargestAbsoluteComponentIndex(value, out largest, out withoutLargest);
Assert.That(index, Is.EqualTo(1));
Assert.That(largest, Is.EqualTo(Mathf.Abs(value.y)));
Assert.That(withoutLargest, Is.EqualTo(new Vector3(value.x, value.z, value.w)));
// positive value & yzw smallest
value = new Vector4(5, 2, 3, 4);
index = Compression.LargestAbsoluteComponentIndex(value, out largest, out withoutLargest);
Assert.That(index, Is.EqualTo(0));
Assert.That(largest, Is.EqualTo(Mathf.Abs(value.x)));
Assert.That(withoutLargest, Is.EqualTo(new Vector3(value.y, value.z, value.w)));
// test to guarantee it uses 'abs' for first value
// to reproduce https://github.com/vis2k/Mirror/issues/2674
// IF all values are properly 'abs', THEN first one should be largest
value = new Vector4(-3, 0, 1, 2);
index = Compression.LargestAbsoluteComponentIndex(value, out largest, out withoutLargest);
Assert.That(index, Is.EqualTo(0));
Assert.That(largest, Is.EqualTo(Mathf.Abs(value.x)));
Assert.That(withoutLargest, Is.EqualTo(new Vector3(value.y, value.z, value.w)));
}
[Test, Ignore("Enable when needed.")]
public void LargestAbsoluteComponentIndexBenchmark()
{
Vector4 value = new Vector4(1, 2, 3, 4);
for (int i = 0; i < 100000; ++i)
Compression.LargestAbsoluteComponentIndex(value, out float _, out Vector3 _);
}
[Test]
public void ScaleFloatToUShort()
{
Assert.That(Compression.ScaleFloatToUShort(-1f, -1f, 1f, ushort.MinValue, ushort.MaxValue), Is.EqualTo(0));
Assert.That(Compression.ScaleFloatToUShort(0f, -1f, 1f, ushort.MinValue, ushort.MaxValue), Is.EqualTo(32767));
Assert.That(Compression.ScaleFloatToUShort(0.5f, -1f, 1f, ushort.MinValue, ushort.MaxValue), Is.EqualTo(49151));
Assert.That(Compression.ScaleFloatToUShort(1f, -1f, 1f, ushort.MinValue, ushort.MaxValue), Is.EqualTo(65535));
}
[Test]
public void ScaleUShortToFloat()
{
Assert.That(Compression.ScaleUShortToFloat(0, ushort.MinValue, ushort.MaxValue, -1, 1), Is.EqualTo(-1).Within(0.0001f));
Assert.That(Compression.ScaleUShortToFloat(32767, ushort.MinValue, ushort.MaxValue, -1, 1), Is.EqualTo(-0f).Within(0.0001f));
Assert.That(Compression.ScaleUShortToFloat(49151, ushort.MinValue, ushort.MaxValue, -1, 1), Is.EqualTo(0.5f).Within(0.0001f));
Assert.That(Compression.ScaleUShortToFloat(65535, ushort.MinValue, ushort.MaxValue, -1, 1), Is.EqualTo(1).Within(0.0001f));
}
[Test]
public void CompressAndDecompressQuaternion()
{
// we need a normalized value
Quaternion value = new Quaternion(1, 3, 4, 2).normalized;
// compress
uint data = Compression.CompressQuaternion(value);
Assert.That(data, Is.EqualTo(0xA83E2F07));
// decompress
Quaternion decompressed = Compression.DecompressQuaternion(data);
Assert.That(decompressed.x, Is.EqualTo(value.x).Within(0.005f));
Assert.That(decompressed.y, Is.EqualTo(value.y).Within(0.005f));
Assert.That(decompressed.z, Is.EqualTo(value.z).Within(0.005f));
Assert.That(decompressed.w, Is.EqualTo(value.w).Within(0.005f));
}
// iterate all [0..360] euler angles for x, y, z
// to make sure it all works and we missed nothing.
[Test]
public void CompressAndDecompressQuaternion_Iterate_0_to_360()
{
// stepSize 1: 360 * 360 * 360 = 46 million [takes 96 s]
// stepSize 5: 72 * 72 * 72 = 373 thousand [takes 700 ms]
// stepSize 10: 36 * 36 * 36 = 46 thousand [takes 100 ms]
//
// => 10 is enough. 700ms accumulates in hours of time waited over
// the years..
const int stepSize = 10;
for (int x = 0; x <= 360; x += stepSize)
{
for (int y = 0; y <= 360; y += stepSize)
{
for (int z = 0; z <= 360; z += stepSize)
{
// we need a normalized value
Quaternion value = Quaternion.Euler(x, y, z).normalized;
// compress
uint data = Compression.CompressQuaternion(value);
// decompress
Quaternion decompressed = Compression.DecompressQuaternion(data);
// compare them. Quaternion.Angle is easiest to get the angle
// between them. using .eulerAngles would give 0, 90, 360 which is
// hard to compare.
float angle = Quaternion.Angle(value, decompressed);
// 1 degree tolerance
Assert.That(Mathf.Abs(angle), Is.LessThanOrEqualTo(1));
}
}
}
}
// someone mentioned issues with 90 degree euler becoming -90 degree
[Test]
public void CompressAndDecompressQuaternion_90DegreeEuler()
{
// we need a normalized value
Quaternion value = Quaternion.Euler(0, 90, 0).normalized;
// compress
uint data = Compression.CompressQuaternion(value);
// decompress
Quaternion decompressed = Compression.DecompressQuaternion(data);
// compare them. Quaternion.Angle is easiest to get the angle
// between them. using .eulerAngles would give 0, 90, 360 which is
// hard to compare.
Debug.Log($"euler={decompressed.eulerAngles}");
float angle = Quaternion.Angle(value, decompressed);
// 1 degree tolerance
Assert.That(Mathf.Abs(angle), Is.LessThanOrEqualTo(1));
}
// test for issue https://github.com/vis2k/Mirror/issues/2674
[Test]
public void CompressAndDecompressQuaternion_2674()
{
// we need a normalized value
Quaternion value = Quaternion.Euler(338.850037f, 170.609955f, 182.979996f).normalized;
Debug.Log($"original={value.eulerAngles}");
// compress
uint data = Compression.CompressQuaternion(value);
// decompress
Quaternion decompressed = Compression.DecompressQuaternion(data);
// compare them. Quaternion.Angle is easiest to get the angle
// between them. using .eulerAngles would give 0, 90, 360 which is
// hard to compare.
// (51.6, 355.5, 348.1)
Debug.Log($"euler={decompressed.eulerAngles}");
float angle = Quaternion.Angle(value, decompressed);
// 1 degree tolerance
Assert.That(Mathf.Abs(angle), Is.LessThanOrEqualTo(1));
}
// client sending invalid data should still produce valid quaternions to
// avoid any possible bugs on server
[Test]
public void DecompressQuaternionInvalidData()
{
// decompress
// 0xFFFFFFFF will decompress to (0.7, 0.7, 0.7, NaN)
Quaternion decompressed = Compression.DecompressQuaternion(0xFFFFFFFF);
Assert.That(decompressed, Is.EqualTo(Quaternion.identity));
}
[Test]
public void VarInt()
{
NetworkWriter writer = new NetworkWriter();
Compression.CompressVarUInt(writer, 0);
Compression.CompressVarUInt(writer, 234);
Compression.CompressVarUInt(writer, 2284);
Compression.CompressVarUInt(writer, 67821);
Compression.CompressVarUInt(writer, 16777210);
Compression.CompressVarUInt(writer, 16777219);
Compression.CompressVarUInt(writer, 4294967295);
Compression.CompressVarUInt(writer, 1099511627775);
Compression.CompressVarUInt(writer, 281474976710655);
Compression.CompressVarUInt(writer, 72057594037927935);
Compression.CompressVarUInt(writer, ulong.MaxValue);
Compression.CompressVarInt(writer, long.MinValue);
Compression.CompressVarInt(writer, -72057594037927935);
Compression.CompressVarInt(writer, -281474976710655);
Compression.CompressVarInt(writer, -1099511627775);
Compression.CompressVarInt(writer, -4294967295);
Compression.CompressVarInt(writer, -16777219);
Compression.CompressVarInt(writer, -16777210);
Compression.CompressVarInt(writer, -67821);
Compression.CompressVarInt(writer, -2284);
Compression.CompressVarInt(writer, -234);
Compression.CompressVarInt(writer, 0);
Compression.CompressVarInt(writer, 234);
Compression.CompressVarInt(writer, 2284);
Compression.CompressVarInt(writer, 67821);
Compression.CompressVarInt(writer, 16777210);
Compression.CompressVarInt(writer, 16777219);
Compression.CompressVarInt(writer, 4294967295);
Compression.CompressVarInt(writer, 1099511627775);
Compression.CompressVarInt(writer, 281474976710655);
Compression.CompressVarInt(writer, 72057594037927935);
Compression.CompressVarInt(writer, long.MaxValue);
NetworkReader reader = new NetworkReader(writer.ToArray());
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(0));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(234));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(2284));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(67821));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(16777210));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(16777219));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(4294967295));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(1099511627775));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(281474976710655));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(72057594037927935));
Assert.That(Compression.DecompressVarUInt(reader), Is.EqualTo(ulong.MaxValue));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(long.MinValue));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-72057594037927935));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-281474976710655));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-1099511627775));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-4294967295));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-16777219));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-16777210));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-67821));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-2284));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(-234));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(0));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(234));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(2284));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(67821));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(16777210));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(16777219));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(4294967295));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(1099511627775));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(281474976710655));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(72057594037927935));
Assert.That(Compression.DecompressVarInt(reader), Is.EqualTo(long.MaxValue));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 541cf2bc89fe4395b2a50d921c91424a
timeCreated: 1613190697

View File

@ -0,0 +1,53 @@
using NUnit.Framework;
namespace Mirror.Tests
{
public class MockQuest
{
public int Id;
public MockQuest(int id)
{
Id = id;
}
public MockQuest()
{
Id = 0;
}
}
public static class MockQuestReaderWriter
{
public static void WriteQuest(this NetworkWriter writer, MockQuest quest)
{
writer.WriteInt(quest.Id);
}
public static MockQuest WriteQuest(this NetworkReader reader)
{
return new MockQuest(reader.ReadInt());
}
}
[TestFixture]
public class CustomRWTest
{
public struct QuestMessage : NetworkMessage
{
public MockQuest quest;
}
[Test]
public void TestCustomRW()
{
QuestMessage message = new QuestMessage
{
quest = new MockQuest(100)
};
byte[] data = MessagePackingTest.PackToByteArray(message);
QuestMessage unpacked = MessagePackingTest.UnpackFromByteArray<QuestMessage>(data);
Assert.That(unpacked.quest.Id, Is.EqualTo(100));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d74d53ca2c8c4b1195833376f9f6bb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,91 @@
using NUnit.Framework;
namespace Mirror.Tests
{
public static class MyCustomEnumReadWrite
{
public static void WriteMyCustomEnum(this NetworkWriter networkWriter, EnumReadWriteTests.MyCustomEnum customEnum)
{
// if O write N
if (customEnum == EnumReadWriteTests.MyCustomEnum.O)
{
networkWriter.WriteInt((int)EnumReadWriteTests.MyCustomEnum.N);
}
else
{
networkWriter.WriteInt((int)customEnum);
}
}
public static EnumReadWriteTests.MyCustomEnum ReadMyCustomEnum(this NetworkReader networkReader)
{
return (EnumReadWriteTests.MyCustomEnum)networkReader.ReadInt();
}
}
public class EnumReadWriteTests
{
public struct ByteMessage : NetworkMessage { public MyByteEnum byteEnum; }
public enum MyByteEnum : byte
{
A, B, C, D
}
public struct ShortMessage : NetworkMessage { public MyShortEnum shortEnum; }
public enum MyShortEnum : short
{
E, F, G, H
}
public struct CustomMessage : NetworkMessage { public MyCustomEnum customEnum; }
public enum MyCustomEnum
{
M, N, O, P
}
[Test]
public void ByteIsSentForByteEnum()
{
ByteMessage msg = new ByteMessage() { byteEnum = MyByteEnum.B };
NetworkWriter writer = new NetworkWriter();
writer.Write(msg);
// should be 1 byte for data
Assert.That(writer.Position, Is.EqualTo(1));
}
[Test]
public void ShortIsSentForShortEnum()
{
ShortMessage msg = new ShortMessage() { shortEnum = MyShortEnum.G };
NetworkWriter writer = new NetworkWriter();
writer.Write(msg);
// should be 2 bytes for data
Assert.That(writer.Position, Is.EqualTo(2));
}
[Test]
public void CustomWriterIsUsedForEnum()
{
CustomMessage serverMsg = new CustomMessage() { customEnum = MyCustomEnum.O };
CustomMessage clientMsg = SerializeAndDeserializeMessage(serverMsg);
// custom writer should write N if it sees O
Assert.That(clientMsg.customEnum, Is.EqualTo(MyCustomEnum.N));
}
T SerializeAndDeserializeMessage<T>(T msg)
where T : struct, NetworkMessage
{
NetworkWriter writer = new NetworkWriter();
writer.Write(msg);
NetworkReader reader = new NetworkReader(writer.ToArraySegment());
return reader.Read<T>();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79e6cd90456eed340a72b1bdb6fe7e49
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
using NUnit.Framework;
namespace Mirror.Tests
{
[TestFixture]
public class ExponentialMovingAverageTest
{
[Test]
public void TestInitial()
{
ExponentialMovingAverage ema = new ExponentialMovingAverage(10);
ema.Add(3);
Assert.That(ema.Value, Is.EqualTo(3));
Assert.That(ema.Var, Is.EqualTo(0));
}
[Test]
public void TestMovingAverage()
{
ExponentialMovingAverage ema = new ExponentialMovingAverage(10);
ema.Add(5);
ema.Add(6);
Assert.That(ema.Value, Is.EqualTo(5.1818).Within(0.0001f));
Assert.That(ema.Var, Is.EqualTo(0.1487).Within(0.0001f));
}
[Test]
public void TestVar()
{
ExponentialMovingAverage ema = new ExponentialMovingAverage(10);
ema.Add(5);
ema.Add(6);
ema.Add(7);
Assert.That(ema.Var, Is.EqualTo(0.6134).Within(0.0001f));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e3f2ecadd13149f29cd3e83ef6a4bff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace Mirror.Tests
{
public class ExtensionsTest
{
// supposed to return same result on all platforms
[Test]
public void GetStableHashHode()
{
Assert.That("".GetStableHashCode(), Is.EqualTo(23));
Assert.That("Test".GetStableHashCode(), Is.EqualTo(23844169));
}
[Test]
public void CopyToList()
{
List<int> source = new List<int>{1, 2, 3};
List<int> destination = new List<int>();
source.CopyTo(destination);
Assert.That(destination.SequenceEqual(source), Is.True);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 21e1452d6f734618a9364a2c6c116922
timeCreated: 1621762116

View File

@ -0,0 +1,56 @@
using System;
using Mirror.Tests.RemoteAttrributeTest;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests.GeneratedWriterTests
{
public class BaseData
{
public bool toggle;
}
public class SomeOtherData : BaseData
{
public int usefulNumber;
}
public class DataSenderBehaviour : NetworkBehaviour
{
public event Action<SomeOtherData> onData;
[Command]
public void CmdSendData(SomeOtherData otherData)
{
onData?.Invoke(otherData);
}
}
public class FieldsInBaseClasses : RemoteTestBase
{
[Test, Ignore("Destroy is needed for the code. Can't be called in Edit mode.")]
public void WriterShouldIncludeFieldsInBaseClass()
{
// spawn with owner
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out DataSenderBehaviour hostBehaviour, NetworkServer.localConnection);
const bool toggle = true;
const int usefulNumber = 10;
int called = 0;
hostBehaviour.onData += data =>
{
called++;
Assert.That(data.usefulNumber, Is.EqualTo(usefulNumber));
Assert.That(data.toggle, Is.EqualTo(toggle));
};
hostBehaviour.CmdSendData(new SomeOtherData
{
usefulNumber = usefulNumber,
toggle = toggle
});
ProcessMessages();
Assert.That(called, Is.EqualTo(1));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 518288ffe7c7215458a98466131fc7af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f1a04c2c41f19ea46b0b1a33c8f2ae89
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e49c298f22d4292439dc17f7e59f08b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c573b90d8949dec4cafb4f7401be9950
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
public class Grid2DTests
{
Grid2D<int> grid = new Grid2D<int>();
[Test]
public void AddAndGetNeighbours()
{
// add two at (0, 0)
grid.Add(Vector2Int.zero, 1);
grid.Add(Vector2Int.zero, 2);
HashSet<int> result = new HashSet<int>();
grid.GetWithNeighbours(Vector2Int.zero, result);
Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result.Contains(1), Is.True);
Assert.That(result.Contains(2), Is.True);
// add a neighbour at (1, 1)
grid.Add(new Vector2Int(1, 1), 3);
grid.GetWithNeighbours(Vector2Int.zero, result);
Assert.That(result.Count, Is.EqualTo(3));
Assert.That(result.Contains(1), Is.True);
Assert.That(result.Contains(2), Is.True);
Assert.That(result.Contains(3), Is.True);
}
[Test]
public void GetIgnoresTooFarNeighbours()
{
// add at (0, 0)
grid.Add(Vector2Int.zero, 1);
// get at (2, 0) which is out of 9 neighbour radius
HashSet<int> result = new HashSet<int>();
grid.GetWithNeighbours(new Vector2Int(2, 0), result);
Assert.That(result.Count, Is.EqualTo(0));
}
[Test]
public void ClearNonAlloc()
{
// add some
grid.Add(Vector2Int.zero, 1);
grid.Add(Vector2Int.zero, 2);
// clear and check if empty now
grid.ClearNonAlloc();
HashSet<int> result = new HashSet<int>();
grid.GetWithNeighbours(Vector2Int.zero, result);
Assert.That(result.Count, Is.EqualTo(0));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f8f3c4f37bb54824b5dfe70e0984b3d3
timeCreated: 1613188745

View File

@ -0,0 +1,95 @@
// default = no component = everyone sees everyone
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
public abstract class InterestManagementTests_Common : MirrorEditModeTest
{
protected GameObject gameObjectA;
protected NetworkIdentity identityA;
protected NetworkConnectionToClient connectionA;
protected GameObject gameObjectB;
protected NetworkIdentity identityB;
protected NetworkConnectionToClient connectionB;
[SetUp]
public override void SetUp()
{
base.SetUp();
// A with connectionId = 0x0A, netId = 0xAA
CreateNetworked(out gameObjectA, out identityA);
connectionA = new NetworkConnectionToClient(0x0A);
connectionA.isAuthenticated = true;
connectionA.isReady = true;
connectionA.identity = identityA;
NetworkServer.spawned[0xAA] = identityA;
// B
CreateNetworked(out gameObjectB, out identityB);
connectionB = new NetworkConnectionToClient(0x0B);
connectionB.isAuthenticated = true;
connectionB.isReady = true;
connectionB.identity = identityB;
NetworkServer.spawned[0xBB] = identityB;
// need to start server so that interest management works
NetworkServer.Listen(10);
// add both connections
NetworkServer.connections[connectionA.connectionId] = connectionA;
NetworkServer.connections[connectionB.connectionId] = connectionB;
// spawn both so that .observers is created
NetworkServer.Spawn(gameObjectA, connectionA);
NetworkServer.Spawn(gameObjectB, connectionB);
// spawn already runs interest management once
// clear observers and observing so tests can start from scratch
identityA.observers.Clear();
identityB.observers.Clear();
connectionA.observing.Clear();
connectionB.observing.Clear();
}
[TearDown]
public override void TearDown()
{
// set isServer is false. otherwise Destroy instead of
// DestroyImmediate is called internally, giving an error in Editor
identityA.isServer = false;
// set isServer is false. otherwise Destroy instead of
// DestroyImmediate is called internally, giving an error in Editor
identityB.isServer = false;
// clear connections first. calling OnDisconnect wouldn't work since
// we have no real clients.
NetworkServer.connections.Clear();
base.TearDown();
}
// player should always see self no matter what
[Test]
public void PlayerAlwaysSeesSelf_Initial()
{
// rebuild for A
// initial rebuild adds all connections if no interest management available
NetworkServer.RebuildObservers(identityA, true);
// should see self
Assert.That(identityA.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// forceHidden should still work
[Test]
public abstract void ForceHidden_Initial();
// forceShown should still work
[Test]
public abstract void ForceShown_Initial();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f0349d2e3a74454e9dc8badec1db0289
timeCreated: 1613049868

View File

@ -0,0 +1,62 @@
// default = no component = everyone sees everyone
using NUnit.Framework;
namespace Mirror.Tests
{
public class InterestManagementTests_Default : InterestManagementTests_Common
{
// no interest management (default)
// => forceHidden should still work
[Test]
public override void ForceHidden_Initial()
{
// force hide A
identityA.visible = Visibility.ForceHidden;
// rebuild for both
// initial rebuild adds all connections if no interest management available
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// A should not be seen by B because A is force hidden
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.False);
// B should be seen by A because
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// no interest management (default)
// => forceShown should still work
[Test]
public override void ForceShown_Initial()
{
// force show A
identityA.visible = Visibility.ForceShown;
// rebuild for both
// initial rebuild adds all connections if no interest management available
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// both should see each other because by default, everyone sees everyone
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// no interest management (default)
// => everyone should see everyone
[Test]
public void EveryoneSeesEveryone_Initial()
{
// rebuild for both
// initial rebuild adds all connections if no interest management available
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// both should see each other
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// TODO add tests to make sure old observers are removed etc.
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c5ac1fc2c25043378f80bc02d868a5d6
timeCreated: 1613042576

View File

@ -0,0 +1,142 @@
// Vector3.Distance based
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
public class InterestManagementTests_Distance : InterestManagementTests_Common
{
DistanceInterestManagement aoi;
[SetUp]
public override void SetUp()
{
base.SetUp();
// create AOI GameObject
CreateGameObject(out GameObject go, out aoi);
aoi.visRange = 10;
// setup server aoi since InterestManagement Awake isn't called
NetworkServer.aoi = aoi;
}
[TearDown]
public override void TearDown()
{
base.TearDown();
// clear server aoi again
NetworkServer.aoi = null;
}
// brute force interest management
// => forceHidden should still work
[Test]
public override void ForceHidden_Initial()
{
// A and B are at (0,0,0) so within range!
// force hide A
identityA.visible = Visibility.ForceHidden;
// rebuild for both
// initial rebuild while both are within range
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// A should not be seen by B because A is force hidden
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.False);
// B should be seen by A because
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// brute force interest management
// => forceHidden should still work
[Test]
public override void ForceShown_Initial()
{
// A and B are too far from each other
identityB.transform.position = Vector3.right * (aoi.visRange + 1);
// force show A
identityA.visible = Visibility.ForceShown;
// rebuild for both
// initial rebuild while both are within range
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// A should see B because A is force shown
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
// B should not be seen by A because they are too far from each other
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.False);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void InRange_Initial()
{
// A and B are at (0,0,0) so within range!
// rebuild for both
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// both should see each other because they are in range
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void InRange_NotInitial()
{
// A and B are at (0,0,0) so within range!
// rebuild for both
NetworkServer.RebuildObservers(identityA, false);
NetworkServer.RebuildObservers(identityB, false);
// both should see each other because they are in range
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void OutOfRange_Initial()
{
// A and B are too far from each other
identityB.transform.position = Vector3.right * (aoi.visRange + 1);
// rebuild for boths
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// both should not see each other
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.False);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.False);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void OutOfRange_NotInitial()
{
// A and B are too far from each other
identityB.transform.position = Vector3.right * (aoi.visRange + 1);
// rebuild for boths
NetworkServer.RebuildObservers(identityA, false);
NetworkServer.RebuildObservers(identityB, false);
// both should not see each other
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.False);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.False);
}
// TODO add tests to make sure old observers are removed etc.
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ffb9dc0ff01e4f979c216984d7fc48d0
timeCreated: 1613049838

View File

@ -0,0 +1,154 @@
// default = no component = everyone sees everyone
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
public class InterestManagementTests_SpatialHashing : InterestManagementTests_Common
{
SpatialHashingInterestManagement aoi;
[SetUp]
public override void SetUp()
{
base.SetUp();
// create spatial hashing object & component
CreateGameObject(out GameObject go, out aoi);
aoi.visRange = 10;
// setup server aoi since InterestManagement Awake isn't called
NetworkServer.aoi = aoi;
// rebuild grid once so the two connections are in there
aoi.Update();
}
[TearDown]
public override void TearDown()
{
base.TearDown();
// clear server aoi again
NetworkServer.aoi = null;
}
// brute force interest management
// => forceHidden should still work
[Test]
public override void ForceHidden_Initial()
{
// A and B are at (0,0,0) so within range!
// force hide A
identityA.visible = Visibility.ForceHidden;
// rebuild for both
// initial rebuild while both are within range
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// A should not be seen by B because A is force hidden
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.False);
// B should be seen by A because
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// brute force interest management
// => forceShown should still work
[Test]
public override void ForceShown_Initial()
{
// A and B are too far from each other
identityB.transform.position = Vector3.right * (aoi.visRange + 1);
// force show A
identityA.visible = Visibility.ForceShown;
// update grid now that positions were changed
aoi.Update();
// rebuild for both
// initial rebuild while both are within range
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// A should see B because A is force shown
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
// B should not be seen by A because they are too far from each other
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.False);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void InRange_Initial()
{
// A and B are at (0,0,0) so within range!
// rebuild for both
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// both should see each other because they are in range
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void InRange_NotInitial()
{
// A and B are at (0,0,0) so within range!
// rebuild for both
NetworkServer.RebuildObservers(identityA, false);
NetworkServer.RebuildObservers(identityB, false);
// both should see each other because they are in range
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.True);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.True);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void OutOfRange_Initial()
{
// A and B are too far from each other
identityB.transform.position = Vector3.right * (aoi.visRange + 1);
// update grid now that positions were changed
aoi.Update();
// rebuild for boths
NetworkServer.RebuildObservers(identityA, true);
NetworkServer.RebuildObservers(identityB, true);
// both should not see each other
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.False);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.False);
}
// brute force interest management
// => everyone should see everyone if in range
[Test]
public void OutOfRange_NotInitial()
{
// A and B are too far from each other
identityB.transform.position = Vector3.right * (aoi.visRange + 1);
// update grid now that positions were changed
aoi.Update();
// rebuild for boths
NetworkServer.RebuildObservers(identityA, false);
NetworkServer.RebuildObservers(identityB, false);
// both should not see each other
Assert.That(identityA.observers.ContainsKey(connectionB.connectionId), Is.False);
Assert.That(identityB.observers.ContainsKey(connectionA.connectionId), Is.False);
}
// TODO add tests to make sure old observers are removed etc.
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 63d9e0d6aa9a4eec8b4e2db07e6261bf
timeCreated: 1613117841

View File

@ -0,0 +1,71 @@
using NUnit.Framework;
namespace Mirror.Tests
{
public class LocalConnectionTest : MirrorTest
{
struct TestMessage : NetworkMessage {}
LocalConnectionToClient connectionToClient;
LocalConnectionToServer connectionToServer;
[SetUp]
public override void SetUp()
{
base.SetUp();
CreateLocalConnectionPair(out connectionToClient, out connectionToServer);
// set up server/client connections so message handling works
NetworkClient.connection = connectionToServer;
NetworkServer.connections[connectionToClient.connectionId] = connectionToClient;
}
[TearDown]
public override void TearDown()
{
connectionToServer.Disconnect();
base.TearDown();
}
[Test]
public void ClientToServerTest()
{
Assert.That(connectionToClient.address, Is.EqualTo("localhost"));
bool invoked = false;
void Handler(NetworkConnection conn, TestMessage message)
{
invoked = true;
}
// set up handler on the server connection
NetworkServer.RegisterHandler<TestMessage>(Handler, false);
connectionToServer.Send(new TestMessage());
connectionToServer.Update();
Assert.True(invoked, "handler should have been invoked");
}
[Test]
public void ServerToClient()
{
Assert.That(connectionToServer.address, Is.EqualTo("localhost"));
bool invoked = false;
void Handler(TestMessage message)
{
invoked = true;
}
// set up handler on the client connection
NetworkClient.RegisterHandler<TestMessage>(Handler, false);
connectionToClient.Send(new TestMessage());
connectionToServer.Update();
Assert.True(invoked, "handler should have been invoked");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 405841e2a21c64d7585d5c71d06ffff2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,125 @@
// TODO Send only supports structs. Consider removing those tests.
using NUnit.Framework;
namespace Mirror.Tests.MessageTests
{
class ParentMessage : NetworkMessage
{
public int parentValue;
}
class ChildMessage : ParentMessage
{
public int childValue;
}
public abstract class RequestMessageBase : NetworkMessage
{
public int responseId = 0;
}
public class ResponseMessage : RequestMessageBase
{
public int state;
public string message = "";
public int errorCode = 0; // optional for error codes
}
//reverseOrder to test this https://github.com/vis2k/Mirror/issues/1925
public class ResponseMessageReverse : RequestMessageBaseReverse
{
public int state;
public string message = "";
public int errorCode = 0; // optional for error codes
}
public abstract class RequestMessageBaseReverse : NetworkMessage
{
public int responseId = 0;
}
[TestFixture]
public class MessageInheritanceTest
{
[Test]
public void SendsVauesInParentAndChildClass()
{
NetworkWriter writer = new NetworkWriter();
writer.Write(new ChildMessage
{
parentValue = 3,
childValue = 4
});
byte[] arr = writer.ToArray();
NetworkReader reader = new NetworkReader(arr);
ChildMessage received = reader.Read<ChildMessage>();
Assert.AreEqual(3, received.parentValue);
Assert.AreEqual(4, received.childValue);
int writeLength = writer.Position;
int readLength = reader.Position;
Assert.That(writeLength == readLength, $"OnSerializeAll and OnDeserializeAll calls write the same amount of data\n writeLength={writeLength}\n readLength={readLength}");
}
[Test]
public void SendsVauesWhenUsingAbstractClass()
{
NetworkWriter writer = new NetworkWriter();
const int state = 2;
const string message = "hello world";
const int responseId = 5;
writer.Write(new ResponseMessage
{
state = state,
message = message,
responseId = responseId,
});
byte[] arr = writer.ToArray();
NetworkReader reader = new NetworkReader(arr);
ResponseMessage received = reader.Read<ResponseMessage>();
Assert.AreEqual(state, received.state);
Assert.AreEqual(message, received.message);
Assert.AreEqual(responseId, received.responseId);
int writeLength = writer.Position;
int readLength = reader.Position;
Assert.That(writeLength == readLength, $"OnSerializeAll and OnDeserializeAll calls write the same amount of data\n writeLength={writeLength}\n readLength={readLength}");
}
[Test]
public void SendsVauesWhenUsingAbstractClassReverseDefineOrder()
{
NetworkWriter writer = new NetworkWriter();
const int state = 2;
const string message = "hello world";
const int responseId = 5;
writer.Write(new ResponseMessageReverse
{
state = state,
message = message,
responseId = responseId,
});
byte[] arr = writer.ToArray();
NetworkReader reader = new NetworkReader(arr);
ResponseMessageReverse received = reader.Read<ResponseMessageReverse>();
Assert.AreEqual(state, received.state);
Assert.AreEqual(message, received.message);
Assert.AreEqual(responseId, received.responseId);
int writeLength = writer.Position;
int readLength = reader.Position;
Assert.That(writeLength == readLength, $"OnSerializeAll and OnDeserializeAll calls write the same amount of data\n writeLength={writeLength}\n readLength={readLength}");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 808855b645f9843d2b3077ab1304b2b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,143 @@
using System;
using Mirror.Tests.MessageTests;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
[TestFixture]
public class MessagePackingTest
{
public struct EmptyMessage : NetworkMessage {}
// helper function to pack message into a simple byte[]
public static byte[] PackToByteArray<T>(T message)
where T : struct, NetworkMessage
{
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
MessagePacking.Pack(message, writer);
return writer.ToArray();
}
}
// unpack a message we received
public static T UnpackFromByteArray<T>(byte[] data)
where T : struct, NetworkMessage
{
using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(data))
{
int msgType = MessagePacking.GetId<T>();
int id = networkReader.ReadUShort();
if (id != msgType)
throw new FormatException($"Invalid message, could not unpack {typeof(T).FullName}");
return networkReader.Read<T>();
}
}
// message id is generated from message.FullName.
// should be consistent across all platforms.
[Test]
public void GetId()
{
// "Mirror.Tests.MessageTests.TestMessage"
Debug.Log(typeof(TestMessage).FullName);
Assert.That(MessagePacking.GetId<TestMessage>(), Is.EqualTo(0x8706));
}
[Test]
public void TestPacking()
{
SceneMessage message = new SceneMessage()
{
sceneName = "Hello world",
sceneOperation = SceneOperation.LoadAdditive
};
byte[] data = PackToByteArray(message);
SceneMessage unpacked = UnpackFromByteArray<SceneMessage>(data);
Assert.That(unpacked.sceneName, Is.EqualTo("Hello world"));
Assert.That(unpacked.sceneOperation, Is.EqualTo(SceneOperation.LoadAdditive));
}
[Test]
public void UnpackWrongMessage()
{
SpawnMessage message = new SpawnMessage();
byte[] data = PackToByteArray(message);
Assert.Throws<FormatException>(() =>
{
ReadyMessage unpacked = UnpackFromByteArray<ReadyMessage>(data);
});
}
[Test]
public void TestUnpackIdMismatch()
{
// Unpack<T> has a id != msgType case that throws a FormatException.
// let's try to trigger it.
SceneMessage message = new SceneMessage()
{
sceneName = "Hello world",
sceneOperation = SceneOperation.LoadAdditive
};
byte[] data = PackToByteArray(message);
// overwrite the id
data[0] = 0x01;
data[1] = 0x02;
Assert.Throws<FormatException>(() =>
{
SceneMessage unpacked = UnpackFromByteArray<SceneMessage>(data);
});
}
[Test]
public void TestUnpackMessageNonGeneric()
{
// try a regular message
SceneMessage message = new SceneMessage()
{
sceneName = "Hello world",
sceneOperation = SceneOperation.LoadAdditive
};
byte[] data = PackToByteArray(message);
NetworkReader reader = new NetworkReader(data);
bool result = MessagePacking.Unpack(reader, out ushort msgType);
Assert.That(result, Is.EqualTo(true));
Assert.That(msgType, Is.EqualTo(BitConverter.ToUInt16(data, 0)));
}
[Test]
public void UnpackInvalidMessage()
{
// try an invalid message
NetworkReader reader2 = new NetworkReader(new byte[0]);
bool result2 = MessagePacking.Unpack(reader2, out ushort msgType2);
Assert.That(result2, Is.EqualTo(false));
Assert.That(msgType2, Is.EqualTo(0));
}
[Test]
public void MessageIdIsCorrectLength()
{
NetworkWriter writer = new NetworkWriter();
MessagePacking.Pack(new EmptyMessage(), writer);
ArraySegment<byte> segment = writer.ToArraySegment();
Assert.That(segment.Count, Is.EqualTo(MessagePacking.HeaderSize), "Empty message should have same size as HeaderSize");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d57c17d9ee7c49e6bacc54ddbeac751
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,386 @@
using System;
using System.IO;
using NSubstitute;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
public class MyMiddleware : MiddlewareTransport {}
[Description("Test to make sure inner methods are called when using Middleware Transport")]
public class MiddlewareTransportTest
{
Transport inner;
MyMiddleware middleware;
[SetUp]
public void Setup()
{
inner = Substitute.For<Transport>();
GameObject gameObject = new GameObject();
middleware = gameObject.AddComponent<MyMiddleware>();
middleware.inner = inner;
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(middleware.gameObject);
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void TestAvailable(bool available)
{
inner.Available().Returns(available);
Assert.That(middleware.Available(), Is.EqualTo(available));
inner.Received(1).Available();
}
[Test]
[TestCase(Channels.Reliable, 4000)]
[TestCase(Channels.Reliable, 2000)]
[TestCase(Channels.Unreliable, 4000)]
public void TestGetMaxPacketSize(int channel, int packageSize)
{
inner.GetMaxPacketSize(Arg.Any<int>()).Returns(packageSize);
Assert.That(middleware.GetMaxPacketSize(channel), Is.EqualTo(packageSize));
inner.Received(1).GetMaxPacketSize(Arg.Is<int>(x => x == channel));
inner.Received(0).GetMaxPacketSize(Arg.Is<int>(x => x != channel));
}
[Test]
public void TestShutdown()
{
middleware.Shutdown();
inner.Received(1).Shutdown();
}
[Test]
[TestCase("localhost")]
[TestCase("example.com")]
public void TestClientConnect(string address)
{
middleware.ClientConnect(address);
inner.Received(1).ClientConnect(address);
inner.Received(0).ClientConnect(Arg.Is<string>(x => x != address));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void TestClientConnected(bool connected)
{
inner.ClientConnected().Returns(connected);
Assert.That(middleware.ClientConnected(), Is.EqualTo(connected));
inner.Received(1).ClientConnected();
}
[Test]
public void TestClientDisconnect()
{
middleware.ClientDisconnect();
inner.Received(1).ClientDisconnect();
}
[Test]
[TestCase(Channels.Reliable)]
[TestCase(Channels.Unreliable)]
public void TestClientSend(int channel)
{
byte[] array = new byte[10];
const int offset = 2;
const int count = 5;
ArraySegment<byte> segment = new ArraySegment<byte>(array, offset, count);
middleware.ClientSend(segment, channel);
inner.Received(1).ClientSend(Arg.Is<ArraySegment<byte>>(x => x.Array == array && x.Offset == offset && x.Count == count), channel);
inner.Received(0).ClientSend(Arg.Any<ArraySegment<byte>>(), Arg.Is<int>(x => x != channel));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void TestServerActive(bool active)
{
inner.ServerActive().Returns(active);
Assert.That(middleware.ServerActive(), Is.EqualTo(active));
inner.Received(1).ServerActive();
}
[Test]
public void TestServerStart()
{
middleware.ServerStart();
inner.Received(1).ServerStart();
}
[Test]
public void TestServerStop()
{
middleware.ServerStop();
inner.Received(1).ServerStop();
}
[Test]
[TestCase(0, 0)]
[TestCase(1, 0)]
[TestCase(0, 1)]
[TestCase(19, 1)]
public void TestServerSend(int id, int channel)
{
byte[] array = new byte[10];
const int offset = 2;
const int count = 5;
ArraySegment<byte> segment = new ArraySegment<byte>(array, offset, count);
middleware.ServerSend(id, segment, channel);
inner.Received(1).ServerSend(id, Arg.Is<ArraySegment<byte>>(x => x.Array == array && x.Offset == offset && x.Count == count), channel);
// only need to check first arg,
inner.Received(0).ServerSend(Arg.Is<int>(x => x != id), Arg.Any<ArraySegment<byte>>(), Arg.Any<int>());
}
[Test]
[TestCase(0, "tcp4://localhost:7777")]
[TestCase(19, "tcp4://example.com:7777")]
public void TestServerGetClientAddress(int id, string result)
{
inner.ServerGetClientAddress(id).Returns(result);
Assert.That(middleware.ServerGetClientAddress(id), Is.EqualTo(result));
inner.Received(1).ServerGetClientAddress(id);
inner.Received(0).ServerGetClientAddress(Arg.Is<int>(x => x != id));
}
[Test]
[TestCase("tcp4://localhost:7777")]
[TestCase("tcp4://example.com:7777")]
public void TestServerUri(string address)
{
Uri uri = new Uri(address);
inner.ServerUri().Returns(uri);
Assert.That(middleware.ServerUri(), Is.EqualTo(uri));
inner.Received(1).ServerUri();
}
[Test]
public void TestClientConnectedCallback()
{
int called = 0;
middleware.OnClientConnected = () =>
{
called++;
};
// connect to give callback to inner
middleware.ClientConnect("localhost");
inner.OnClientConnected.Invoke();
Assert.That(called, Is.EqualTo(1));
inner.OnClientConnected.Invoke();
Assert.That(called, Is.EqualTo(2));
}
[Test]
[TestCase(0)]
[TestCase(1)]
public void TestClientDataReceivedCallback(int channel)
{
byte[] data = new byte[4];
ArraySegment<byte> segment = new ArraySegment<byte>(data, 1, 2);
int called = 0;
middleware.OnClientDataReceived = (d, c) =>
{
called++;
Assert.That(c, Is.EqualTo(channel));
Assert.That(d.Array, Is.EqualTo(segment.Array));
Assert.That(d.Offset, Is.EqualTo(segment.Offset));
Assert.That(d.Count, Is.EqualTo(segment.Count));
};
// connect to give callback to inner
middleware.ClientConnect("localhost");
inner.OnClientDataReceived.Invoke(segment, channel);
Assert.That(called, Is.EqualTo(1));
data = new byte[4];
segment = new ArraySegment<byte>(data, 0, 3);
inner.OnClientDataReceived.Invoke(segment, channel);
Assert.That(called, Is.EqualTo(2));
}
[Test]
public void TestClientDisconnectedCallback()
{
int called = 0;
middleware.OnClientDisconnected = () =>
{
called++;
};
// connect to give callback to inner
middleware.ClientConnect("localhost");
inner.OnClientDisconnected.Invoke();
Assert.That(called, Is.EqualTo(1));
inner.OnClientDisconnected.Invoke();
Assert.That(called, Is.EqualTo(2));
}
[Test]
public void TestClientErrorCallback()
{
Exception exception = new InvalidDataException();
int called = 0;
middleware.OnClientError = (e) =>
{
called++;
Assert.That(e, Is.EqualTo(exception));
};
// connect to give callback to inner
middleware.ClientConnect("localhost");
inner.OnClientError.Invoke(exception);
Assert.That(called, Is.EqualTo(1));
exception = new NullReferenceException();
inner.OnClientError.Invoke(exception);
Assert.That(called, Is.EqualTo(2));
}
[Test]
[TestCase(0)]
[TestCase(1)]
[TestCase(19)]
public void TestServerConnectedCallback(int id)
{
int called = 0;
middleware.OnServerConnected = (i) =>
{
called++;
Assert.That(i, Is.EqualTo(id));
};
// start to give callback to inner
middleware.ServerStart();
inner.OnServerConnected.Invoke(id);
Assert.That(called, Is.EqualTo(1));
inner.OnServerConnected.Invoke(id);
Assert.That(called, Is.EqualTo(2));
}
[Test]
[TestCase(0, 0)]
[TestCase(1, 0)]
[TestCase(19, 0)]
[TestCase(0, 1)]
[TestCase(1, 1)]
[TestCase(19, 1)]
public void TestServerDataReceivedCallback(int id, int channel)
{
byte[] data = new byte[4];
ArraySegment<byte> segment = new ArraySegment<byte>(data, 1, 2);
int called = 0;
middleware.OnServerDataReceived = (i, d, c) =>
{
called++;
Assert.That(i, Is.EqualTo(id));
Assert.That(c, Is.EqualTo(channel));
Assert.That(d.Array, Is.EqualTo(segment.Array));
Assert.That(d.Offset, Is.EqualTo(segment.Offset));
Assert.That(d.Count, Is.EqualTo(segment.Count));
};
// start to give callback to inner
middleware.ServerStart();
inner.OnServerDataReceived.Invoke(id, segment, channel);
Assert.That(called, Is.EqualTo(1));
data = new byte[4];
segment = new ArraySegment<byte>(data, 0, 3);
inner.OnServerDataReceived.Invoke(id, segment, channel);
Assert.That(called, Is.EqualTo(2));
}
[Test]
[TestCase(0)]
[TestCase(1)]
[TestCase(19)]
public void TestServerDisconnectedCallback(int id)
{
int called = 0;
middleware.OnServerDisconnected = (i) =>
{
called++;
Assert.That(i, Is.EqualTo(id));
};
// start to give callback to inner
middleware.ServerStart();
inner.OnServerDisconnected.Invoke(id);
Assert.That(called, Is.EqualTo(1));
inner.OnServerDisconnected.Invoke(id);
Assert.That(called, Is.EqualTo(2));
}
[Test]
[TestCase(0)]
[TestCase(1)]
[TestCase(19)]
public void TestServerErrorCallback(int id)
{
Exception exception = new InvalidDataException();
int called = 0;
middleware.OnServerError = (i, e) =>
{
called++;
Assert.That(i, Is.EqualTo(id));
Assert.That(e, Is.EqualTo(exception));
};
// start to give callback to inner
middleware.ServerStart();
inner.OnServerError.Invoke(id, exception);
Assert.That(called, Is.EqualTo(1));
exception = new NullReferenceException();
inner.OnServerError.Invoke(id, exception);
Assert.That(called, Is.EqualTo(2));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ee4c7efa89013a41aeee942e60af4e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
{
"name": "Mirror.Tests",
"rootNamespace": "",
"references": [
"Mirror",
"Mirror.Editor",
"Mirror.Components",
"Mirror.Tests.Common",
"Telepathy",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"Unity.Mirror.CodeGen"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"NSubstitute.dll",
"Castle.Core.dll",
"System.Threading.Tasks.Extensions.dll",
"Mono.CecilX.dll",
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8b489029f75e64a7bbf6918bf1a49e39
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,180 @@
using System;
using NSubstitute;
using NUnit.Framework;
namespace Mirror.Tests
{
public class MultiplexTest : MirrorTest
{
Transport transport1;
Transport transport2;
new MultiplexTransport transport;
[SetUp]
public void Setup()
{
base.SetUp();
CreateGameObject(out _, out transport);
transport1 = Substitute.For<Transport>();
transport2 = Substitute.For<Transport>();
transport.transports = new[] { transport1, transport2 };
transport.Awake();
}
// A Test behaves as an ordinary method
[Test]
public void TestAvailable()
{
transport1.Available().Returns(true);
transport2.Available().Returns(false);
Assert.That(transport.Available());
}
// A Test behaves as an ordinary method
[Test]
public void TestNotAvailable()
{
transport1.Available().Returns(false);
transport2.Available().Returns(false);
Assert.That(transport.Available(), Is.False);
}
// A Test behaves as an ordinary method
[Test]
public void TestConnect()
{
transport1.Available().Returns(false);
transport2.Available().Returns(true);
transport.ClientConnect("some.server.com");
transport1.DidNotReceive().ClientConnect(Arg.Any<string>());
transport2.Received().ClientConnect("some.server.com");
}
// A Test behaves as an ordinary method
[Test]
public void TestConnectFirstUri()
{
Uri uri = new Uri("tcp://some.server.com");
transport1.Available().Returns(true);
transport2.Available().Returns(true);
transport.ClientConnect(uri);
transport1.Received().ClientConnect(uri);
transport2.DidNotReceive().ClientConnect(uri);
}
// A Test behaves as an ordinary method
[Test]
public void TestConnectSecondUri()
{
Uri uri = new Uri("ws://some.server.com");
transport1.Available().Returns(true);
// first transport does not support websocket
transport1
.When(x => x.ClientConnect(uri))
.Do(x => { throw new ArgumentException("Scheme not supported"); });
transport2.Available().Returns(true);
transport.ClientConnect(uri);
transport2.Received().ClientConnect(uri);
}
[Test]
public void TestConnected()
{
transport1.Available().Returns(true);
transport.ClientConnect("some.server.com");
transport1.ClientConnected().Returns(true);
Assert.That(transport.ClientConnected());
}
[Test]
public void TestDisconnect()
{
transport1.Available().Returns(true);
transport.ClientConnect("some.server.com");
transport.ClientDisconnect();
transport1.Received().ClientDisconnect();
}
[Test]
public void TestClientSend()
{
transport1.Available().Returns(true);
transport.ClientConnect("some.server.com");
byte[] data = { 1, 2, 3 };
ArraySegment<byte> segment = new ArraySegment<byte>(data);
transport.ClientSend(segment, 3);
transport1.Received().ClientSend(segment, 3);
}
[Test]
public void TestClient1Connected()
{
transport1.Available().Returns(true);
transport2.Available().Returns(true);
Action callback = Substitute.For<Action>();
// find available
transport.Awake();
// set event and connect to give event to inner
transport.OnClientConnected = callback;
transport.ClientConnect("localhost");
transport1.OnClientConnected.Invoke();
callback.Received().Invoke();
}
[Test]
public void TestClient2Connected()
{
transport1.Available().Returns(false);
transport2.Available().Returns(true);
Action callback = Substitute.For<Action>();
// find available
transport.Awake();
// set event and connect to give event to inner
transport.OnClientConnected = callback;
transport.ClientConnect("localhost");
transport2.OnClientConnected.Invoke();
callback.Received().Invoke();
}
[Test]
public void TestServerConnected()
{
byte[] data = { 1, 2, 3 };
ArraySegment<byte> segment = new ArraySegment<byte>(data);
// on connect, send a message back
void SendMessage(int connectionId)
{
transport.ServerSend(connectionId, segment, 5);
}
// set event and Start to give event to inner
transport.OnServerConnected = SendMessage;
transport.ServerStart();
transport1.OnServerConnected.Invoke(1);
transport1.Received().ServerSend(1, segment, 5);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0d8bef6afd464247bf433cdc5d3ff23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,184 @@
// dirty bits are powerful magic.
// add some tests to guarantee correct behaviour.
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
class NetworkBehaviourWithSyncVarsAndCollections : NetworkBehaviour
{
// SyncVars
[SyncVar] public int health;
[SyncVar] public int mana;
// SyncCollections
public readonly SyncList<int> list = new SyncList<int>();
public readonly SyncDictionary<int, string> dict = new SyncDictionary<int, string>();
}
public class NetworkBehaviourSyncVarDirtyBitsExposed : NetworkBehaviour
{
public ulong syncVarDirtyBitsExposed => syncVarDirtyBits;
}
public class NetworkBehaviourDirtyBitsTests : MirrorEditModeTest
{
[SetUp]
public override void SetUp()
{
base.SetUp();
// SyncLists are only set dirty while owner has observers.
// need a connection.
NetworkServer.Listen(1);
ConnectHostClientBlockingAuthenticatedAndReady();
}
[Test]
public void SetSyncVarDirtyBit()
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out NetworkBehaviourSyncVarDirtyBitsExposed comp);
// set 3rd dirty bit.
comp.SetSyncVarDirtyBit(0b_00000000_00000100);
Assert.That(comp.syncVarDirtyBitsExposed, Is.EqualTo(0b_00000000_00000100));
// set 5th dirty bit.
// both 3rd and 5th should be set.
comp.SetSyncVarDirtyBit(0b_00000000_00010000);
Assert.That(comp.syncVarDirtyBitsExposed, Is.EqualTo(0b_00000000_00010100));
}
// changing a SyncObject (collection) should modify the dirty mask.
[Test]
public void SyncObjectsSetDirtyBits()
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out NetworkBehaviourWithSyncVarsAndCollections comp);
// not dirty by default
Assert.That(comp.syncObjectDirtyBits, Is.EqualTo(0UL));
// change the list. should be dirty now.
comp.list.Add(42);
Assert.That(comp.syncObjectDirtyBits, Is.EqualTo(0b01));
// change the dict. should both be dirty.
comp.dict[42] = null;
Assert.That(comp.syncObjectDirtyBits, Is.EqualTo(0b11));
}
[Test]
public void IsDirty()
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity identity, out NetworkBehaviourWithSyncVarsAndCollections comp);
// not dirty by default
Assert.That(comp.IsDirty(), Is.False);
// changing a [SyncVar] should set it dirty
++comp.health;
Assert.That(comp.IsDirty(), Is.True);
comp.ClearAllDirtyBits();
// changing a SyncCollection should set it dirty
comp.list.Add(42);
Assert.That(comp.IsDirty(), Is.True);
comp.ClearAllDirtyBits();
// it should only be dirty after syncInterval elapsed
comp.syncInterval = float.MaxValue;
Assert.That(comp.IsDirty(), Is.False);
}
[Test]
public void ClearAllDirtyBitsClearsSyncVarDirtyBits()
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out EmptyBehaviour emptyBehaviour);
// set syncinterval so dirtybit works fine
emptyBehaviour.syncInterval = 0;
Assert.That(emptyBehaviour.IsDirty(), Is.False);
// set one syncvar dirty bit
emptyBehaviour.SetSyncVarDirtyBit(1);
Assert.That(emptyBehaviour.IsDirty(), Is.True);
// clear it
emptyBehaviour.ClearAllDirtyBits();
Assert.That(emptyBehaviour.IsDirty(), Is.False);
}
[Test]
public void ClearAllDirtyBitsClearsSyncObjectsDirtyBits()
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out NetworkBehaviourWithSyncVarsAndCollections comp);
// set syncinterval so dirtybit works fine
comp.syncInterval = 0;
Assert.That(comp.IsDirty(), Is.False);
// dirty the synclist
comp.list.Add(42);
Assert.That(comp.IsDirty, Is.True);
// clear bits should clear synclist bits too
comp.ClearAllDirtyBits();
Assert.That(comp.IsDirty, Is.False);
}
// NetworkServer.Broadcast clears all dirty bits in all spawned
// identity's components if they have no observers.
//
// this way dirty bit tracking only starts after first observer.
// otherwise first observer would still get dirty update for everything
// that was dirty before he observed. even though he already got the
// full state in spawn packet.
[Test]
public void DirtyBitsAreClearedForSpawnedWithoutObservers()
{
// need one player, one monster
CreateNetworkedAndSpawnPlayer(out _, out NetworkIdentity player, NetworkServer.localConnection);
CreateNetworkedAndSpawn(out _, out NetworkIdentity monster, out NetworkBehaviourWithSyncVarsAndCollections monsterComp);
// without AOI, player connection sees everyone automatically.
// remove the monster from observing.
// remvoe player from monster observers.
monster.RemoveObserver(player.connectionToClient);
Assert.That(monster.observers.Count, Is.EqualTo(0));
// modify something in the monster so that dirty bit is set
monsterComp.syncInterval = 0;
++monsterComp.health;
Assert.That(monsterComp.IsDirty(), Is.True);
// add first observer. dirty bits should be cleared.
monster.AddObserver(player.connectionToClient);
Assert.That(monsterComp.IsDirty(), Is.False);
}
}
// hook tests can only be ran when inheriting from NetworkBehaviour
public class NetworkBehaviourDirtyBitsHookGuardTester : NetworkBehaviour
{
[Test]
public void HookGuard()
{
// set hook guard for some bits
for (int i = 0; i < 10; ++i)
{
ulong bit = 1ul << i;
// should be false by default
Assert.That(GetSyncVarHookGuard(bit), Is.False);
// set true
SetSyncVarHookGuard(bit, true);
Assert.That(GetSyncVarHookGuard(bit), Is.True);
// set false again
SetSyncVarHookGuard(bit, false);
Assert.That(GetSyncVarHookGuard(bit), Is.False);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 71cd60f7177a4d3da75bb87f5ac13a18
timeCreated: 1631772886

View File

@ -0,0 +1,341 @@
using NUnit.Framework;
using UnityEngine;
// Note: Weaver doesn't run on nested class so use namespace to group classes instead
namespace Mirror.Tests.NetworkBehaviourSerialize
{
#region No OnSerialize/OnDeserialize override
abstract class AbstractBehaviour : NetworkBehaviour
{
public readonly SyncList<bool> syncListInAbstract = new SyncList<bool>();
[SyncVar]
public int SyncFieldInAbstract;
}
class BehaviourWithSyncVar : NetworkBehaviour
{
public readonly SyncList<bool> syncList = new SyncList<bool>();
[SyncVar]
public int SyncField;
}
class OverrideBehaviourFromSyncVar : AbstractBehaviour {}
class OverrideBehaviourWithSyncVarFromSyncVar : AbstractBehaviour
{
public readonly SyncList<bool> syncListInOverride = new SyncList<bool>();
[SyncVar]
public int SyncFieldInOverride;
}
class MiddleClass : AbstractBehaviour
{
// class with no sync var
}
class SubClass : MiddleClass
{
// class with sync var
// this is to make sure that override works correctly if base class doesn't have sync vars
[SyncVar]
public Vector3 anotherSyncField;
}
class MiddleClassWithSyncVar : AbstractBehaviour
{
// class with sync var
[SyncVar]
public string syncFieldInMiddle;
}
class SubClassFromSyncVar : MiddleClassWithSyncVar
{
// class with sync var
// this is to make sure that override works correctly if base class doesn't have sync vars
[SyncVar]
public Vector3 syncFieldInSub;
}
#endregion
#region OnSerialize/OnDeserialize override
class BehaviourWithSyncVarWithOnSerialize : NetworkBehaviour
{
public readonly SyncList<bool> syncList = new SyncList<bool>();
[SyncVar]
public int SyncField;
public float customSerializeField;
public override bool OnSerialize(NetworkWriter writer, bool initialState)
{
writer.WriteFloat(customSerializeField);
return base.OnSerialize(writer, initialState);
}
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
customSerializeField = reader.ReadFloat();
base.OnDeserialize(reader, initialState);
}
}
class OverrideBehaviourFromSyncVarWithOnSerialize : AbstractBehaviour
{
public float customSerializeField;
public override bool OnSerialize(NetworkWriter writer, bool initialState)
{
writer.WriteFloat(customSerializeField);
return base.OnSerialize(writer, initialState);
}
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
customSerializeField = reader.ReadFloat();
base.OnDeserialize(reader, initialState);
}
}
class OverrideBehaviourWithSyncVarFromSyncVarWithOnSerialize : AbstractBehaviour
{
public readonly SyncList<bool> syncListInOverride = new SyncList<bool>();
[SyncVar]
public int SyncFieldInOverride;
public float customSerializeField;
public override bool OnSerialize(NetworkWriter writer, bool initialState)
{
writer.WriteFloat(customSerializeField);
return base.OnSerialize(writer, initialState);
}
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
customSerializeField = reader.ReadFloat();
base.OnDeserialize(reader, initialState);
}
}
#endregion
public class NetworkBehaviourSerializeTest : MirrorEditModeTest
{
static void SyncNetworkBehaviour(NetworkBehaviour source, NetworkBehaviour target, bool initialState)
{
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
source.OnSerialize(writer, initialState);
using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment()))
{
target.OnDeserialize(reader, initialState);
}
}
}
[SetUp]
public override void SetUp()
{
base.SetUp();
// SyncLists are only set dirty while owner has observers.
// need a connection.
NetworkServer.Listen(1);
ConnectHostClientBlockingAuthenticatedAndReady();
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void BehaviourWithSyncVarTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out BehaviourWithSyncVar source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out BehaviourWithSyncVar target);
source.SyncField = 10;
source.syncList.Add(true);
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncField, Is.EqualTo(10));
Assert.That(target.syncList.Count, Is.EqualTo(1));
Assert.That(target.syncList[0], Is.True);
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void OverrideBehaviourFromSyncVarTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourFromSyncVar source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourFromSyncVar target);
source.SyncFieldInAbstract = 12;
source.syncListInAbstract.Add(true);
source.syncListInAbstract.Add(false);
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncFieldInAbstract, Is.EqualTo(12));
Assert.That(target.syncListInAbstract.Count, Is.EqualTo(2));
Assert.That(target.syncListInAbstract[0], Is.True);
Assert.That(target.syncListInAbstract[1], Is.False);
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void OverrideBehaviourWithSyncVarFromSyncVarTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourWithSyncVarFromSyncVar source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourWithSyncVarFromSyncVar target);
source.SyncFieldInAbstract = 10;
source.syncListInAbstract.Add(true);
source.SyncFieldInOverride = 52;
source.syncListInOverride.Add(false);
source.syncListInOverride.Add(true);
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncFieldInAbstract, Is.EqualTo(10));
Assert.That(target.syncListInAbstract.Count, Is.EqualTo(1));
Assert.That(target.syncListInAbstract[0], Is.True);
Assert.That(target.SyncFieldInOverride, Is.EqualTo(52));
Assert.That(target.syncListInOverride.Count, Is.EqualTo(2));
Assert.That(target.syncListInOverride[0], Is.False);
Assert.That(target.syncListInOverride[1], Is.True);
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void SubClassTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out SubClass source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out SubClass target);
source.SyncFieldInAbstract = 10;
source.syncListInAbstract.Add(true);
source.anotherSyncField = new Vector3(40, 20, 10);
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncFieldInAbstract, Is.EqualTo(10));
Assert.That(target.syncListInAbstract.Count, Is.EqualTo(1));
Assert.That(target.syncListInAbstract[0], Is.True);
Assert.That(target.anotherSyncField, Is.EqualTo(new Vector3(40, 20, 10)));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void SubClassFromSyncVarTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out SubClassFromSyncVar source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out SubClassFromSyncVar target);
source.SyncFieldInAbstract = 10;
source.syncListInAbstract.Add(true);
source.syncFieldInMiddle = "Hello World!";
source.syncFieldInSub = new Vector3(40, 20, 10);
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncFieldInAbstract, Is.EqualTo(10));
Assert.That(target.syncListInAbstract.Count, Is.EqualTo(1));
Assert.That(target.syncListInAbstract[0], Is.True);
Assert.That(target.syncFieldInMiddle, Is.EqualTo("Hello World!"));
Assert.That(target.syncFieldInSub, Is.EqualTo(new Vector3(40, 20, 10)));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void BehaviourWithSyncVarWithOnSerializeTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out BehaviourWithSyncVarWithOnSerialize source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out BehaviourWithSyncVarWithOnSerialize target);
source.SyncField = 10;
source.syncList.Add(true);
source.customSerializeField = 20.5f;
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncField, Is.EqualTo(10));
Assert.That(target.syncList.Count, Is.EqualTo(1));
Assert.That(target.syncList[0], Is.True);
Assert.That(target.customSerializeField, Is.EqualTo(20.5f));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void OverrideBehaviourFromSyncVarWithOnSerializeTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourFromSyncVarWithOnSerialize source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourFromSyncVarWithOnSerialize target);
source.SyncFieldInAbstract = 12;
source.syncListInAbstract.Add(true);
source.syncListInAbstract.Add(false);
source.customSerializeField = 20.5f;
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncFieldInAbstract, Is.EqualTo(12));
Assert.That(target.syncListInAbstract.Count, Is.EqualTo(2));
Assert.That(target.syncListInAbstract[0], Is.True);
Assert.That(target.syncListInAbstract[1], Is.False);
Assert.That(target.customSerializeField, Is.EqualTo(20.5f));
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void OverrideBehaviourWithSyncVarFromSyncVarWithOnSerializeTest(bool initialState)
{
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourWithSyncVarFromSyncVarWithOnSerialize source);
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out OverrideBehaviourWithSyncVarFromSyncVarWithOnSerialize target);
source.SyncFieldInAbstract = 10;
source.syncListInAbstract.Add(true);
source.SyncFieldInOverride = 52;
source.syncListInOverride.Add(false);
source.syncListInOverride.Add(true);
source.customSerializeField = 20.5f;
SyncNetworkBehaviour(source, target, initialState);
Assert.That(target.SyncFieldInAbstract, Is.EqualTo(10));
Assert.That(target.syncListInAbstract.Count, Is.EqualTo(1));
Assert.That(target.syncListInAbstract[0], Is.True);
Assert.That(target.SyncFieldInOverride, Is.EqualTo(52));
Assert.That(target.syncListInOverride.Count, Is.EqualTo(2));
Assert.That(target.syncListInOverride[0], Is.False);
Assert.That(target.syncListInOverride[1], Is.True);
Assert.That(target.customSerializeField, Is.EqualTo(20.5f));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 253d419ce2900134482c3c8b76883c60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,884 @@
using Mirror.RemoteCalls;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Mirror.Tests
{
class EmptyBehaviour : NetworkBehaviour {}
// we need to inherit from networkbehaviour to test protected functions
public class NetworkBehaviourDelegateComponent : NetworkBehaviour
{
public static void Delegate(NetworkBehaviour comp, NetworkReader reader, NetworkConnection senderConnection) {}
public static void Delegate2(NetworkBehaviour comp, NetworkReader reader, NetworkConnection senderConnection) {}
}
// we need to inherit from networkbehaviour to test protected functions
public class NetworkBehaviourSetSyncVarGameObjectComponent : NetworkBehaviour
{
//[SyncVar]
public GameObject test;
// usually generated by weaver
public uint testNetId;
// SetSyncVarGameObject wrapper to expose it
public void SetSyncVarGameObjectExposed(GameObject newGameObject, ulong dirtyBit) =>
SetSyncVarGameObject(newGameObject, ref test, dirtyBit, ref testNetId);
}
// we need to inherit from networkbehaviour to test protected functions
public class NetworkBehaviourGetSyncVarGameObjectComponent : NetworkBehaviour
{
//[SyncVar]
public GameObject test;
// usually generated by weaver
public uint testNetId;
// SetSyncVarGameObject wrapper to expose it
public GameObject GetSyncVarGameObjectExposed() =>
GetSyncVarGameObject(testNetId, ref test);
}
// we need to inherit from networkbehaviour to test protected functions
public class NetworkBehaviourSetSyncVarNetworkIdentityComponent : NetworkBehaviour
{
//[SyncVar]
public NetworkIdentity test;
// usually generated by weaver
public uint testNetId;
// SetSyncVarNetworkIdentity wrapper to expose it
public void SetSyncVarNetworkIdentityExposed(NetworkIdentity newNetworkIdentity, ulong dirtyBit) =>
SetSyncVarNetworkIdentity(newNetworkIdentity, ref test, dirtyBit, ref testNetId);
}
// we need to inherit from networkbehaviour to test protected functions
public class NetworkBehaviourGetSyncVarNetworkIdentityComponent : NetworkBehaviour
{
//[SyncVar]
public NetworkIdentity test;
// usually generated by weaver
public uint testNetId;
// SetSyncVarNetworkIdentity wrapper to expose it
public NetworkIdentity GetSyncVarNetworkIdentityExposed() =>
GetSyncVarNetworkIdentity(testNetId, ref test);
}
// we need to inherit from networkbehaviour to test protected functions
public class OnStopClientComponent : NetworkBehaviour
{
public int called;
public override void OnStopClient() => ++called;
}
// we need to inherit from networkbehaviour to test protected functions
public class OnStartClientComponent : NetworkBehaviour
{
public int called;
public override void OnStartClient() => ++called;
}
// we need to inherit from networkbehaviour to test protected functions
public class OnStartLocalPlayerComponent : NetworkBehaviour
{
public int called;
public override void OnStartLocalPlayer() => ++called;
}
public class NetworkBehaviourTests : MirrorEditModeTest
{
[TearDown]
public override void TearDown()
{
NetworkServer.RemoveLocalConnection();
base.TearDown();
}
[Test]
public void IsServerOnly()
{
CreateNetworked(out _, out NetworkIdentity identity, out EmptyBehaviour emptyBehaviour);
// call OnStartServer so isServer is true
identity.OnStartServer();
Assert.That(identity.isServer, Is.True);
// isServerOnly should be true when isServer = true && isClient = false
Assert.That(emptyBehaviour.isServer, Is.True);
Assert.That(emptyBehaviour.isClient, Is.False);
Assert.That(emptyBehaviour.isServerOnly, Is.True);
}
[Test]
public void IsClientOnly()
{
CreateNetworked(out _, out NetworkIdentity identity, out EmptyBehaviour emptyBehaviour);
// isClientOnly should be true when isServer = false && isClient = true
identity.isClient = true;
Assert.That(emptyBehaviour.isServer, Is.False);
Assert.That(emptyBehaviour.isClient, Is.True);
Assert.That(emptyBehaviour.isClientOnly, Is.True);
}
[Test]
public void HasNoAuthorityByDefault()
{
// no authority by default
CreateNetworked(out _, out _, out EmptyBehaviour emptyBehaviour);
Assert.That(emptyBehaviour.hasAuthority, Is.False);
}
[Test]
public void HasIdentitysNetId()
{
CreateNetworked(out _, out NetworkIdentity identity, out EmptyBehaviour emptyBehaviour);
identity.netId = 42;
Assert.That(emptyBehaviour.netId, Is.EqualTo(42));
}
[Test]
public void HasIdentitysConnectionToServer()
{
CreateNetworked(out _, out NetworkIdentity identity, out EmptyBehaviour emptyBehaviour);
identity.connectionToServer = new LocalConnectionToServer();
Assert.That(emptyBehaviour.connectionToServer, Is.EqualTo(identity.connectionToServer));
}
[Test]
public void HasIdentitysConnectionToClient()
{
CreateNetworked(out _, out NetworkIdentity identity, out EmptyBehaviour emptyBehaviour);
identity.connectionToClient = new LocalConnectionToClient();
Assert.That(emptyBehaviour.connectionToClient, Is.EqualTo(identity.connectionToClient));
}
[Test]
public void ComponentIndex()
{
// create a NetworkIdentity with two components
CreateNetworked(out GameObject _, out NetworkIdentity _, out EmptyBehaviour first, out EmptyBehaviour second);
Assert.That(first.ComponentIndex, Is.EqualTo(0));
Assert.That(second.ComponentIndex, Is.EqualTo(1));
}
[Test, Ignore("NetworkServerTest.SendCommand does it already")]
public void SendCommandInternal() {}
[Test, Ignore("ClientRpcTest.cs tests Rpcs already")]
public void SendRPCInternal() {}
[Test, Ignore("TargetRpcTest.cs tests TargetRpcs already")]
public void SendTargetRPCInternal() {}
[Test]
public void RegisterDelegateDoesntOverwrite()
{
// registerdelegate is protected, but we can use
// RegisterCommandDelegate which calls RegisterDelegate
int registeredHash1 = RemoteProcedureCalls.RegisterDelegate(
typeof(NetworkBehaviourDelegateComponent),
nameof(NetworkBehaviourDelegateComponent.Delegate),
RemoteCallType.Command,
NetworkBehaviourDelegateComponent.Delegate,
false);
// registering the exact same one should be fine. it should simply
// do nothing.
int registeredHash2 = RemoteProcedureCalls.RegisterDelegate(
typeof(NetworkBehaviourDelegateComponent),
nameof(NetworkBehaviourDelegateComponent.Delegate),
RemoteCallType.Command,
NetworkBehaviourDelegateComponent.Delegate,
false);
// registering the same name with a different callback shouldn't
// work
LogAssert.Expect(LogType.Error, $"Function {typeof(NetworkBehaviourDelegateComponent)}.{nameof(NetworkBehaviourDelegateComponent.Delegate)} and {typeof(NetworkBehaviourDelegateComponent)}.{nameof(NetworkBehaviourDelegateComponent.Delegate2)} have the same hash. Please rename one of them");
int registeredHash3 = RemoteProcedureCalls.RegisterDelegate(
typeof(NetworkBehaviourDelegateComponent),
nameof(NetworkBehaviourDelegateComponent.Delegate),
RemoteCallType.Command,
NetworkBehaviourDelegateComponent.Delegate2,
false);
// clean up
RemoteProcedureCalls.RemoveDelegate(registeredHash1);
RemoteProcedureCalls.RemoveDelegate(registeredHash2);
RemoteProcedureCalls.RemoveDelegate(registeredHash3);
}
[Test]
public void GetDelegate()
{
// registerdelegate is protected, but we can use
// RegisterCommandDelegate which calls RegisterDelegate
int registeredHash = RemoteProcedureCalls.RegisterDelegate(
typeof(NetworkBehaviourDelegateComponent),
nameof(NetworkBehaviourDelegateComponent.Delegate),
RemoteCallType.Command,
NetworkBehaviourDelegateComponent.Delegate,
false);
// get handler
int cmdHash = nameof(NetworkBehaviourDelegateComponent.Delegate).GetStableHashCode();
RemoteCallDelegate func = RemoteProcedureCalls.GetDelegate(cmdHash);
RemoteCallDelegate expected = NetworkBehaviourDelegateComponent.Delegate;
Assert.That(func, Is.EqualTo(expected));
// invalid hash should return null handler
RemoteCallDelegate funcNull = RemoteProcedureCalls.GetDelegate(1234);
Assert.That(funcNull, Is.Null);
// clean up
RemoteProcedureCalls.RemoveDelegate(registeredHash);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualZeroNetIdNullIsTrue()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// null and identity.netid==0 returns true (=equal)
//
// later we should reevaluate if this is so smart or not. might be
// better to return false here.
// => we possibly return false so that resync doesn't happen when
// GO disappears? or not?
bool result = NetworkBehaviour.SyncVarGameObjectEqual(null, identity.netId);
Assert.That(result, Is.True);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualNull()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// null should return false
bool result = NetworkBehaviour.SyncVarGameObjectEqual(null, identity.netId);
Assert.That(result, Is.False);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualZeroNetIdAndGOWithoutIdentityComponentIsTrue()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
CreateNetworked(out GameObject go, out NetworkIdentity _);
// null and identity.netid==0 returns true (=equal)
//
// later we should reevaluate if this is so smart or not. might be
// better to return false here.
// => we possibly return false so that resync doesn't happen when
// GO disappears? or not?
bool result = NetworkBehaviour.SyncVarGameObjectEqual(go, identity.netId);
Assert.That(result, Is.True);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualWithoutIdentityComponent()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// gameobject without networkidentity component should return false
CreateNetworked(out GameObject go, out NetworkIdentity _);
bool result = NetworkBehaviour.SyncVarGameObjectEqual(go, identity.netId);
Assert.That(result, Is.False);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualValidGOWithDifferentNetId()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// gameobject with valid networkidentity and netid that is different
CreateNetworked(out GameObject go, out NetworkIdentity ni);
ni.netId = 43;
bool result = NetworkBehaviour.SyncVarGameObjectEqual(go, identity.netId);
Assert.That(result, Is.False);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualValidGOWithSameNetId()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// gameobject with valid networkidentity and netid that is different
CreateNetworked(out GameObject go, out NetworkIdentity ni);
ni.netId = 42;
bool result = NetworkBehaviour.SyncVarGameObjectEqual(go, identity.netId);
Assert.That(result, Is.True);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualUnspawnedGO()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// gameobject with valid networkidentity and 0 netid that is unspawned
CreateNetworked(out GameObject go, out NetworkIdentity ni);
LogAssert.Expect(LogType.Warning, $"SetSyncVarGameObject GameObject {go} has a zero netId. Maybe it is not spawned yet?");
bool result = NetworkBehaviour.SyncVarGameObjectEqual(go, identity.netId);
Assert.That(result, Is.False);
}
// NOTE: SyncVarGameObjectEqual should be static later
[Test]
public void SyncVarGameObjectEqualUnspawnedGOZeroNetIdIsTrue()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// unspawned go and identity.netid==0 returns true (=equal)
CreateNetworked(out GameObject go, out NetworkIdentity ni);
LogAssert.Expect(LogType.Warning, $"SetSyncVarGameObject GameObject {go} has a zero netId. Maybe it is not spawned yet?");
bool result = NetworkBehaviour.SyncVarGameObjectEqual(go, identity.netId);
Assert.That(result, Is.True);
}
// NOTE: SyncVarNetworkIdentityEqual should be static later
[Test]
public void SyncVarNetworkIdentityEqualZeroNetIdNullIsTrue()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// null and identity.netid==0 returns true (=equal)
//
// later we should reevaluate if this is so smart or not. might be
// better to return false here.
// => we possibly return false so that resync doesn't happen when
// GO disappears? or not?
bool result = NetworkBehaviour.SyncVarNetworkIdentityEqual(null, identity.netId);
Assert.That(result, Is.True);
}
// NOTE: SyncVarNetworkIdentityEqual should be static later
[Test]
public void SyncVarNetworkIdentityEqualNull()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// null should return false
bool result = NetworkBehaviour.SyncVarNetworkIdentityEqual(null, identity.netId);
Assert.That(result, Is.False);
}
// NOTE: SyncVarNetworkIdentityEqual should be static later
[Test]
public void SyncVarNetworkIdentityEqualValidIdentityWithDifferentNetId()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// gameobject with valid networkidentity and netid that is different
CreateNetworked(out GameObject go, out NetworkIdentity ni);
ni.netId = 43;
bool result = NetworkBehaviour.SyncVarNetworkIdentityEqual(ni, identity.netId);
Assert.That(result, Is.False);
}
// NOTE: SyncVarNetworkIdentityEqual should be static later
[Test]
public void SyncVarNetworkIdentityEqualValidIdentityWithSameNetId()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// gameobject with valid networkidentity and netid that is different
CreateNetworked(out GameObject _, out NetworkIdentity ni);
ni.netId = 42;
bool result = NetworkBehaviour.SyncVarNetworkIdentityEqual(ni, identity.netId);
Assert.That(result, Is.True);
}
// NOTE: SyncVarNetworkIdentityEqual should be static later
[Test]
public void SyncVarNetworkIdentityEqualUnspawnedIdentity()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// our identity should have a netid for comparing
identity.netId = 42;
// gameobject with valid networkidentity and 0 netid that is unspawned
CreateNetworked(out GameObject go, out NetworkIdentity ni);
LogAssert.Expect(LogType.Warning, $"SetSyncVarNetworkIdentity NetworkIdentity {ni} has a zero netId. Maybe it is not spawned yet?");
bool result = NetworkBehaviour.SyncVarNetworkIdentityEqual(ni, identity.netId);
Assert.That(result, Is.False);
}
// NOTE: SyncVarNetworkIdentityEqual should be static later
[Test]
public void SyncVarNetworkIdentityEqualUnspawnedIdentityZeroNetIdIsTrue()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// unspawned go and identity.netid==0 returns true (=equal)
CreateNetworked(out GameObject go, out NetworkIdentity ni);
LogAssert.Expect(LogType.Warning, $"SetSyncVarNetworkIdentity NetworkIdentity {ni} has a zero netId. Maybe it is not spawned yet?");
bool result = NetworkBehaviour.SyncVarNetworkIdentityEqual(ni, identity.netId);
Assert.That(result, Is.True);
}
[Test]
public void SetSyncVarGameObjectWithValidObject()
{
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourSetSyncVarGameObjectComponent comp);
// create a valid GameObject with networkidentity and netid
CreateNetworked(out GameObject go, out NetworkIdentity ni);
ni.netId = 43;
// set the GameObject SyncVar
Assert.That(comp.IsDirty(), Is.False);
comp.SetSyncVarGameObjectExposed(go, 1ul);
Assert.That(comp.test, Is.EqualTo(go));
Assert.That(comp.testNetId, Is.EqualTo(ni.netId));
Assert.That(comp.IsDirty(), Is.True);
}
[Test]
public void SetSyncVarGameObjectNull()
{
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourSetSyncVarGameObjectComponent comp);
// set some existing GO+netId first to check if it is going to be
// overwritten
CreateGameObject(out GameObject go);
comp.test = go;
comp.testNetId = 43;
// set the GameObject SyncVar to null
Assert.That(comp.IsDirty(), Is.False);
comp.SetSyncVarGameObjectExposed(null, 1ul);
Assert.That(comp.test, Is.EqualTo(null));
Assert.That(comp.testNetId, Is.EqualTo(0));
Assert.That(comp.IsDirty(), Is.True);
}
[Test]
public void SetSyncVarGameObjectWithoutNetworkIdentity()
{
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourSetSyncVarGameObjectComponent comp);
// set some existing GO+netId first to check if it is going to be
// overwritten
CreateGameObject(out GameObject go);
comp.test = go;
comp.testNetId = 43;
// create test GO with no networkidentity
CreateGameObject(out GameObject test);
// set the GameObject SyncVar to 'test' GO without netidentity.
// -> the way it currently works is that it sets netId to 0, but
// it DOES set gameObjectField to the GameObject without the
// NetworkIdentity, instead of setting it to null because it's
// seemingly invalid.
// -> there might be a deeper reason why UNET did that. perhaps for
// cases where we assign a GameObject before the network was
// fully started and has no netId or networkidentity yet etc.
// => it works, so let's keep it for now
Assert.That(comp.IsDirty(), Is.False);
comp.SetSyncVarGameObjectExposed(test, 1ul);
Assert.That(comp.test, Is.EqualTo(test));
Assert.That(comp.testNetId, Is.EqualTo(0));
Assert.That(comp.IsDirty(), Is.True);
}
[Test]
public void SetSyncVarGameObjectZeroNetId()
{
CreateNetworked(out GameObject gameObject, out NetworkIdentity identity, out NetworkBehaviourSetSyncVarGameObjectComponent comp);
// set some existing GO+netId first to check if it is going to be
// overwritten
CreateGameObject(out GameObject go);
comp.test = go;
comp.testNetId = 43;
// create test GO with networkidentity and zero netid
CreateNetworked(out GameObject test, out NetworkIdentity ni);
Assert.That(ni.netId, Is.EqualTo(0));
// set the GameObject SyncVar to 'test' GO with zero netId.
// -> the way it currently works is that it sets netId to 0, but
// it DOES set gameObjectField to the GameObject without the
// NetworkIdentity, instead of setting it to null because it's
// seemingly invalid.
// -> there might be a deeper reason why UNET did that. perhaps for
// cases where we assign a GameObject before the network was
// fully started and has no netId or networkidentity yet etc.
// => it works, so let's keep it for now
Assert.That(comp.IsDirty(), Is.False);
LogAssert.Expect(LogType.Warning, $"SetSyncVarGameObject GameObject {test} has a zero netId. Maybe it is not spawned yet?");
comp.SetSyncVarGameObjectExposed(test, 1ul);
Assert.That(comp.test, Is.EqualTo(test));
Assert.That(comp.testNetId, Is.EqualTo(0));
Assert.That(comp.IsDirty(), Is.True);
}
[Test]
public void GetSyncVarGameObjectOnServer()
{
CreateNetworked(out GameObject gameObject, out NetworkIdentity identity, out NetworkBehaviourGetSyncVarGameObjectComponent comp);
// call OnStartServer so isServer is true
identity.OnStartServer();
Assert.That(identity.isServer, Is.True);
// create a syncable GameObject
CreateNetworked(out GameObject go, out NetworkIdentity ni);
ni.netId = identity.netId + 1;
// assign it in the component
comp.test = go;
comp.testNetId = ni.netId;
// get it on the server. should simply return the field instead of
// doing any netId lookup like on the client
GameObject result = comp.GetSyncVarGameObjectExposed();
Assert.That(result, Is.EqualTo(go));
// clean up: set isServer false first, otherwise Destroy instead of DestroyImmediate is called
identity.netId = 0;
}
[Test]
public void GetSyncVarGameObjectOnServerNull()
{
CreateNetworked(out GameObject gameObject, out NetworkIdentity identity, out NetworkBehaviourGetSyncVarGameObjectComponent comp);
// call OnStartServer and assign netId so isServer is true
identity.OnStartServer();
Assert.That(identity.isServer, Is.True);
// get it on the server. null should work fine.
GameObject result = comp.GetSyncVarGameObjectExposed();
Assert.That(result, Is.Null);
}
[Test]
public void GetSyncVarGameObjectOnClient()
{
// start server & connect client because we need spawn functions
NetworkServer.Listen(1);
ConnectClientBlockingAuthenticatedAndReady(out _);
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// are we on client and not on server?
identity.isClient = true;
Assert.That(identity.isServer, Is.False);
// create a networked object with test component
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourGetSyncVarGameObjectComponent comp);
// create a spawned, syncable GameObject
// (client tries to look up via netid, so needs to be spawned)
CreateNetworkedAndSpawn(
out GameObject serverGO, out NetworkIdentity serverIdentity,
out GameObject clientGO, out NetworkIdentity clientIdentity);
// assign ONLY netId in the component. assume that GameObject was
// assigned earlier but client walked so far out of range that it
// was despawned on the client. so it's forced to do the netId look-
// up.
Assert.That(comp.test, Is.Null);
comp.testNetId = clientIdentity.netId;
// get it on the client. should look up netId in spawned
GameObject result = comp.GetSyncVarGameObjectExposed();
Assert.That(result, Is.EqualTo(clientGO));
}
[Test]
public void GetSyncVarGameObjectOnClientNull()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity);
// are we on client and not on server?
identity.isClient = true;
Assert.That(identity.isServer, Is.False);
// create a networked object with test component
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourGetSyncVarGameObjectComponent comp);
// get it on the client. null should be supported.
GameObject result = comp.GetSyncVarGameObjectExposed();
Assert.That(result, Is.Null);
}
[Test]
public void SetSyncVarNetworkIdentityWithValidObject()
{
// create a networked object with test component
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourSetSyncVarNetworkIdentityComponent comp);
// create a valid GameObject with networkidentity and netid
CreateNetworked(out GameObject _, out NetworkIdentity ni);
ni.netId = 43;
// set the NetworkIdentity SyncVar
Assert.That(comp.IsDirty(), Is.False);
comp.SetSyncVarNetworkIdentityExposed(ni, 1ul);
Assert.That(comp.test, Is.EqualTo(ni));
Assert.That(comp.testNetId, Is.EqualTo(ni.netId));
Assert.That(comp.IsDirty(), Is.True);
}
[Test]
public void SetSyncVarNetworkIdentityNull()
{
// create a networked object with test component
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourSetSyncVarNetworkIdentityComponent comp);
// set some existing NI+netId first to check if it is going to be
// overwritten
CreateNetworked(out GameObject _, out NetworkIdentity ni);
comp.test = ni;
comp.testNetId = 43;
// set the NetworkIdentity SyncVar to null
Assert.That(comp.IsDirty(), Is.False);
comp.SetSyncVarNetworkIdentityExposed(null, 1ul);
Assert.That(comp.test, Is.EqualTo(null));
Assert.That(comp.testNetId, Is.EqualTo(0));
Assert.That(comp.IsDirty(), Is.True);
}
[Test]
public void SetSyncVarNetworkIdentityZeroNetId()
{
CreateNetworked(out _, out _, out NetworkBehaviourSetSyncVarNetworkIdentityComponent comp);
// set some existing NI+netId first to check if it is going to be
// overwritten
CreateNetworked(out GameObject _, out NetworkIdentity ni);
comp.test = ni;
comp.testNetId = 43;
// create test GO with networkidentity and zero netid
CreateNetworked(out GameObject _, out NetworkIdentity testNi);
Assert.That(testNi.netId, Is.EqualTo(0));
// set the NetworkIdentity SyncVar to 'test' GO with zero netId.
// -> the way it currently works is that it sets netId to 0, but
// it DOES set gameObjectField to the GameObject without the
// NetworkIdentity, instead of setting it to null because it's
// seemingly invalid.
// -> there might be a deeper reason why UNET did that. perhaps for
// cases where we assign a GameObject before the network was
// fully started and has no netId or networkidentity yet etc.
// => it works, so let's keep it for now
Assert.That(comp.IsDirty(), Is.False);
LogAssert.Expect(LogType.Warning, $"SetSyncVarNetworkIdentity NetworkIdentity {testNi} has a zero netId. Maybe it is not spawned yet?");
comp.SetSyncVarNetworkIdentityExposed(testNi, 1ul);
Assert.That(comp.test, Is.EqualTo(testNi));
Assert.That(comp.testNetId, Is.EqualTo(0));
Assert.That(comp.IsDirty(), Is.True);
}
[Test]
public void GetSyncVarNetworkIdentityOnServer()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity, out NetworkBehaviourGetSyncVarNetworkIdentityComponent comp);
// call OnStartServer so isServer is true
identity.OnStartServer();
Assert.That(identity.isServer, Is.True);
// create a syncable GameObject
CreateNetworked(out _, out NetworkIdentity ni);
ni.netId = identity.netId + 1;
// assign it in the component
comp.test = ni;
comp.testNetId = ni.netId;
// get it on the server. should simply return the field instead of
// doing any netId lookup like on the client
NetworkIdentity result = comp.GetSyncVarNetworkIdentityExposed();
Assert.That(result, Is.EqualTo(ni));
}
[Test]
public void GetSyncVarNetworkIdentityOnServerNull()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity, out NetworkBehaviourGetSyncVarNetworkIdentityComponent comp);
// call OnStartServer so isServer is true
identity.OnStartServer();
Assert.That(identity.isServer, Is.True);
// get it on the server. null should work fine.
NetworkIdentity result = comp.GetSyncVarNetworkIdentityExposed();
Assert.That(result, Is.Null);
}
[Test]
public void GetSyncVarNetworkIdentityOnClient()
{
// start server & connect client because we need spawn functions
NetworkServer.Listen(1);
ConnectClientBlockingAuthenticatedAndReady(out _);
CreateNetworked(out GameObject _, out NetworkIdentity identity, out NetworkBehaviourGetSyncVarNetworkIdentityComponent comp);
// are we on client and not on server?
identity.isClient = true;
Assert.That(identity.isServer, Is.False);
// create a spawned, syncable GameObject
// (client tries to look up via netid, so needs to be spawned)
CreateNetworkedAndSpawn(
out _, out NetworkIdentity serverIdentity,
out _, out NetworkIdentity clientIdentity);
// assign ONLY netId in the component. assume that GameObject was
// assigned earlier but client walked so far out of range that it
// was despawned on the client. so it's forced to do the netId look-
// up.
Assert.That(comp.test, Is.Null);
comp.testNetId = clientIdentity.netId;
// get it on the client. should look up netId in spawned
NetworkIdentity result = comp.GetSyncVarNetworkIdentityExposed();
Assert.That(result, Is.EqualTo(clientIdentity));
}
[Test]
public void GetSyncVarNetworkIdentityOnClientNull()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity, out NetworkBehaviourGetSyncVarNetworkIdentityComponent comp);
// are we on client and not on server?
identity.isClient = true;
Assert.That(identity.isServer, Is.False);
// get it on the client. null should be supported.
NetworkIdentity result = comp.GetSyncVarNetworkIdentityExposed();
Assert.That(result, Is.Null);
}
[Test]
public void SerializeAndDeserializeObjectsAll()
{
CreateNetworked(out GameObject _, out NetworkIdentity _, out NetworkBehaviourWithSyncVarsAndCollections comp);
// add values to synclist
comp.list.Add(42);
comp.list.Add(43);
// serialize it
NetworkWriter writer = new NetworkWriter();
comp.SerializeObjectsAll(writer);
// clear original list
comp.list.Clear();
Assert.That(comp.list.Count, Is.EqualTo(0));
// deserialize it
NetworkReader reader = new NetworkReader(writer.ToArray());
comp.DeSerializeObjectsAll(reader);
Assert.That(comp.list.Count, Is.EqualTo(2));
Assert.That(comp.list[0], Is.EqualTo(42));
Assert.That(comp.list[1], Is.EqualTo(43));
}
[Test]
public void SerializeAndDeserializeObjectsDelta()
{
// SyncLists are only set dirty while owner has observers.
// need a connection.
NetworkServer.Listen(1);
ConnectHostClientBlockingAuthenticatedAndReady();
CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out NetworkBehaviourWithSyncVarsAndCollections comp);
// add to synclist
comp.list.Add(42);
comp.list.Add(43);
// serialize it
NetworkWriter writer = new NetworkWriter();
comp.SerializeObjectsDelta(writer);
// clear original list
comp.list.Clear();
Assert.That(comp.list.Count, Is.EqualTo(0));
// deserialize it
NetworkReader reader = new NetworkReader(writer.ToArray());
comp.DeSerializeObjectsDelta(reader);
Assert.That(comp.list.Count, Is.EqualTo(2));
Assert.That(comp.list[0], Is.EqualTo(42));
Assert.That(comp.list[1], Is.EqualTo(43));
}
[Test]
public void OnStopClient()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity, out OnStopClientComponent comp);
identity.OnStopClient();
Assert.That(comp.called, Is.EqualTo(1));
}
[Test]
public void OnStartClient()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity, out OnStartClientComponent comp);
identity.OnStartClient();
Assert.That(comp.called, Is.EqualTo(1));
}
[Test]
public void OnStartLocalPlayer()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity, out OnStartLocalPlayerComponent comp);
identity.OnStartLocalPlayer();
Assert.That(comp.called, Is.EqualTo(1));
}
}
// we need to inherit from networkbehaviour to test protected functions
public class NetworkBehaviourInitSyncObjectTester : NetworkBehaviour
{
[Test]
public void InitSyncObject()
{
SyncObject syncObject = new SyncList<bool>();
InitSyncObject(syncObject);
Assert.That(syncObjects.Count, Is.EqualTo(1));
Assert.That(syncObjects[0], Is.EqualTo(syncObject));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e4164d273d6e4d71bd39cc246f454b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,128 @@
using System;
using NUnit.Framework;
namespace Mirror.Tests
{
public class NetworkClientTests : MirrorEditModeTest
{
[SetUp]
public override void SetUp()
{
base.SetUp();
// we need a server to connect to
NetworkServer.Listen(10);
}
[Test]
public void ServerIp()
{
NetworkClient.ConnectHost();
Assert.That(NetworkClient.serverIp, Is.EqualTo("localhost"));
}
[Test]
public void IsConnected()
{
Assert.That(NetworkClient.isConnected, Is.False);
NetworkClient.ConnectHost();
Assert.That(NetworkClient.isConnected, Is.True);
}
[Test]
public void ConnectUri()
{
NetworkClient.Connect(new Uri("memory://localhost"));
// update transport so connect event is processed
UpdateTransport();
Assert.That(NetworkClient.isConnected, Is.True);
}
[Test]
public void DisconnectInHostMode()
{
NetworkClient.ConnectHost();
Assert.That(NetworkClient.isConnected, Is.True);
Assert.That(NetworkServer.localConnection, !Is.Null);
NetworkClient.Disconnect();
Assert.That(NetworkClient.isConnected, Is.False);
Assert.That(NetworkServer.localConnection, Is.Null);
}
[Test, Ignore("NetworkServerTest.SendClientToServerMessage does it already")]
public void Send() {}
// test to guarantee Disconnect() eventually calls OnClientDisconnected.
// prevents https://github.com/vis2k/Mirror/issues/2818 forever.
// previously there was a bug where:
// - Disconnect() sets state = Disconnected
// - Transport processes it
// - OnTransportDisconnected() early returns because
// state == Disconnected already, so it wouldn't call the event.
[Test]
public void DisconnectCallsOnClientDisconnected_Remote()
{
// setup hook
bool called = false;
NetworkClient.OnDisconnectedEvent = () => called = true;
// connect
ConnectClientBlocking(out _);
// disconnect & process everything
NetworkClient.Disconnect();
UpdateTransport();
// was it called?
Assert.That(called, Is.True);
}
// same as above, but for host mode
// prevents https://github.com/vis2k/Mirror/issues/2818 forever.
[Test]
public void DisconnectCallsOnClientDisconnected_HostMode()
{
// setup hook
bool called = false;
NetworkClient.OnDisconnectedEvent = () => called = true;
// connect host
NetworkClient.ConnectHost();
// disconnect & process everything
NetworkClient.Disconnect();
UpdateTransport();
// was it called?
Assert.That(called, Is.True);
}
[Test]
public void ShutdownCleanup()
{
// add some test event hooks to make sure they are cleaned up.
// there used to be a bug where they wouldn't be cleaned up.
NetworkClient.OnConnectedEvent = () => {};
NetworkClient.OnDisconnectedEvent = () => {};
NetworkClient.Shutdown();
Assert.That(NetworkClient.handlers.Count, Is.EqualTo(0));
Assert.That(NetworkClient.spawned.Count, Is.EqualTo(0));
Assert.That(NetworkClient.spawnableObjects.Count, Is.EqualTo(0));
Assert.That(NetworkClient.connectState, Is.EqualTo(ConnectState.None));
Assert.That(NetworkClient.connection, Is.Null);
Assert.That(NetworkClient.localPlayer, Is.Null);
Assert.That(NetworkClient.ready, Is.False);
Assert.That(NetworkClient.isSpawnFinished, Is.False);
Assert.That(NetworkClient.isLoadingScene, Is.False);
Assert.That(NetworkClient.OnConnectedEvent, Is.Null);
Assert.That(NetworkClient.OnDisconnectedEvent, Is.Null);
Assert.That(NetworkClient.OnErrorEvent, Is.Null);
}
}
}

Some files were not shown because too many files have changed in this diff Show More