diff --git a/Assets/Mirror/Tests.meta b/Assets/Mirror/Tests.meta new file mode 100644 index 000000000..a519cf7ae --- /dev/null +++ b/Assets/Mirror/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4de157ac7e1594c758ce6dc401674f5c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common.meta b/Assets/Mirror/Tests/Common.meta new file mode 100644 index 000000000..cb52e9878 --- /dev/null +++ b/Assets/Mirror/Tests/Common.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd4f310377c5a4ad39bd31546c95c2a2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/Castle.Core.dll b/Assets/Mirror/Tests/Common/Castle.Core.dll new file mode 100644 index 000000000..dbc4b29de Binary files /dev/null and b/Assets/Mirror/Tests/Common/Castle.Core.dll differ diff --git a/Assets/Mirror/Tests/Common/Castle.Core.dll.meta b/Assets/Mirror/Tests/Common/Castle.Core.dll.meta new file mode 100644 index 000000000..5189874d6 --- /dev/null +++ b/Assets/Mirror/Tests/Common/Castle.Core.dll.meta @@ -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: diff --git a/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs b/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs new file mode 100644 index 000000000..2d4a1634e --- /dev/null +++ b/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs @@ -0,0 +1,62 @@ +using System; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; + +namespace Mirror.Tests +{ + + /// + /// Used by both runtime and edit time tests + /// + [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(AssetDatabase.GUIDToAssetPath(guid)); + } + + [OneTimeSetUp] + public void OneTimeSetUp() + { + validPrefab = LoadPrefab(ValidPrefabAssetGuid); + validPrefabNetworkIdentity = validPrefab.GetComponent(); + 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; + } + } +} diff --git a/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs.meta b/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs.meta new file mode 100644 index 000000000..9be790649 --- /dev/null +++ b/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf741fb5970e5e048aea5ff3e396d24c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/ClientSceneTests_RegisterPrefabBase.cs b/Assets/Mirror/Tests/Common/ClientSceneTests_RegisterPrefabBase.cs new file mode 100644 index 000000000..cd7131049 --- /dev/null +++ b/Assets/Mirror/Tests/Common/ClientSceneTests_RegisterPrefabBase.cs @@ -0,0 +1,212 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + /// + /// Used by both runtime and edit time tests + /// + [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)); + } + + /// + /// Allows TestCases to call different overloads for RegisterPrefab. + /// Without this we would need duplicate tests for each overload + /// + [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; + } +} diff --git a/Assets/Mirror/Tests/Common/ClientSceneTests_RegisterPrefabBase.cs.meta b/Assets/Mirror/Tests/Common/ClientSceneTests_RegisterPrefabBase.cs.meta new file mode 100644 index 000000000..8324dc240 --- /dev/null +++ b/Assets/Mirror/Tests/Common/ClientSceneTests_RegisterPrefabBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8eb53689226946640bc49bb962b13638 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/FakeNetworkConnection.cs b/Assets/Mirror/Tests/Common/FakeNetworkConnection.cs new file mode 100644 index 000000000..58ea0a434 --- /dev/null +++ b/Assets/Mirror/Tests/Common/FakeNetworkConnection.cs @@ -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 segment, int channelId = 0) {} + } +} diff --git a/Assets/Mirror/Tests/Common/FakeNetworkConnection.cs.meta b/Assets/Mirror/Tests/Common/FakeNetworkConnection.cs.meta new file mode 100644 index 000000000..129765a98 --- /dev/null +++ b/Assets/Mirror/Tests/Common/FakeNetworkConnection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 935b1eb49c500674eaaf88982952a69e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/MemoryTransport.cs b/Assets/Mirror/Tests/Common/MemoryTransport.cs new file mode 100644 index 000000000..6f0ace3bc --- /dev/null +++ b/Assets/Mirror/Tests/Common/MemoryTransport.cs @@ -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 clientIncoming = new Queue(); + bool serverActive; + public Queue serverIncoming = new Queue(); + + 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 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(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 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(message.data), 0); + break; + case EventType.Disconnected: + Debug.Log("MemoryTransport Server Message: Disconnected"); + OnServerDisconnected.Invoke(message.connectionId); + break; + } + } + } + } +} diff --git a/Assets/Mirror/Tests/Common/MemoryTransport.cs.meta b/Assets/Mirror/Tests/Common/MemoryTransport.cs.meta new file mode 100644 index 000000000..d624b49ff --- /dev/null +++ b/Assets/Mirror/Tests/Common/MemoryTransport.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6131cf1e8a1c14ef5b5253f86f3fc9c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/Mirror.Tests.Common.asmdef b/Assets/Mirror/Tests/Common/Mirror.Tests.Common.asmdef new file mode 100644 index 000000000..ca39e3a96 --- /dev/null +++ b/Assets/Mirror/Tests/Common/Mirror.Tests.Common.asmdef @@ -0,0 +1,16 @@ +{ + "name": "Mirror.Tests.Common", + "references": [ + "Mirror" + ], + "optionalUnityReferences": [ + "TestAssemblies" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Assets/Mirror/Tests/Common/Mirror.Tests.Common.asmdef.meta b/Assets/Mirror/Tests/Common/Mirror.Tests.Common.asmdef.meta new file mode 100644 index 000000000..e06568a88 --- /dev/null +++ b/Assets/Mirror/Tests/Common/Mirror.Tests.Common.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4e9aca8a359ab48de906aedbfa1ffe21 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/MirrorEditModeTest.cs b/Assets/Mirror/Tests/Common/MirrorEditModeTest.cs new file mode 100644 index 000000000..57db52499 --- /dev/null +++ b/Assets/Mirror/Tests/Common/MirrorEditModeTest.cs @@ -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(); + } +} diff --git a/Assets/Mirror/Tests/Common/MirrorEditModeTest.cs.meta b/Assets/Mirror/Tests/Common/MirrorEditModeTest.cs.meta new file mode 100644 index 000000000..2f51f7e36 --- /dev/null +++ b/Assets/Mirror/Tests/Common/MirrorEditModeTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a6110480a9c07423290301aedafb2a93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/MirrorPlayModeTest.cs b/Assets/Mirror/Tests/Common/MirrorPlayModeTest.cs new file mode 100644 index 000000000..a63dc836a --- /dev/null +++ b/Assets/Mirror/Tests/Common/MirrorPlayModeTest.cs @@ -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; + } + } +} diff --git a/Assets/Mirror/Tests/Common/MirrorPlayModeTest.cs.meta b/Assets/Mirror/Tests/Common/MirrorPlayModeTest.cs.meta new file mode 100644 index 000000000..5314431b2 --- /dev/null +++ b/Assets/Mirror/Tests/Common/MirrorPlayModeTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be3f9e24bcdb748728f846e88eea29f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/MirrorTest.cs b/Assets/Mirror/Tests/Common/MirrorTest.cs new file mode 100644 index 000000000..b8b9ccd25 --- /dev/null +++ b/Assets/Mirror/Tests/Common/MirrorTest.cs @@ -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 instantiated; + + // we usually need the memory transport + public MemoryTransport transport; + + public virtual void SetUp() + { + instantiated = new List(); + + // need a transport to send & receive + Transport.activeTransport = transport = new GameObject().AddComponent(); + } + + 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 + // add to tracker list if needed (useful for cleanups afterwards) + protected void CreateGameObject(out GameObject go, out T component) + where T : MonoBehaviour + { + CreateGameObject(out go); + component = go.AddComponent(); + } + + // 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(); + // Awake is only called in play mode. + // call manually for initialization. + identity.Awake(); + // track + instantiated.Add(go); + } + + // create GameObject + NetworkIdentity + NetworkBehaviour + // add to tracker list if needed (useful for cleanups afterwards) + protected void CreateNetworked(out GameObject go, out NetworkIdentity identity, out T component) + where T : NetworkBehaviour + { + go = new GameObject(); + identity = go.AddComponent(); + component = go.AddComponent(); + // 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 + // add to tracker list if needed (useful for cleanups afterwards) + protected void CreateNetworked(out GameObject go, out NetworkIdentity identity, out T componentA, out U componentB) + where T : NetworkBehaviour + where U : NetworkBehaviour + { + go = new GameObject(); + identity = go.AddComponent(); + componentA = go.AddComponent(); + componentB = go.AddComponent(); + // 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 + // add to tracker list if needed (useful for cleanups afterwards) + protected void CreateNetworked(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(); + componentA = go.AddComponent(); + componentB = go.AddComponent(); + componentC = go.AddComponent(); + // 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(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( + 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(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( + 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(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( + 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(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( + 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; + } + } +} diff --git a/Assets/Mirror/Tests/Common/MirrorTest.cs.meta b/Assets/Mirror/Tests/Common/MirrorTest.cs.meta new file mode 100644 index 000000000..d7b13dbe2 --- /dev/null +++ b/Assets/Mirror/Tests/Common/MirrorTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67c5177a4b35749b8b9c4ca7107d8c25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Common/NSubstitute.dll b/Assets/Mirror/Tests/Common/NSubstitute.dll new file mode 100644 index 000000000..010169440 Binary files /dev/null and b/Assets/Mirror/Tests/Common/NSubstitute.dll differ diff --git a/Assets/Mirror/Tests/Common/NSubstitute.dll.meta b/Assets/Mirror/Tests/Common/NSubstitute.dll.meta new file mode 100644 index 000000000..5fe9de787 --- /dev/null +++ b/Assets/Mirror/Tests/Common/NSubstitute.dll.meta @@ -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: diff --git a/Assets/Mirror/Tests/Common/System.Threading.Tasks.Extensions.dll b/Assets/Mirror/Tests/Common/System.Threading.Tasks.Extensions.dll new file mode 100644 index 000000000..a1234ce81 Binary files /dev/null and b/Assets/Mirror/Tests/Common/System.Threading.Tasks.Extensions.dll differ diff --git a/Assets/Mirror/Tests/Common/System.Threading.Tasks.Extensions.dll.meta b/Assets/Mirror/Tests/Common/System.Threading.Tasks.Extensions.dll.meta new file mode 100644 index 000000000..7637cff41 --- /dev/null +++ b/Assets/Mirror/Tests/Common/System.Threading.Tasks.Extensions.dll.meta @@ -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: diff --git a/Assets/Mirror/Tests/Editor.meta b/Assets/Mirror/Tests/Editor.meta new file mode 100644 index 000000000..e516951e1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1d3db979f8f114547977d68180a77080 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Batching.meta b/Assets/Mirror/Tests/Editor/Batching.meta new file mode 100644 index 000000000..76b1f836c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Batching.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6d113040314b4578b93a6f140f064f09 +timeCreated: 1623240703 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Batching/BatcherTests.cs b/Assets/Mirror/Tests/Editor/Batching/BatcherTests.cs new file mode 100644 index 000000000..4c5bc21e8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Batching/BatcherTests.cs @@ -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(message)); + } + + [Test] + public void MakeNextBatch_OnlyAcceptsFreshWriter() + { + batcher.AddMessage(new ArraySegment(new byte[]{0x01})); + + writer.WriteByte(0); + Assert.Throws(() => { + 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(message)); + + // make batch + bool result = batcher.MakeNextBatch(writer, TimeStamp); + Assert.That(result, Is.EqualTo(true)); + + // check result: <> + Assert.That(writer.ToArray().SequenceEqual(ConcatTimestamp(TimeStamp, message))); + } + + [Test] + public void MakeNextBatch_MultipleMessages_AlmostFullBatch() + { + batcher.AddMessage(new ArraySegment(new byte[]{0x01, 0x02})); + batcher.AddMessage(new ArraySegment(new byte[]{0x03})); + + // make batch + bool result = batcher.MakeNextBatch(writer, TimeStamp); + Assert.That(result, Is.EqualTo(true)); + + // check result: <> + 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(new byte[]{0x01, 0x02})); + batcher.AddMessage(new ArraySegment(new byte[]{0x03, 0x04})); + + // make batch + bool result = batcher.MakeNextBatch(writer, TimeStamp); + Assert.That(result, Is.EqualTo(true)); + + // check result: <> + 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(new byte[]{0x01, 0x02})); + batcher.AddMessage(new ArraySegment(new byte[]{0x03, 0x04})); + batcher.AddMessage(new ArraySegment(new byte[]{0x05})); + + // first batch + bool result = batcher.MakeNextBatch(writer, TimeStamp); + Assert.That(result, Is.EqualTo(true)); + + // check result: <> + 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: <> + 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(new byte[]{0x01})); + batcher.AddMessage(new ArraySegment(new byte[]{0x02, 0x03, 0x04, 0x05})); + batcher.AddMessage(new ArraySegment(new byte[]{0x06, 0x07})); + + // first batch + bool result = batcher.MakeNextBatch(writer, TimeStamp); + Assert.That(result, Is.EqualTo(true)); + + // check result: <> + 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: <> + 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: <> + 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(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(new byte[]{0x01})); + batcher.AddMessage(new ArraySegment(new byte[]{0x02})); + batcher.AddMessage(new ArraySegment(large)); + batcher.AddMessage(new ArraySegment(new byte[]{0x03})); + batcher.AddMessage(new ArraySegment(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}))); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Batching/BatcherTests.cs.meta b/Assets/Mirror/Tests/Editor/Batching/BatcherTests.cs.meta new file mode 100644 index 000000000..9d19de00b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Batching/BatcherTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 787d83b7e2ca4547aca251617d91f7d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Batching/UnbatcherTests.cs b/Assets/Mirror/Tests/Editor/Batching/UnbatcherTests.cs new file mode 100644 index 000000000..e4b7e02ab --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Batching/UnbatcherTests.cs @@ -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(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(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(firstBatch)); + + // add second batch + byte[] secondBatch = BatcherTests.ConcatTimestamp(TimeStamp + 1, new byte[]{0x03, 0x04}); + unbatcher.AddBatch(new ArraySegment(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(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(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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Batching/UnbatcherTests.cs.meta b/Assets/Mirror/Tests/Editor/Batching/UnbatcherTests.cs.meta new file mode 100644 index 000000000..b3cf32232 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Batching/UnbatcherTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ccc928bb22f5469886cef8c6132aa717 +timeCreated: 1623240730 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/ClientRpcOverrideTest.cs b/Assets/Mirror/Tests/Editor/ClientRpcOverrideTest.cs new file mode 100644 index 000000000..613b006c9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientRpcOverrideTest.cs @@ -0,0 +1,134 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.RemoteAttrributeTest +{ + class VirtualClientRpc : NetworkBehaviour + { + public event Action onVirtualSendInt; + + [ClientRpc] + public virtual void RpcSendInt(int someInt) => + onVirtualSendInt?.Invoke(someInt); + } + + class VirtualNoOverrideClientRpc : VirtualClientRpc {} + + class VirtualOverrideClientRpc : VirtualClientRpc + { + public event Action onOverrideSendInt; + + [ClientRpc] + public override void RpcSendInt(int someInt) => + onOverrideSendInt?.Invoke(someInt); + } + + class VirtualOverrideClientRpcWithBase : VirtualClientRpc + { + public event Action 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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientRpcOverrideTest.cs.meta b/Assets/Mirror/Tests/Editor/ClientRpcOverrideTest.cs.meta new file mode 100644 index 000000000..68e53470b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientRpcOverrideTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9759f0eddb9731c4b881ac30bacc0de0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientRpcTest.cs b/Assets/Mirror/Tests/Editor/ClientRpcTest.cs new file mode 100644 index 000000000..86e1e47c6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientRpcTest.cs @@ -0,0 +1,155 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.RemoteAttrributeTest +{ + class ClientRpcBehaviour : NetworkBehaviour + { + public event Action onSendInt; + + [ClientRpc] + public void SendInt(int someInt) => + onSendInt?.Invoke(someInt); + } + + class ExcludeOwnerBehaviour : NetworkBehaviour + { + public event Action 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 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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientRpcTest.cs.meta b/Assets/Mirror/Tests/Editor/ClientRpcTest.cs.meta new file mode 100644 index 000000000..36ea9394d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientRpcTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2928376e56382b646975dd00b68c2287 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_ClearSpawners.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_ClearSpawners.cs new file mode 100644 index 000000000..1b6cdbc71 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_ClearSpawners.cs @@ -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); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_ClearSpawners.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_ClearSpawners.cs.meta new file mode 100644 index 000000000..3a955eb07 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_ClearSpawners.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad2fe5633a9652045a5f7eb2643b6956 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_GetPrefab.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_GetPrefab.cs new file mode 100644 index 000000000..9fb848785 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_GetPrefab.cs @@ -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(); + Assert.AreEqual(networkID.assetId, validPrefabGuid); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_GetPrefab.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_GetPrefab.cs.meta new file mode 100644 index 000000000..2dde78154 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_GetPrefab.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27cbff3906928974bb20c3f50eacbbb0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_OnSpawn.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_OnSpawn.cs new file mode 100644 index 000000000..054151d94 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_OnSpawn.cs @@ -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 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); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_OnSpawn.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_OnSpawn.cs.meta new file mode 100644 index 000000000..03d445c2d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_OnSpawn.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b85d949dccc6ab4498da187264323dcc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_PrepareToSpawnSceneObjects.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_PrepareToSpawnSceneObjects.cs new file mode 100644 index 000000000..4f9532c28 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_PrepareToSpawnSceneObjects.cs @@ -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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_PrepareToSpawnSceneObjects.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_PrepareToSpawnSceneObjects.cs.meta new file mode 100644 index 000000000..9652845b1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_PrepareToSpawnSceneObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 546931b1b1c38d5498f2c189e68b63aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterPrefab.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterPrefab.cs new file mode 100644 index 000000000..a63b1f628 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterPrefab.cs @@ -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(); + + 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(); + + 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(); + + 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(); + + 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(); + // 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); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterPrefab.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterPrefab.cs.meta new file mode 100644 index 000000000..803aa0ab5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterPrefab.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bdfdcfafb85b3be418f2085e38663006 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterSpawnHandler.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterSpawnHandler.cs new file mode 100644 index 000000000..fce157b0d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterSpawnHandler.cs @@ -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); + } + + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterSpawnHandler.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterSpawnHandler.cs.meta new file mode 100644 index 000000000..ec826b7c5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_RegisterSpawnHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7e6577e8c2e64e41ae255edc61e91a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterPrefab.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterPrefab.cs new file mode 100644 index 000000000..ec1b7a6a5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterPrefab.cs @@ -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); + } + + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterPrefab.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterPrefab.cs.meta new file mode 100644 index 000000000..8478bffbc --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterPrefab.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aa6fa692d80eaf8419e559c35034f016 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterSpawnHandler.cs b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterSpawnHandler.cs new file mode 100644 index 000000000..7e1f6e3d3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterSpawnHandler.cs @@ -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)); + } + + } +} diff --git a/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterSpawnHandler.cs.meta b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterSpawnHandler.cs.meta new file mode 100644 index 000000000..006dad00a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ClientSceneTests_UnregisterSpawnHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49ef76b8883ba3845942503683c9a9b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/CommandOverrideTest.cs b/Assets/Mirror/Tests/Editor/CommandOverrideTest.cs new file mode 100644 index 000000000..c9c57d54e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CommandOverrideTest.cs @@ -0,0 +1,182 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.RemoteAttrributeTest +{ + class VirtualCommand : NetworkBehaviour + { + public event Action onVirtualSendInt; + + [Command] + public virtual void CmdSendInt(int someInt) => + onVirtualSendInt?.Invoke(someInt); + } + + class VirtualNoOverrideCommand : VirtualCommand {} + + class VirtualOverrideCommand : VirtualCommand + { + public event Action onOverrideSendInt; + + [Command] + public override void CmdSendInt(int someInt) => + onOverrideSendInt?.Invoke(someInt); + } + + class VirtualOverrideCommandWithBase : VirtualCommand + { + public event Action onOverrideSendInt; + + [Command] + public override void CmdSendInt(int someInt) + { + base.CmdSendInt(someInt); + onOverrideSendInt?.Invoke(someInt); + } + } + + // test for 2 overrides + class VirtualOverrideCommandWithBase2 : VirtualOverrideCommandWithBase + { + public event Action 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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/CommandOverrideTest.cs.meta b/Assets/Mirror/Tests/Editor/CommandOverrideTest.cs.meta new file mode 100644 index 000000000..2afb7834e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CommandOverrideTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 881c801c26e90df43b3558a23c96e0ea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/CommandTest.cs b/Assets/Mirror/Tests/Editor/CommandTest.cs new file mode 100644 index 000000000..3c6b4b884 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CommandTest.cs @@ -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 onSendInt; + + [Command] + public void SendInt(int someInt) => + onSendInt?.Invoke(someInt); + } + + class IgnoreAuthorityBehaviour : NetworkBehaviour + { + public event Action onSendInt; + + [Command(requiresAuthority = false)] + public void CmdSendInt(int someInt) => + onSendInt?.Invoke(someInt); + } + + class SenderConnectionBehaviour : NetworkBehaviour + { + public event Action onSendInt; + + [Command] + public void CmdSendInt(int someInt, NetworkConnectionToClient conn = null) => + onSendInt?.Invoke(someInt, conn); + } + + class SenderConnectionIgnoreAuthorityBehaviour : NetworkBehaviour + { + public event Action 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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/CommandTest.cs.meta b/Assets/Mirror/Tests/Editor/CommandTest.cs.meta new file mode 100644 index 000000000..4d4917ddf --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CommandTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1dd3f8a95eee6f74997bc8abcd43a401 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/CompressionTests.cs b/Assets/Mirror/Tests/Editor/CompressionTests.cs new file mode 100644 index 000000000..b03ec36f0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CompressionTests.cs @@ -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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/CompressionTests.cs.meta b/Assets/Mirror/Tests/Editor/CompressionTests.cs.meta new file mode 100644 index 000000000..b1487c00a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CompressionTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 541cf2bc89fe4395b2a50d921c91424a +timeCreated: 1613190697 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/CustomRWTest.cs b/Assets/Mirror/Tests/Editor/CustomRWTest.cs new file mode 100644 index 000000000..3b015b383 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CustomRWTest.cs @@ -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(data); + Assert.That(unpacked.quest.Id, Is.EqualTo(100)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/CustomRWTest.cs.meta b/Assets/Mirror/Tests/Editor/CustomRWTest.cs.meta new file mode 100644 index 000000000..d1adf21d0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/CustomRWTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3d74d53ca2c8c4b1195833376f9f6bb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/EnumReadWriteTests.cs b/Assets/Mirror/Tests/Editor/EnumReadWriteTests.cs new file mode 100644 index 000000000..a37e18245 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/EnumReadWriteTests.cs @@ -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 msg) + where T : struct, NetworkMessage + { + NetworkWriter writer = new NetworkWriter(); + + writer.Write(msg); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + return reader.Read(); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/EnumReadWriteTests.cs.meta b/Assets/Mirror/Tests/Editor/EnumReadWriteTests.cs.meta new file mode 100644 index 000000000..5832f084c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/EnumReadWriteTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 79e6cd90456eed340a72b1bdb6fe7e49 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ExponentialMovingAverageTest.cs b/Assets/Mirror/Tests/Editor/ExponentialMovingAverageTest.cs new file mode 100644 index 000000000..3b55264c4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ExponentialMovingAverageTest.cs @@ -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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ExponentialMovingAverageTest.cs.meta b/Assets/Mirror/Tests/Editor/ExponentialMovingAverageTest.cs.meta new file mode 100644 index 000000000..535f33d77 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ExponentialMovingAverageTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e3f2ecadd13149f29cd3e83ef6a4bff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ExtensionsTest.cs b/Assets/Mirror/Tests/Editor/ExtensionsTest.cs new file mode 100644 index 000000000..ef1e0d8ab --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ExtensionsTest.cs @@ -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 source = new List{1, 2, 3}; + List destination = new List(); + source.CopyTo(destination); + Assert.That(destination.SequenceEqual(source), Is.True); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/ExtensionsTest.cs.meta b/Assets/Mirror/Tests/Editor/ExtensionsTest.cs.meta new file mode 100644 index 000000000..bc31df854 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ExtensionsTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 21e1452d6f734618a9364a2c6c116922 +timeCreated: 1621762116 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs new file mode 100644 index 000000000..8d1ee0fe7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs @@ -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 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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs.meta b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs.meta new file mode 100644 index 000000000..67a428572 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 518288ffe7c7215458a98466131fc7af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Generated.meta b/Assets/Mirror/Tests/Editor/Generated.meta new file mode 100644 index 000000000..be2f418af --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Generated.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f1a04c2c41f19ea46b0b1a33c8f2ae89 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Generated/AttritubeTest.gen.cs b/Assets/Mirror/Tests/Editor/Generated/AttritubeTest.gen.cs new file mode 100644 index 000000000..773567b10 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Generated/AttritubeTest.gen.cs @@ -0,0 +1,5946 @@ +// Generated by AttributeTestGenerator.cs +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Generated.Attributes +{ + public class ClassWithNoConstructor + { + public int a; + } + + public class ClassWithConstructor + { + public int a; + + public ClassWithConstructor(int a) + { + this.a = a; + } + } + + public class AttributeBehaviour_NetworkBehaviour : NetworkBehaviour + { + public static readonly float Expected_float = 2020f; + public static readonly double Expected_double = 2.54; + public static readonly bool Expected_bool = true; + public static readonly char Expected_char = 'a'; + public static readonly byte Expected_byte = 224; + public static readonly int Expected_int = 103; + public static readonly long Expected_long = -123456789L; + public static readonly ulong Expected_ulong = 123456789UL; + public static readonly Vector3 Expected_Vector3 = new Vector3(29, 1, 10); + public static readonly ClassWithNoConstructor Expected_ClassWithNoConstructor = new ClassWithNoConstructor { a = 10 }; + public static readonly ClassWithConstructor Expected_ClassWithConstructor = new ClassWithConstructor(29); + + + [Client] + public float Client_float_Function() + { + return Expected_float; + } + + [Client] + public void Client_float_out_Function(out float value) + { + value = Expected_float; + } + + [Client] + public double Client_double_Function() + { + return Expected_double; + } + + [Client] + public void Client_double_out_Function(out double value) + { + value = Expected_double; + } + + [Client] + public bool Client_bool_Function() + { + return Expected_bool; + } + + [Client] + public void Client_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [Client] + public char Client_char_Function() + { + return Expected_char; + } + + [Client] + public void Client_char_out_Function(out char value) + { + value = Expected_char; + } + + [Client] + public byte Client_byte_Function() + { + return Expected_byte; + } + + [Client] + public void Client_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [Client] + public int Client_int_Function() + { + return Expected_int; + } + + [Client] + public void Client_int_out_Function(out int value) + { + value = Expected_int; + } + + [Client] + public long Client_long_Function() + { + return Expected_long; + } + + [Client] + public void Client_long_out_Function(out long value) + { + value = Expected_long; + } + + [Client] + public ulong Client_ulong_Function() + { + return Expected_ulong; + } + + [Client] + public void Client_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [Client] + public Vector3 Client_Vector3_Function() + { + return Expected_Vector3; + } + + [Client] + public void Client_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [Client] + public ClassWithNoConstructor Client_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [Client] + public void Client_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [Client] + public ClassWithConstructor Client_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [Client] + public void Client_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [Server] + public float Server_float_Function() + { + return Expected_float; + } + + [Server] + public void Server_float_out_Function(out float value) + { + value = Expected_float; + } + + [Server] + public double Server_double_Function() + { + return Expected_double; + } + + [Server] + public void Server_double_out_Function(out double value) + { + value = Expected_double; + } + + [Server] + public bool Server_bool_Function() + { + return Expected_bool; + } + + [Server] + public void Server_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [Server] + public char Server_char_Function() + { + return Expected_char; + } + + [Server] + public void Server_char_out_Function(out char value) + { + value = Expected_char; + } + + [Server] + public byte Server_byte_Function() + { + return Expected_byte; + } + + [Server] + public void Server_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [Server] + public int Server_int_Function() + { + return Expected_int; + } + + [Server] + public void Server_int_out_Function(out int value) + { + value = Expected_int; + } + + [Server] + public long Server_long_Function() + { + return Expected_long; + } + + [Server] + public void Server_long_out_Function(out long value) + { + value = Expected_long; + } + + [Server] + public ulong Server_ulong_Function() + { + return Expected_ulong; + } + + [Server] + public void Server_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [Server] + public Vector3 Server_Vector3_Function() + { + return Expected_Vector3; + } + + [Server] + public void Server_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [Server] + public ClassWithNoConstructor Server_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [Server] + public void Server_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [Server] + public ClassWithConstructor Server_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [Server] + public void Server_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [ClientCallback] + public float ClientCallback_float_Function() + { + return Expected_float; + } + + [ClientCallback] + public void ClientCallback_float_out_Function(out float value) + { + value = Expected_float; + } + + [ClientCallback] + public double ClientCallback_double_Function() + { + return Expected_double; + } + + [ClientCallback] + public void ClientCallback_double_out_Function(out double value) + { + value = Expected_double; + } + + [ClientCallback] + public bool ClientCallback_bool_Function() + { + return Expected_bool; + } + + [ClientCallback] + public void ClientCallback_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [ClientCallback] + public char ClientCallback_char_Function() + { + return Expected_char; + } + + [ClientCallback] + public void ClientCallback_char_out_Function(out char value) + { + value = Expected_char; + } + + [ClientCallback] + public byte ClientCallback_byte_Function() + { + return Expected_byte; + } + + [ClientCallback] + public void ClientCallback_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [ClientCallback] + public int ClientCallback_int_Function() + { + return Expected_int; + } + + [ClientCallback] + public void ClientCallback_int_out_Function(out int value) + { + value = Expected_int; + } + + [ClientCallback] + public long ClientCallback_long_Function() + { + return Expected_long; + } + + [ClientCallback] + public void ClientCallback_long_out_Function(out long value) + { + value = Expected_long; + } + + [ClientCallback] + public ulong ClientCallback_ulong_Function() + { + return Expected_ulong; + } + + [ClientCallback] + public void ClientCallback_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [ClientCallback] + public Vector3 ClientCallback_Vector3_Function() + { + return Expected_Vector3; + } + + [ClientCallback] + public void ClientCallback_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [ClientCallback] + public ClassWithNoConstructor ClientCallback_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [ClientCallback] + public void ClientCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [ClientCallback] + public ClassWithConstructor ClientCallback_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [ClientCallback] + public void ClientCallback_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [ServerCallback] + public float ServerCallback_float_Function() + { + return Expected_float; + } + + [ServerCallback] + public void ServerCallback_float_out_Function(out float value) + { + value = Expected_float; + } + + [ServerCallback] + public double ServerCallback_double_Function() + { + return Expected_double; + } + + [ServerCallback] + public void ServerCallback_double_out_Function(out double value) + { + value = Expected_double; + } + + [ServerCallback] + public bool ServerCallback_bool_Function() + { + return Expected_bool; + } + + [ServerCallback] + public void ServerCallback_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [ServerCallback] + public char ServerCallback_char_Function() + { + return Expected_char; + } + + [ServerCallback] + public void ServerCallback_char_out_Function(out char value) + { + value = Expected_char; + } + + [ServerCallback] + public byte ServerCallback_byte_Function() + { + return Expected_byte; + } + + [ServerCallback] + public void ServerCallback_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [ServerCallback] + public int ServerCallback_int_Function() + { + return Expected_int; + } + + [ServerCallback] + public void ServerCallback_int_out_Function(out int value) + { + value = Expected_int; + } + + [ServerCallback] + public long ServerCallback_long_Function() + { + return Expected_long; + } + + [ServerCallback] + public void ServerCallback_long_out_Function(out long value) + { + value = Expected_long; + } + + [ServerCallback] + public ulong ServerCallback_ulong_Function() + { + return Expected_ulong; + } + + [ServerCallback] + public void ServerCallback_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [ServerCallback] + public Vector3 ServerCallback_Vector3_Function() + { + return Expected_Vector3; + } + + [ServerCallback] + public void ServerCallback_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [ServerCallback] + public ClassWithNoConstructor ServerCallback_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [ServerCallback] + public void ServerCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [ServerCallback] + public ClassWithConstructor ServerCallback_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [ServerCallback] + public void ServerCallback_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + } + + + public class AttributeTest_NetworkBehaviour + { + AttributeBehaviour_NetworkBehaviour behaviour; + GameObject go; + + [OneTimeSetUp] + public void SetUp() + { + go = new GameObject(); + behaviour = go.AddComponent(); + } + + [OneTimeTearDown] + public void TearDown() + { + UnityEngine.Object.DestroyImmediate(go); + NetworkClient.connectState = ConnectState.None; + NetworkServer.active = false; + } + + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_float_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Single Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_float_Function()' called when client was not active"); + } + float actual = behaviour.Client_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_float_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_float_out_Function(System.Single&)' called when client was not active"); + } + behaviour.Client_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_double_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Double Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_double_Function()' called when client was not active"); + } + double actual = behaviour.Client_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_double_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_double_out_Function(System.Double&)' called when client was not active"); + } + behaviour.Client_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_bool_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Boolean Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_bool_Function()' called when client was not active"); + } + bool actual = behaviour.Client_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_bool_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_bool_out_Function(System.Boolean&)' called when client was not active"); + } + behaviour.Client_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_char_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Char Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_char_Function()' called when client was not active"); + } + char actual = behaviour.Client_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_char_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_char_out_Function(System.Char&)' called when client was not active"); + } + behaviour.Client_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_byte_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Byte Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_byte_Function()' called when client was not active"); + } + byte actual = behaviour.Client_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_byte_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_byte_out_Function(System.Byte&)' called when client was not active"); + } + behaviour.Client_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_int_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Int32 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_int_Function()' called when client was not active"); + } + int actual = behaviour.Client_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_int_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_int_out_Function(System.Int32&)' called when client was not active"); + } + behaviour.Client_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_long_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Int64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_long_Function()' called when client was not active"); + } + long actual = behaviour.Client_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_long_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_long_out_Function(System.Int64&)' called when client was not active"); + } + behaviour.Client_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ulong_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.UInt64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_ulong_Function()' called when client was not active"); + } + ulong actual = behaviour.Client_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ulong_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_ulong_out_Function(System.UInt64&)' called when client was not active"); + } + behaviour.Client_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_Vector3_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'UnityEngine.Vector3 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_Vector3_Function()' called when client was not active"); + } + Vector3 actual = behaviour.Client_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_Vector3_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_Vector3_out_Function(UnityEngine.Vector3&)' called when client was not active"); + } + behaviour.Client_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'Mirror.Tests.Generated.Attributes.ClassWithNoConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_ClassWithNoConstructor_Function()' called when client was not active"); + } + ClassWithNoConstructor actual = behaviour.Client_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_ClassWithNoConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithNoConstructor&)' called when client was not active"); + } + behaviour.Client_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'Mirror.Tests.Generated.Attributes.ClassWithConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_ClassWithConstructor_Function()' called when client was not active"); + } + ClassWithConstructor actual = behaviour.Client_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Client_ClassWithConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithConstructor&)' called when client was not active"); + } + behaviour.Client_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_float_returnsValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Single Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_float_Function()' called when server was not active"); + } + float actual = behaviour.Server_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_float_setsOutValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_float_out_Function(System.Single&)' called when server was not active"); + } + behaviour.Server_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_double_returnsValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Double Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_double_Function()' called when server was not active"); + } + double actual = behaviour.Server_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_double_setsOutValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_double_out_Function(System.Double&)' called when server was not active"); + } + behaviour.Server_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_bool_returnsValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Boolean Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_bool_Function()' called when server was not active"); + } + bool actual = behaviour.Server_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_bool_setsOutValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_bool_out_Function(System.Boolean&)' called when server was not active"); + } + behaviour.Server_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_char_returnsValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Char Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_char_Function()' called when server was not active"); + } + char actual = behaviour.Server_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_char_setsOutValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_char_out_Function(System.Char&)' called when server was not active"); + } + behaviour.Server_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_byte_returnsValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Byte Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_byte_Function()' called when server was not active"); + } + byte actual = behaviour.Server_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_byte_setsOutValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_byte_out_Function(System.Byte&)' called when server was not active"); + } + behaviour.Server_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_int_returnsValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Int32 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_int_Function()' called when server was not active"); + } + int actual = behaviour.Server_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_int_setsOutValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_int_out_Function(System.Int32&)' called when server was not active"); + } + behaviour.Server_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_long_returnsValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Int64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_long_Function()' called when server was not active"); + } + long actual = behaviour.Server_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_long_setsOutValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_long_out_Function(System.Int64&)' called when server was not active"); + } + behaviour.Server_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ulong_returnsValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.UInt64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_ulong_Function()' called when server was not active"); + } + ulong actual = behaviour.Server_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ulong_setsOutValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_ulong_out_Function(System.UInt64&)' called when server was not active"); + } + behaviour.Server_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_Vector3_returnsValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'UnityEngine.Vector3 Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_Vector3_Function()' called when server was not active"); + } + Vector3 actual = behaviour.Server_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_Vector3_setsOutValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_Vector3_out_Function(UnityEngine.Vector3&)' called when server was not active"); + } + behaviour.Server_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'Mirror.Tests.Generated.Attributes.ClassWithNoConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_ClassWithNoConstructor_Function()' called when server was not active"); + } + ClassWithNoConstructor actual = behaviour.Server_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_ClassWithNoConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithNoConstructor&)' called when server was not active"); + } + behaviour.Server_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'Mirror.Tests.Generated.Attributes.ClassWithConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_ClassWithConstructor_Function()' called when server was not active"); + } + ClassWithConstructor actual = behaviour.Server_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_NetworkBehaviour::Server_ClassWithConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithConstructor&)' called when server was not active"); + } + behaviour.Server_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_float_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + float actual = behaviour.ClientCallback_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_float_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + behaviour.ClientCallback_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_double_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + double actual = behaviour.ClientCallback_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_double_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + behaviour.ClientCallback_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_bool_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + bool actual = behaviour.ClientCallback_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_bool_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + behaviour.ClientCallback_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_char_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + char actual = behaviour.ClientCallback_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_char_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + behaviour.ClientCallback_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_byte_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + byte actual = behaviour.ClientCallback_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_byte_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + behaviour.ClientCallback_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_int_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + int actual = behaviour.ClientCallback_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_int_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + behaviour.ClientCallback_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_long_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + long actual = behaviour.ClientCallback_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_long_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + behaviour.ClientCallback_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ulong_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + ulong actual = behaviour.ClientCallback_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ulong_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + behaviour.ClientCallback_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_Vector3_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + Vector3 actual = behaviour.ClientCallback_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_Vector3_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + behaviour.ClientCallback_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + ClassWithNoConstructor actual = behaviour.ClientCallback_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + behaviour.ClientCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + ClassWithConstructor actual = behaviour.ClientCallback_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + behaviour.ClientCallback_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_float_returnsValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + float actual = behaviour.ServerCallback_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_float_setsOutValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_float : default; + + behaviour.ServerCallback_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_double_returnsValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + double actual = behaviour.ServerCallback_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_double_setsOutValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_double : default; + + behaviour.ServerCallback_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_bool_returnsValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + bool actual = behaviour.ServerCallback_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_bool_setsOutValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_bool : default; + + behaviour.ServerCallback_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_char_returnsValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + char actual = behaviour.ServerCallback_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_char_setsOutValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_char : default; + + behaviour.ServerCallback_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_byte_returnsValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + byte actual = behaviour.ServerCallback_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_byte_setsOutValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_byte : default; + + behaviour.ServerCallback_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_int_returnsValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + int actual = behaviour.ServerCallback_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_int_setsOutValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_int : default; + + behaviour.ServerCallback_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_long_returnsValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + long actual = behaviour.ServerCallback_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_long_setsOutValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_long : default; + + behaviour.ServerCallback_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ulong_returnsValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + ulong actual = behaviour.ServerCallback_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ulong_setsOutValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ulong : default; + + behaviour.ServerCallback_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_Vector3_returnsValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + Vector3 actual = behaviour.ServerCallback_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_Vector3_setsOutValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_Vector3 : default; + + behaviour.ServerCallback_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + ClassWithNoConstructor actual = behaviour.ServerCallback_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithNoConstructor : default; + + behaviour.ServerCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + ClassWithConstructor actual = behaviour.ServerCallback_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_NetworkBehaviour.Expected_ClassWithConstructor : default; + + behaviour.ServerCallback_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + } + + public class AttributeBehaviour_MonoBehaviour : MonoBehaviour + { + public static readonly float Expected_float = 2020f; + public static readonly double Expected_double = 2.54; + public static readonly bool Expected_bool = true; + public static readonly char Expected_char = 'a'; + public static readonly byte Expected_byte = 224; + public static readonly int Expected_int = 103; + public static readonly long Expected_long = -123456789L; + public static readonly ulong Expected_ulong = 123456789UL; + public static readonly Vector3 Expected_Vector3 = new Vector3(29, 1, 10); + public static readonly ClassWithNoConstructor Expected_ClassWithNoConstructor = new ClassWithNoConstructor { a = 10 }; + public static readonly ClassWithConstructor Expected_ClassWithConstructor = new ClassWithConstructor(29); + + + [Client] + public float Client_float_Function() + { + return Expected_float; + } + + [Client] + public void Client_float_out_Function(out float value) + { + value = Expected_float; + } + + [Client] + public double Client_double_Function() + { + return Expected_double; + } + + [Client] + public void Client_double_out_Function(out double value) + { + value = Expected_double; + } + + [Client] + public bool Client_bool_Function() + { + return Expected_bool; + } + + [Client] + public void Client_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [Client] + public char Client_char_Function() + { + return Expected_char; + } + + [Client] + public void Client_char_out_Function(out char value) + { + value = Expected_char; + } + + [Client] + public byte Client_byte_Function() + { + return Expected_byte; + } + + [Client] + public void Client_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [Client] + public int Client_int_Function() + { + return Expected_int; + } + + [Client] + public void Client_int_out_Function(out int value) + { + value = Expected_int; + } + + [Client] + public long Client_long_Function() + { + return Expected_long; + } + + [Client] + public void Client_long_out_Function(out long value) + { + value = Expected_long; + } + + [Client] + public ulong Client_ulong_Function() + { + return Expected_ulong; + } + + [Client] + public void Client_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [Client] + public Vector3 Client_Vector3_Function() + { + return Expected_Vector3; + } + + [Client] + public void Client_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [Client] + public ClassWithNoConstructor Client_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [Client] + public void Client_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [Client] + public ClassWithConstructor Client_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [Client] + public void Client_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [Server] + public float Server_float_Function() + { + return Expected_float; + } + + [Server] + public void Server_float_out_Function(out float value) + { + value = Expected_float; + } + + [Server] + public double Server_double_Function() + { + return Expected_double; + } + + [Server] + public void Server_double_out_Function(out double value) + { + value = Expected_double; + } + + [Server] + public bool Server_bool_Function() + { + return Expected_bool; + } + + [Server] + public void Server_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [Server] + public char Server_char_Function() + { + return Expected_char; + } + + [Server] + public void Server_char_out_Function(out char value) + { + value = Expected_char; + } + + [Server] + public byte Server_byte_Function() + { + return Expected_byte; + } + + [Server] + public void Server_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [Server] + public int Server_int_Function() + { + return Expected_int; + } + + [Server] + public void Server_int_out_Function(out int value) + { + value = Expected_int; + } + + [Server] + public long Server_long_Function() + { + return Expected_long; + } + + [Server] + public void Server_long_out_Function(out long value) + { + value = Expected_long; + } + + [Server] + public ulong Server_ulong_Function() + { + return Expected_ulong; + } + + [Server] + public void Server_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [Server] + public Vector3 Server_Vector3_Function() + { + return Expected_Vector3; + } + + [Server] + public void Server_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [Server] + public ClassWithNoConstructor Server_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [Server] + public void Server_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [Server] + public ClassWithConstructor Server_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [Server] + public void Server_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [ClientCallback] + public float ClientCallback_float_Function() + { + return Expected_float; + } + + [ClientCallback] + public void ClientCallback_float_out_Function(out float value) + { + value = Expected_float; + } + + [ClientCallback] + public double ClientCallback_double_Function() + { + return Expected_double; + } + + [ClientCallback] + public void ClientCallback_double_out_Function(out double value) + { + value = Expected_double; + } + + [ClientCallback] + public bool ClientCallback_bool_Function() + { + return Expected_bool; + } + + [ClientCallback] + public void ClientCallback_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [ClientCallback] + public char ClientCallback_char_Function() + { + return Expected_char; + } + + [ClientCallback] + public void ClientCallback_char_out_Function(out char value) + { + value = Expected_char; + } + + [ClientCallback] + public byte ClientCallback_byte_Function() + { + return Expected_byte; + } + + [ClientCallback] + public void ClientCallback_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [ClientCallback] + public int ClientCallback_int_Function() + { + return Expected_int; + } + + [ClientCallback] + public void ClientCallback_int_out_Function(out int value) + { + value = Expected_int; + } + + [ClientCallback] + public long ClientCallback_long_Function() + { + return Expected_long; + } + + [ClientCallback] + public void ClientCallback_long_out_Function(out long value) + { + value = Expected_long; + } + + [ClientCallback] + public ulong ClientCallback_ulong_Function() + { + return Expected_ulong; + } + + [ClientCallback] + public void ClientCallback_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [ClientCallback] + public Vector3 ClientCallback_Vector3_Function() + { + return Expected_Vector3; + } + + [ClientCallback] + public void ClientCallback_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [ClientCallback] + public ClassWithNoConstructor ClientCallback_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [ClientCallback] + public void ClientCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [ClientCallback] + public ClassWithConstructor ClientCallback_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [ClientCallback] + public void ClientCallback_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [ServerCallback] + public float ServerCallback_float_Function() + { + return Expected_float; + } + + [ServerCallback] + public void ServerCallback_float_out_Function(out float value) + { + value = Expected_float; + } + + [ServerCallback] + public double ServerCallback_double_Function() + { + return Expected_double; + } + + [ServerCallback] + public void ServerCallback_double_out_Function(out double value) + { + value = Expected_double; + } + + [ServerCallback] + public bool ServerCallback_bool_Function() + { + return Expected_bool; + } + + [ServerCallback] + public void ServerCallback_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [ServerCallback] + public char ServerCallback_char_Function() + { + return Expected_char; + } + + [ServerCallback] + public void ServerCallback_char_out_Function(out char value) + { + value = Expected_char; + } + + [ServerCallback] + public byte ServerCallback_byte_Function() + { + return Expected_byte; + } + + [ServerCallback] + public void ServerCallback_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [ServerCallback] + public int ServerCallback_int_Function() + { + return Expected_int; + } + + [ServerCallback] + public void ServerCallback_int_out_Function(out int value) + { + value = Expected_int; + } + + [ServerCallback] + public long ServerCallback_long_Function() + { + return Expected_long; + } + + [ServerCallback] + public void ServerCallback_long_out_Function(out long value) + { + value = Expected_long; + } + + [ServerCallback] + public ulong ServerCallback_ulong_Function() + { + return Expected_ulong; + } + + [ServerCallback] + public void ServerCallback_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [ServerCallback] + public Vector3 ServerCallback_Vector3_Function() + { + return Expected_Vector3; + } + + [ServerCallback] + public void ServerCallback_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [ServerCallback] + public ClassWithNoConstructor ServerCallback_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [ServerCallback] + public void ServerCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [ServerCallback] + public ClassWithConstructor ServerCallback_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [ServerCallback] + public void ServerCallback_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + } + + + public class AttributeTest_MonoBehaviour + { + AttributeBehaviour_MonoBehaviour behaviour; + GameObject go; + + [OneTimeSetUp] + public void SetUp() + { + go = new GameObject(); + behaviour = go.AddComponent(); + } + + [OneTimeTearDown] + public void TearDown() + { + UnityEngine.Object.DestroyImmediate(go); + NetworkClient.connectState = ConnectState.None; + NetworkServer.active = false; + } + + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_float_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Single Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_float_Function()' called when client was not active"); + } + float actual = behaviour.Client_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_float_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_float_out_Function(System.Single&)' called when client was not active"); + } + behaviour.Client_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_double_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Double Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_double_Function()' called when client was not active"); + } + double actual = behaviour.Client_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_double_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_double_out_Function(System.Double&)' called when client was not active"); + } + behaviour.Client_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_bool_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Boolean Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_bool_Function()' called when client was not active"); + } + bool actual = behaviour.Client_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_bool_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_bool_out_Function(System.Boolean&)' called when client was not active"); + } + behaviour.Client_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_char_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Char Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_char_Function()' called when client was not active"); + } + char actual = behaviour.Client_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_char_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_char_out_Function(System.Char&)' called when client was not active"); + } + behaviour.Client_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_byte_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Byte Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_byte_Function()' called when client was not active"); + } + byte actual = behaviour.Client_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_byte_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_byte_out_Function(System.Byte&)' called when client was not active"); + } + behaviour.Client_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_int_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Int32 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_int_Function()' called when client was not active"); + } + int actual = behaviour.Client_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_int_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_int_out_Function(System.Int32&)' called when client was not active"); + } + behaviour.Client_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_long_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Int64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_long_Function()' called when client was not active"); + } + long actual = behaviour.Client_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_long_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_long_out_Function(System.Int64&)' called when client was not active"); + } + behaviour.Client_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ulong_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.UInt64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_ulong_Function()' called when client was not active"); + } + ulong actual = behaviour.Client_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ulong_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_ulong_out_Function(System.UInt64&)' called when client was not active"); + } + behaviour.Client_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_Vector3_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'UnityEngine.Vector3 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_Vector3_Function()' called when client was not active"); + } + Vector3 actual = behaviour.Client_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_Vector3_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_Vector3_out_Function(UnityEngine.Vector3&)' called when client was not active"); + } + behaviour.Client_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'Mirror.Tests.Generated.Attributes.ClassWithNoConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_ClassWithNoConstructor_Function()' called when client was not active"); + } + ClassWithNoConstructor actual = behaviour.Client_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_ClassWithNoConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithNoConstructor&)' called when client was not active"); + } + behaviour.Client_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'Mirror.Tests.Generated.Attributes.ClassWithConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_ClassWithConstructor_Function()' called when client was not active"); + } + ClassWithConstructor actual = behaviour.Client_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Client_ClassWithConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithConstructor&)' called when client was not active"); + } + behaviour.Client_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_float_returnsValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Single Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_float_Function()' called when server was not active"); + } + float actual = behaviour.Server_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_float_setsOutValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_float_out_Function(System.Single&)' called when server was not active"); + } + behaviour.Server_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_double_returnsValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Double Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_double_Function()' called when server was not active"); + } + double actual = behaviour.Server_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_double_setsOutValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_double_out_Function(System.Double&)' called when server was not active"); + } + behaviour.Server_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_bool_returnsValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Boolean Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_bool_Function()' called when server was not active"); + } + bool actual = behaviour.Server_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_bool_setsOutValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_bool_out_Function(System.Boolean&)' called when server was not active"); + } + behaviour.Server_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_char_returnsValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Char Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_char_Function()' called when server was not active"); + } + char actual = behaviour.Server_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_char_setsOutValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_char_out_Function(System.Char&)' called when server was not active"); + } + behaviour.Server_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_byte_returnsValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Byte Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_byte_Function()' called when server was not active"); + } + byte actual = behaviour.Server_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_byte_setsOutValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_byte_out_Function(System.Byte&)' called when server was not active"); + } + behaviour.Server_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_int_returnsValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Int32 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_int_Function()' called when server was not active"); + } + int actual = behaviour.Server_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_int_setsOutValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_int_out_Function(System.Int32&)' called when server was not active"); + } + behaviour.Server_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_long_returnsValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Int64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_long_Function()' called when server was not active"); + } + long actual = behaviour.Server_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_long_setsOutValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_long_out_Function(System.Int64&)' called when server was not active"); + } + behaviour.Server_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ulong_returnsValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.UInt64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_ulong_Function()' called when server was not active"); + } + ulong actual = behaviour.Server_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ulong_setsOutValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_ulong_out_Function(System.UInt64&)' called when server was not active"); + } + behaviour.Server_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_Vector3_returnsValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'UnityEngine.Vector3 Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_Vector3_Function()' called when server was not active"); + } + Vector3 actual = behaviour.Server_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_Vector3_setsOutValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_Vector3_out_Function(UnityEngine.Vector3&)' called when server was not active"); + } + behaviour.Server_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'Mirror.Tests.Generated.Attributes.ClassWithNoConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_ClassWithNoConstructor_Function()' called when server was not active"); + } + ClassWithNoConstructor actual = behaviour.Server_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_ClassWithNoConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithNoConstructor&)' called when server was not active"); + } + behaviour.Server_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'Mirror.Tests.Generated.Attributes.ClassWithConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_ClassWithConstructor_Function()' called when server was not active"); + } + ClassWithConstructor actual = behaviour.Server_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_MonoBehaviour::Server_ClassWithConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithConstructor&)' called when server was not active"); + } + behaviour.Server_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_float_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + float actual = behaviour.ClientCallback_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_float_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + behaviour.ClientCallback_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_double_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + double actual = behaviour.ClientCallback_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_double_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + behaviour.ClientCallback_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_bool_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + bool actual = behaviour.ClientCallback_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_bool_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + behaviour.ClientCallback_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_char_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + char actual = behaviour.ClientCallback_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_char_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + behaviour.ClientCallback_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_byte_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + byte actual = behaviour.ClientCallback_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_byte_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + behaviour.ClientCallback_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_int_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + int actual = behaviour.ClientCallback_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_int_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + behaviour.ClientCallback_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_long_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + long actual = behaviour.ClientCallback_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_long_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + behaviour.ClientCallback_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ulong_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + ulong actual = behaviour.ClientCallback_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ulong_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + behaviour.ClientCallback_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_Vector3_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + Vector3 actual = behaviour.ClientCallback_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_Vector3_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + behaviour.ClientCallback_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + ClassWithNoConstructor actual = behaviour.ClientCallback_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + behaviour.ClientCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + ClassWithConstructor actual = behaviour.ClientCallback_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + behaviour.ClientCallback_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_float_returnsValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + float actual = behaviour.ServerCallback_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_float_setsOutValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_MonoBehaviour.Expected_float : default; + + behaviour.ServerCallback_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_double_returnsValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + double actual = behaviour.ServerCallback_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_double_setsOutValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_MonoBehaviour.Expected_double : default; + + behaviour.ServerCallback_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_bool_returnsValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + bool actual = behaviour.ServerCallback_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_bool_setsOutValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_MonoBehaviour.Expected_bool : default; + + behaviour.ServerCallback_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_char_returnsValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + char actual = behaviour.ServerCallback_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_char_setsOutValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_MonoBehaviour.Expected_char : default; + + behaviour.ServerCallback_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_byte_returnsValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + byte actual = behaviour.ServerCallback_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_byte_setsOutValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_MonoBehaviour.Expected_byte : default; + + behaviour.ServerCallback_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_int_returnsValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + int actual = behaviour.ServerCallback_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_int_setsOutValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_MonoBehaviour.Expected_int : default; + + behaviour.ServerCallback_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_long_returnsValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + long actual = behaviour.ServerCallback_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_long_setsOutValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_MonoBehaviour.Expected_long : default; + + behaviour.ServerCallback_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ulong_returnsValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + ulong actual = behaviour.ServerCallback_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ulong_setsOutValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ulong : default; + + behaviour.ServerCallback_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_Vector3_returnsValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + Vector3 actual = behaviour.ServerCallback_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_Vector3_setsOutValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_MonoBehaviour.Expected_Vector3 : default; + + behaviour.ServerCallback_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + ClassWithNoConstructor actual = behaviour.ServerCallback_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithNoConstructor : default; + + behaviour.ServerCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + ClassWithConstructor actual = behaviour.ServerCallback_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_MonoBehaviour.Expected_ClassWithConstructor : default; + + behaviour.ServerCallback_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + } + + public class AttributeBehaviour_ClassWithNoConstructor : ClassWithNoConstructor + { + public static readonly float Expected_float = 2020f; + public static readonly double Expected_double = 2.54; + public static readonly bool Expected_bool = true; + public static readonly char Expected_char = 'a'; + public static readonly byte Expected_byte = 224; + public static readonly int Expected_int = 103; + public static readonly long Expected_long = -123456789L; + public static readonly ulong Expected_ulong = 123456789UL; + public static readonly Vector3 Expected_Vector3 = new Vector3(29, 1, 10); + public static readonly ClassWithNoConstructor Expected_ClassWithNoConstructor = new ClassWithNoConstructor { a = 10 }; + public static readonly ClassWithConstructor Expected_ClassWithConstructor = new ClassWithConstructor(29); + + + [Client] + public float Client_float_Function() + { + return Expected_float; + } + + [Client] + public void Client_float_out_Function(out float value) + { + value = Expected_float; + } + + [Client] + public double Client_double_Function() + { + return Expected_double; + } + + [Client] + public void Client_double_out_Function(out double value) + { + value = Expected_double; + } + + [Client] + public bool Client_bool_Function() + { + return Expected_bool; + } + + [Client] + public void Client_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [Client] + public char Client_char_Function() + { + return Expected_char; + } + + [Client] + public void Client_char_out_Function(out char value) + { + value = Expected_char; + } + + [Client] + public byte Client_byte_Function() + { + return Expected_byte; + } + + [Client] + public void Client_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [Client] + public int Client_int_Function() + { + return Expected_int; + } + + [Client] + public void Client_int_out_Function(out int value) + { + value = Expected_int; + } + + [Client] + public long Client_long_Function() + { + return Expected_long; + } + + [Client] + public void Client_long_out_Function(out long value) + { + value = Expected_long; + } + + [Client] + public ulong Client_ulong_Function() + { + return Expected_ulong; + } + + [Client] + public void Client_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [Client] + public Vector3 Client_Vector3_Function() + { + return Expected_Vector3; + } + + [Client] + public void Client_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [Client] + public ClassWithNoConstructor Client_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [Client] + public void Client_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [Client] + public ClassWithConstructor Client_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [Client] + public void Client_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [Server] + public float Server_float_Function() + { + return Expected_float; + } + + [Server] + public void Server_float_out_Function(out float value) + { + value = Expected_float; + } + + [Server] + public double Server_double_Function() + { + return Expected_double; + } + + [Server] + public void Server_double_out_Function(out double value) + { + value = Expected_double; + } + + [Server] + public bool Server_bool_Function() + { + return Expected_bool; + } + + [Server] + public void Server_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [Server] + public char Server_char_Function() + { + return Expected_char; + } + + [Server] + public void Server_char_out_Function(out char value) + { + value = Expected_char; + } + + [Server] + public byte Server_byte_Function() + { + return Expected_byte; + } + + [Server] + public void Server_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [Server] + public int Server_int_Function() + { + return Expected_int; + } + + [Server] + public void Server_int_out_Function(out int value) + { + value = Expected_int; + } + + [Server] + public long Server_long_Function() + { + return Expected_long; + } + + [Server] + public void Server_long_out_Function(out long value) + { + value = Expected_long; + } + + [Server] + public ulong Server_ulong_Function() + { + return Expected_ulong; + } + + [Server] + public void Server_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [Server] + public Vector3 Server_Vector3_Function() + { + return Expected_Vector3; + } + + [Server] + public void Server_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [Server] + public ClassWithNoConstructor Server_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [Server] + public void Server_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [Server] + public ClassWithConstructor Server_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [Server] + public void Server_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [ClientCallback] + public float ClientCallback_float_Function() + { + return Expected_float; + } + + [ClientCallback] + public void ClientCallback_float_out_Function(out float value) + { + value = Expected_float; + } + + [ClientCallback] + public double ClientCallback_double_Function() + { + return Expected_double; + } + + [ClientCallback] + public void ClientCallback_double_out_Function(out double value) + { + value = Expected_double; + } + + [ClientCallback] + public bool ClientCallback_bool_Function() + { + return Expected_bool; + } + + [ClientCallback] + public void ClientCallback_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [ClientCallback] + public char ClientCallback_char_Function() + { + return Expected_char; + } + + [ClientCallback] + public void ClientCallback_char_out_Function(out char value) + { + value = Expected_char; + } + + [ClientCallback] + public byte ClientCallback_byte_Function() + { + return Expected_byte; + } + + [ClientCallback] + public void ClientCallback_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [ClientCallback] + public int ClientCallback_int_Function() + { + return Expected_int; + } + + [ClientCallback] + public void ClientCallback_int_out_Function(out int value) + { + value = Expected_int; + } + + [ClientCallback] + public long ClientCallback_long_Function() + { + return Expected_long; + } + + [ClientCallback] + public void ClientCallback_long_out_Function(out long value) + { + value = Expected_long; + } + + [ClientCallback] + public ulong ClientCallback_ulong_Function() + { + return Expected_ulong; + } + + [ClientCallback] + public void ClientCallback_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [ClientCallback] + public Vector3 ClientCallback_Vector3_Function() + { + return Expected_Vector3; + } + + [ClientCallback] + public void ClientCallback_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [ClientCallback] + public ClassWithNoConstructor ClientCallback_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [ClientCallback] + public void ClientCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [ClientCallback] + public ClassWithConstructor ClientCallback_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [ClientCallback] + public void ClientCallback_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + + [ServerCallback] + public float ServerCallback_float_Function() + { + return Expected_float; + } + + [ServerCallback] + public void ServerCallback_float_out_Function(out float value) + { + value = Expected_float; + } + + [ServerCallback] + public double ServerCallback_double_Function() + { + return Expected_double; + } + + [ServerCallback] + public void ServerCallback_double_out_Function(out double value) + { + value = Expected_double; + } + + [ServerCallback] + public bool ServerCallback_bool_Function() + { + return Expected_bool; + } + + [ServerCallback] + public void ServerCallback_bool_out_Function(out bool value) + { + value = Expected_bool; + } + + [ServerCallback] + public char ServerCallback_char_Function() + { + return Expected_char; + } + + [ServerCallback] + public void ServerCallback_char_out_Function(out char value) + { + value = Expected_char; + } + + [ServerCallback] + public byte ServerCallback_byte_Function() + { + return Expected_byte; + } + + [ServerCallback] + public void ServerCallback_byte_out_Function(out byte value) + { + value = Expected_byte; + } + + [ServerCallback] + public int ServerCallback_int_Function() + { + return Expected_int; + } + + [ServerCallback] + public void ServerCallback_int_out_Function(out int value) + { + value = Expected_int; + } + + [ServerCallback] + public long ServerCallback_long_Function() + { + return Expected_long; + } + + [ServerCallback] + public void ServerCallback_long_out_Function(out long value) + { + value = Expected_long; + } + + [ServerCallback] + public ulong ServerCallback_ulong_Function() + { + return Expected_ulong; + } + + [ServerCallback] + public void ServerCallback_ulong_out_Function(out ulong value) + { + value = Expected_ulong; + } + + [ServerCallback] + public Vector3 ServerCallback_Vector3_Function() + { + return Expected_Vector3; + } + + [ServerCallback] + public void ServerCallback_Vector3_out_Function(out Vector3 value) + { + value = Expected_Vector3; + } + + [ServerCallback] + public ClassWithNoConstructor ServerCallback_ClassWithNoConstructor_Function() + { + return Expected_ClassWithNoConstructor; + } + + [ServerCallback] + public void ServerCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor value) + { + value = Expected_ClassWithNoConstructor; + } + + [ServerCallback] + public ClassWithConstructor ServerCallback_ClassWithConstructor_Function() + { + return Expected_ClassWithConstructor; + } + + [ServerCallback] + public void ServerCallback_ClassWithConstructor_out_Function(out ClassWithConstructor value) + { + value = Expected_ClassWithConstructor; + } + } + + + public class AttributeTest_ClassWithNoConstructor + { + AttributeBehaviour_ClassWithNoConstructor behaviour; + GameObject go; + + [OneTimeSetUp] + public void SetUp() + { + behaviour = new AttributeBehaviour_ClassWithNoConstructor(); + } + + [OneTimeTearDown] + public void TearDown() + { + + NetworkClient.connectState = ConnectState.None; + NetworkServer.active = false; + } + + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_float_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Single Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_float_Function()' called when client was not active"); + } + float actual = behaviour.Client_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_float_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_float_out_Function(System.Single&)' called when client was not active"); + } + behaviour.Client_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_double_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Double Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_double_Function()' called when client was not active"); + } + double actual = behaviour.Client_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_double_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_double_out_Function(System.Double&)' called when client was not active"); + } + behaviour.Client_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_bool_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Boolean Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_bool_Function()' called when client was not active"); + } + bool actual = behaviour.Client_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_bool_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_bool_out_Function(System.Boolean&)' called when client was not active"); + } + behaviour.Client_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_char_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Char Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_char_Function()' called when client was not active"); + } + char actual = behaviour.Client_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_char_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_char_out_Function(System.Char&)' called when client was not active"); + } + behaviour.Client_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_byte_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Byte Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_byte_Function()' called when client was not active"); + } + byte actual = behaviour.Client_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_byte_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_byte_out_Function(System.Byte&)' called when client was not active"); + } + behaviour.Client_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_int_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Int32 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_int_Function()' called when client was not active"); + } + int actual = behaviour.Client_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_int_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_int_out_Function(System.Int32&)' called when client was not active"); + } + behaviour.Client_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_long_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Int64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_long_Function()' called when client was not active"); + } + long actual = behaviour.Client_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_long_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_long_out_Function(System.Int64&)' called when client was not active"); + } + behaviour.Client_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ulong_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.UInt64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_ulong_Function()' called when client was not active"); + } + ulong actual = behaviour.Client_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ulong_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_ulong_out_Function(System.UInt64&)' called when client was not active"); + } + behaviour.Client_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_Vector3_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'UnityEngine.Vector3 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_Vector3_Function()' called when client was not active"); + } + Vector3 actual = behaviour.Client_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_Vector3_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_Vector3_out_Function(UnityEngine.Vector3&)' called when client was not active"); + } + behaviour.Client_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'Mirror.Tests.Generated.Attributes.ClassWithNoConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_ClassWithNoConstructor_Function()' called when client was not active"); + } + ClassWithNoConstructor actual = behaviour.Client_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_ClassWithNoConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithNoConstructor&)' called when client was not active"); + } + behaviour.Client_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'Mirror.Tests.Generated.Attributes.ClassWithConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_ClassWithConstructor_Function()' called when client was not active"); + } + ClassWithConstructor actual = behaviour.Client_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Client_ClassWithConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Client] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Client_ClassWithConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithConstructor&)' called when client was not active"); + } + behaviour.Client_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_float_returnsValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Single Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_float_Function()' called when server was not active"); + } + float actual = behaviour.Server_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_float_setsOutValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_float_out_Function(System.Single&)' called when server was not active"); + } + behaviour.Server_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_double_returnsValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Double Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_double_Function()' called when server was not active"); + } + double actual = behaviour.Server_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_double_setsOutValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_double_out_Function(System.Double&)' called when server was not active"); + } + behaviour.Server_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_bool_returnsValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Boolean Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_bool_Function()' called when server was not active"); + } + bool actual = behaviour.Server_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_bool_setsOutValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_bool_out_Function(System.Boolean&)' called when server was not active"); + } + behaviour.Server_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_char_returnsValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Char Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_char_Function()' called when server was not active"); + } + char actual = behaviour.Server_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_char_setsOutValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_char_out_Function(System.Char&)' called when server was not active"); + } + behaviour.Server_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_byte_returnsValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Byte Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_byte_Function()' called when server was not active"); + } + byte actual = behaviour.Server_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_byte_setsOutValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_byte_out_Function(System.Byte&)' called when server was not active"); + } + behaviour.Server_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_int_returnsValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Int32 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_int_Function()' called when server was not active"); + } + int actual = behaviour.Server_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_int_setsOutValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_int_out_Function(System.Int32&)' called when server was not active"); + } + behaviour.Server_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_long_returnsValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Int64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_long_Function()' called when server was not active"); + } + long actual = behaviour.Server_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_long_setsOutValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_long_out_Function(System.Int64&)' called when server was not active"); + } + behaviour.Server_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ulong_returnsValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.UInt64 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_ulong_Function()' called when server was not active"); + } + ulong actual = behaviour.Server_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ulong_setsOutValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_ulong_out_Function(System.UInt64&)' called when server was not active"); + } + behaviour.Server_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_Vector3_returnsValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'UnityEngine.Vector3 Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_Vector3_Function()' called when server was not active"); + } + Vector3 actual = behaviour.Server_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_Vector3_setsOutValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_Vector3_out_Function(UnityEngine.Vector3&)' called when server was not active"); + } + behaviour.Server_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'Mirror.Tests.Generated.Attributes.ClassWithNoConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_ClassWithNoConstructor_Function()' called when server was not active"); + } + ClassWithNoConstructor actual = behaviour.Server_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_ClassWithNoConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithNoConstructor&)' called when server was not active"); + } + behaviour.Server_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'Mirror.Tests.Generated.Attributes.ClassWithConstructor Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_ClassWithConstructor_Function()' called when server was not active"); + } + ClassWithConstructor actual = behaviour.Server_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Server_ClassWithConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + if (!active) + { + LogAssert.Expect(LogType.Warning, "[Server] function 'System.Void Mirror.Tests.Generated.Attributes.AttributeBehaviour_ClassWithNoConstructor::Server_ClassWithConstructor_out_Function(Mirror.Tests.Generated.Attributes.ClassWithConstructor&)' called when server was not active"); + } + behaviour.Server_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_float_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + float actual = behaviour.ClientCallback_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_float_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + behaviour.ClientCallback_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_double_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + double actual = behaviour.ClientCallback_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_double_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + behaviour.ClientCallback_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_bool_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + bool actual = behaviour.ClientCallback_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_bool_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + behaviour.ClientCallback_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_char_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + char actual = behaviour.ClientCallback_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_char_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + behaviour.ClientCallback_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_byte_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + byte actual = behaviour.ClientCallback_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_byte_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + behaviour.ClientCallback_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_int_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + int actual = behaviour.ClientCallback_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_int_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + behaviour.ClientCallback_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_long_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + long actual = behaviour.ClientCallback_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_long_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + behaviour.ClientCallback_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ulong_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + ulong actual = behaviour.ClientCallback_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ulong_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + behaviour.ClientCallback_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_Vector3_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + Vector3 actual = behaviour.ClientCallback_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_Vector3_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + behaviour.ClientCallback_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + ClassWithNoConstructor actual = behaviour.ClientCallback_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + behaviour.ClientCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithConstructor_returnsValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + ClassWithConstructor actual = behaviour.ClientCallback_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ClientCallback_ClassWithConstructor_setsOutValue(bool active) + { + NetworkClient.connectState = active ? ConnectState.Connected : ConnectState.None; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + behaviour.ClientCallback_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_float_returnsValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + float actual = behaviour.ServerCallback_float_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_float_setsOutValue(bool active) + { + NetworkServer.active = active; + + float expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_float : default; + + behaviour.ServerCallback_float_out_Function(out float actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_double_returnsValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + double actual = behaviour.ServerCallback_double_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_double_setsOutValue(bool active) + { + NetworkServer.active = active; + + double expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_double : default; + + behaviour.ServerCallback_double_out_Function(out double actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_bool_returnsValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + bool actual = behaviour.ServerCallback_bool_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_bool_setsOutValue(bool active) + { + NetworkServer.active = active; + + bool expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_bool : default; + + behaviour.ServerCallback_bool_out_Function(out bool actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_char_returnsValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + char actual = behaviour.ServerCallback_char_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_char_setsOutValue(bool active) + { + NetworkServer.active = active; + + char expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_char : default; + + behaviour.ServerCallback_char_out_Function(out char actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_byte_returnsValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + byte actual = behaviour.ServerCallback_byte_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_byte_setsOutValue(bool active) + { + NetworkServer.active = active; + + byte expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_byte : default; + + behaviour.ServerCallback_byte_out_Function(out byte actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_int_returnsValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + int actual = behaviour.ServerCallback_int_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_int_setsOutValue(bool active) + { + NetworkServer.active = active; + + int expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_int : default; + + behaviour.ServerCallback_int_out_Function(out int actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_long_returnsValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + long actual = behaviour.ServerCallback_long_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_long_setsOutValue(bool active) + { + NetworkServer.active = active; + + long expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_long : default; + + behaviour.ServerCallback_long_out_Function(out long actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ulong_returnsValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + ulong actual = behaviour.ServerCallback_ulong_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ulong_setsOutValue(bool active) + { + NetworkServer.active = active; + + ulong expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ulong : default; + + behaviour.ServerCallback_ulong_out_Function(out ulong actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_Vector3_returnsValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + Vector3 actual = behaviour.ServerCallback_Vector3_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_Vector3_setsOutValue(bool active) + { + NetworkServer.active = active; + + Vector3 expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_Vector3 : default; + + behaviour.ServerCallback_Vector3_out_Function(out Vector3 actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithNoConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + ClassWithNoConstructor actual = behaviour.ServerCallback_ClassWithNoConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithNoConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithNoConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithNoConstructor : default; + + behaviour.ServerCallback_ClassWithNoConstructor_out_Function(out ClassWithNoConstructor actual); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithConstructor_returnsValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + ClassWithConstructor actual = behaviour.ServerCallback_ClassWithConstructor_Function(); + + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void ServerCallback_ClassWithConstructor_setsOutValue(bool active) + { + NetworkServer.active = active; + + ClassWithConstructor expected = active ? AttributeBehaviour_ClassWithNoConstructor.Expected_ClassWithConstructor : default; + + behaviour.ServerCallback_ClassWithConstructor_out_Function(out ClassWithConstructor actual); + + Assert.AreEqual(expected, actual); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Generated/AttritubeTest.gen.cs.meta b/Assets/Mirror/Tests/Editor/Generated/AttritubeTest.gen.cs.meta new file mode 100644 index 000000000..3e05bedb0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Generated/AttritubeTest.gen.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e49c298f22d4292439dc17f7e59f08b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Generated/CollectionWriterTests.gen.cs b/Assets/Mirror/Tests/Editor/Generated/CollectionWriterTests.gen.cs new file mode 100644 index 000000000..081b7f4c0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Generated/CollectionWriterTests.gen.cs @@ -0,0 +1,1049 @@ +// Generated by CollectionWriterGenerator.cs +using System; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.Generated.CollectionWriters +{ + public struct FloatStringStruct + { + public float value; + public string anotherValue; + } + + public class ClassWithNoConstructor + { + public int a; + } + + public class Array_int_Test + { + public struct Message : NetworkMessage + { + public int[] collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + int[] unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new int[] {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + int[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new int[] + { + 3, 4, 5 + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + int[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo(3)); + Assert.That(unpackedCollection[1], Is.EqualTo(4)); + Assert.That(unpackedCollection[2], Is.EqualTo(5)); + } + } + + public class Array_string_Test + { + public struct Message : NetworkMessage + { + public string[] collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + string[] unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new string[] {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + string[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new string[] + { + "Some", "String", "Value" + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + string[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo("Some")); + Assert.That(unpackedCollection[1], Is.EqualTo("String")); + Assert.That(unpackedCollection[2], Is.EqualTo("Value")); + } + } + + public class Array_Vector3_Test + { + public struct Message : NetworkMessage + { + public Vector3[] collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + Vector3[] unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new Vector3[] {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + Vector3[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new Vector3[] + { + new Vector3(1, 2, 3), new Vector3(4, 5, 6), new Vector3(7, 8, 9) + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + Vector3[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo(new Vector3(1, 2, 3))); + Assert.That(unpackedCollection[1], Is.EqualTo(new Vector3(4, 5, 6))); + Assert.That(unpackedCollection[2], Is.EqualTo(new Vector3(7, 8, 9))); + } + } + + public class Array_FloatStringStruct_Test + { + public struct Message : NetworkMessage + { + public FloatStringStruct[] collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + FloatStringStruct[] unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new FloatStringStruct[] {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + FloatStringStruct[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new FloatStringStruct[] + { + new FloatStringStruct { value = 3, anotherValue = "Some" }, new FloatStringStruct { value = 4, anotherValue = "String" }, new FloatStringStruct { value = 5, anotherValue = "Values" } + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + FloatStringStruct[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo(new FloatStringStruct { value = 3, anotherValue = "Some" })); + Assert.That(unpackedCollection[1], Is.EqualTo(new FloatStringStruct { value = 4, anotherValue = "String" })); + Assert.That(unpackedCollection[2], Is.EqualTo(new FloatStringStruct { value = 5, anotherValue = "Values" })); + } + } + + public class Array_ClassWithNoConstructor_Test + { + public struct Message : NetworkMessage + { + public ClassWithNoConstructor[] collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ClassWithNoConstructor[] unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new ClassWithNoConstructor[] {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ClassWithNoConstructor[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new ClassWithNoConstructor[] + { + new ClassWithNoConstructor { a = 3 }, new ClassWithNoConstructor { a = 4 }, new ClassWithNoConstructor { a = 5 } + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ClassWithNoConstructor[] unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0].a, Is.EqualTo(new ClassWithNoConstructor { a = 3 }.a)); + Assert.That(unpackedCollection[1].a, Is.EqualTo(new ClassWithNoConstructor { a = 4 }.a)); + Assert.That(unpackedCollection[2].a, Is.EqualTo(new ClassWithNoConstructor { a = 5 }.a)); + } + } + + public class ArraySegment_int_Test + { + public struct Message : NetworkMessage + { + public ArraySegment collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection.Array, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + int[] array = new int[] + { + default, + default, + default, + }; + + Message message = new Message + { + collection = new ArraySegment(array, 0, 0) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsEmpty(unpackedCollection.Array); + } + + [Test] + public void SendsData() + { + int[] array = new int[] + { + default, + 3, 4, 5, + default, + default, + default, + }; + + + Message message = new Message + { + collection = new ArraySegment(array, 1, 3) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsNotEmpty(unpackedCollection.Array); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 0], Is.EqualTo(3)); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 1], Is.EqualTo(4)); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 2], Is.EqualTo(5)); + } + } + + public class ArraySegment_string_Test + { + public struct Message : NetworkMessage + { + public ArraySegment collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection.Array, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + string[] array = new string[] + { + default, + default, + default, + }; + + Message message = new Message + { + collection = new ArraySegment(array, 0, 0) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsEmpty(unpackedCollection.Array); + } + + [Test] + public void SendsData() + { + string[] array = new string[] + { + default, + "Some", "String", "Value", + default, + default, + default, + }; + + + Message message = new Message + { + collection = new ArraySegment(array, 1, 3) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsNotEmpty(unpackedCollection.Array); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 0], Is.EqualTo("Some")); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 1], Is.EqualTo("String")); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 2], Is.EqualTo("Value")); + } + } + + public class ArraySegment_Vector3_Test + { + public struct Message : NetworkMessage + { + public ArraySegment collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection.Array, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Vector3[] array = new Vector3[] + { + default, + default, + default, + }; + + Message message = new Message + { + collection = new ArraySegment(array, 0, 0) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsEmpty(unpackedCollection.Array); + } + + [Test] + public void SendsData() + { + Vector3[] array = new Vector3[] + { + default, + new Vector3(1, 2, 3), new Vector3(4, 5, 6), new Vector3(7, 8, 9), + default, + default, + default, + }; + + + Message message = new Message + { + collection = new ArraySegment(array, 1, 3) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsNotEmpty(unpackedCollection.Array); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 0], Is.EqualTo(new Vector3(1, 2, 3))); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 1], Is.EqualTo(new Vector3(4, 5, 6))); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 2], Is.EqualTo(new Vector3(7, 8, 9))); + } + } + + public class ArraySegment_FloatStringStruct_Test + { + public struct Message : NetworkMessage + { + public ArraySegment collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection.Array, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + FloatStringStruct[] array = new FloatStringStruct[] + { + default, + default, + default, + }; + + Message message = new Message + { + collection = new ArraySegment(array, 0, 0) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsEmpty(unpackedCollection.Array); + } + + [Test] + public void SendsData() + { + FloatStringStruct[] array = new FloatStringStruct[] + { + default, + new FloatStringStruct { value = 3, anotherValue = "Some" }, new FloatStringStruct { value = 4, anotherValue = "String" }, new FloatStringStruct { value = 5, anotherValue = "Values" }, + default, + default, + default, + }; + + + Message message = new Message + { + collection = new ArraySegment(array, 1, 3) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsNotEmpty(unpackedCollection.Array); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 0], Is.EqualTo(new FloatStringStruct { value = 3, anotherValue = "Some" })); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 1], Is.EqualTo(new FloatStringStruct { value = 4, anotherValue = "String" })); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 2], Is.EqualTo(new FloatStringStruct { value = 5, anotherValue = "Values" })); + } + } + + public class ArraySegment_ClassWithNoConstructor_Test + { + public struct Message : NetworkMessage + { + public ArraySegment collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection.Array, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + ClassWithNoConstructor[] array = new ClassWithNoConstructor[] + { + default, + default, + default, + }; + + Message message = new Message + { + collection = new ArraySegment(array, 0, 0) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsEmpty(unpackedCollection.Array); + } + + [Test] + public void SendsData() + { + ClassWithNoConstructor[] array = new ClassWithNoConstructor[] + { + default, + new ClassWithNoConstructor { a = 3 }, new ClassWithNoConstructor { a = 4 }, new ClassWithNoConstructor { a = 5 }, + default, + default, + default, + }; + + + Message message = new Message + { + collection = new ArraySegment(array, 1, 3) + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + ArraySegment unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection.Array); + Assert.IsNotEmpty(unpackedCollection.Array); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 0].a, Is.EqualTo(new ClassWithNoConstructor { a = 3 }.a)); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 1].a, Is.EqualTo(new ClassWithNoConstructor { a = 4 }.a)); + Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 2].a, Is.EqualTo(new ClassWithNoConstructor { a = 5 }.a)); + } + } + + public class List_int_Test + { + public struct Message : NetworkMessage + { + public List collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new List {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new List + { + 3, 4, 5 + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo(3)); + Assert.That(unpackedCollection[1], Is.EqualTo(4)); + Assert.That(unpackedCollection[2], Is.EqualTo(5)); + } + } + + public class List_string_Test + { + public struct Message : NetworkMessage + { + public List collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new List {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new List + { + "Some", "String", "Value" + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo("Some")); + Assert.That(unpackedCollection[1], Is.EqualTo("String")); + Assert.That(unpackedCollection[2], Is.EqualTo("Value")); + } + } + + public class List_Vector3_Test + { + public struct Message : NetworkMessage + { + public List collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new List {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new List + { + new Vector3(1, 2, 3), new Vector3(4, 5, 6), new Vector3(7, 8, 9) + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo(new Vector3(1, 2, 3))); + Assert.That(unpackedCollection[1], Is.EqualTo(new Vector3(4, 5, 6))); + Assert.That(unpackedCollection[2], Is.EqualTo(new Vector3(7, 8, 9))); + } + } + + public class List_FloatStringStruct_Test + { + public struct Message : NetworkMessage + { + public List collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new List {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new List + { + new FloatStringStruct { value = 3, anotherValue = "Some" }, new FloatStringStruct { value = 4, anotherValue = "String" }, new FloatStringStruct { value = 5, anotherValue = "Values" } + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0], Is.EqualTo(new FloatStringStruct { value = 3, anotherValue = "Some" })); + Assert.That(unpackedCollection[1], Is.EqualTo(new FloatStringStruct { value = 4, anotherValue = "String" })); + Assert.That(unpackedCollection[2], Is.EqualTo(new FloatStringStruct { value = 5, anotherValue = "Values" })); + } + } + + public class List_ClassWithNoConstructor_Test + { + public struct Message : NetworkMessage + { + public List collection; + } + + [Test] + public void SendsNull() + { + Message message = new Message + { + collection = default + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.That(unpackedCollection, Is.Null.Or.Empty); + } + + [Test] + public void SendsEmpty() + { + Message message = new Message + { + collection = new List {} + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsEmpty(unpackedCollection); + } + + [Test] + public void SendsData() + { + Message message = new Message + { + collection = new List + { + new ClassWithNoConstructor { a = 3 }, new ClassWithNoConstructor { a = 4 }, new ClassWithNoConstructor { a = 5 } + } + }; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + Message unpacked = MessagePackingTest.UnpackFromByteArray(data); + List unpackedCollection = unpacked.collection; + + Assert.IsNotNull(unpackedCollection); + Assert.IsNotEmpty(unpackedCollection); + Assert.That(unpackedCollection[0].a, Is.EqualTo(new ClassWithNoConstructor { a = 3 }.a)); + Assert.That(unpackedCollection[1].a, Is.EqualTo(new ClassWithNoConstructor { a = 4 }.a)); + Assert.That(unpackedCollection[2].a, Is.EqualTo(new ClassWithNoConstructor { a = 5 }.a)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Generated/CollectionWriterTests.gen.cs.meta b/Assets/Mirror/Tests/Editor/Generated/CollectionWriterTests.gen.cs.meta new file mode 100644 index 000000000..28a873611 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Generated/CollectionWriterTests.gen.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c573b90d8949dec4cafb4f7401be9950 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Grid2DTests.cs b/Assets/Mirror/Tests/Editor/Grid2DTests.cs new file mode 100644 index 000000000..b3a608542 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Grid2DTests.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + public class Grid2DTests + { + Grid2D grid = new Grid2D(); + + [Test] + public void AddAndGetNeighbours() + { + // add two at (0, 0) + grid.Add(Vector2Int.zero, 1); + grid.Add(Vector2Int.zero, 2); + HashSet result = new HashSet(); + 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 result = new HashSet(); + 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 result = new HashSet(); + grid.GetWithNeighbours(Vector2Int.zero, result); + Assert.That(result.Count, Is.EqualTo(0)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Grid2DTests.cs.meta b/Assets/Mirror/Tests/Editor/Grid2DTests.cs.meta new file mode 100644 index 000000000..2f71bbdc1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Grid2DTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f8f3c4f37bb54824b5dfe70e0984b3d3 +timeCreated: 1613188745 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_Common.cs b/Assets/Mirror/Tests/Editor/InterestManagementTests_Common.cs new file mode 100644 index 000000000..8b57a8966 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_Common.cs @@ -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(); + } +} diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_Common.cs.meta b/Assets/Mirror/Tests/Editor/InterestManagementTests_Common.cs.meta new file mode 100644 index 000000000..fa5a3884a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_Common.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f0349d2e3a74454e9dc8badec1db0289 +timeCreated: 1613049868 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_Default.cs b/Assets/Mirror/Tests/Editor/InterestManagementTests_Default.cs new file mode 100644 index 000000000..f33505d40 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_Default.cs @@ -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. + } +} diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_Default.cs.meta b/Assets/Mirror/Tests/Editor/InterestManagementTests_Default.cs.meta new file mode 100644 index 000000000..96ed6142a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_Default.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c5ac1fc2c25043378f80bc02d868a5d6 +timeCreated: 1613042576 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_Distance.cs b/Assets/Mirror/Tests/Editor/InterestManagementTests_Distance.cs new file mode 100644 index 000000000..9d8cf35a6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_Distance.cs @@ -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. + } +} diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_Distance.cs.meta b/Assets/Mirror/Tests/Editor/InterestManagementTests_Distance.cs.meta new file mode 100644 index 000000000..269736062 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_Distance.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ffb9dc0ff01e4f979c216984d7fc48d0 +timeCreated: 1613049838 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_SpatialHashing.cs b/Assets/Mirror/Tests/Editor/InterestManagementTests_SpatialHashing.cs new file mode 100644 index 000000000..78a3b9090 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_SpatialHashing.cs @@ -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. + } +} diff --git a/Assets/Mirror/Tests/Editor/InterestManagementTests_SpatialHashing.cs.meta b/Assets/Mirror/Tests/Editor/InterestManagementTests_SpatialHashing.cs.meta new file mode 100644 index 000000000..8f145f0a4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/InterestManagementTests_SpatialHashing.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 63d9e0d6aa9a4eec8b4e2db07e6261bf +timeCreated: 1613117841 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/LocalConnectionTest.cs b/Assets/Mirror/Tests/Editor/LocalConnectionTest.cs new file mode 100644 index 000000000..9fe986b94 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/LocalConnectionTest.cs @@ -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(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(Handler, false); + + connectionToClient.Send(new TestMessage()); + connectionToServer.Update(); + + Assert.True(invoked, "handler should have been invoked"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/LocalConnectionTest.cs.meta b/Assets/Mirror/Tests/Editor/LocalConnectionTest.cs.meta new file mode 100644 index 000000000..a1a525792 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/LocalConnectionTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 405841e2a21c64d7585d5c71d06ffff2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/MessageInheritanceTest.cs b/Assets/Mirror/Tests/Editor/MessageInheritanceTest.cs new file mode 100644 index 000000000..89a84b7a5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MessageInheritanceTest.cs @@ -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(); + + 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(); + + 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(); + + 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}"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/MessageInheritanceTest.cs.meta b/Assets/Mirror/Tests/Editor/MessageInheritanceTest.cs.meta new file mode 100644 index 000000000..4e78acae8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MessageInheritanceTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 808855b645f9843d2b3077ab1304b2b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/MessagePackingTest.cs b/Assets/Mirror/Tests/Editor/MessagePackingTest.cs new file mode 100644 index 000000000..570edb435 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MessagePackingTest.cs @@ -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 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(byte[] data) + where T : struct, NetworkMessage + { + using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(data)) + { + int msgType = MessagePacking.GetId(); + + int id = networkReader.ReadUShort(); + if (id != msgType) + throw new FormatException($"Invalid message, could not unpack {typeof(T).FullName}"); + + return networkReader.Read(); + } + } + + // 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(), Is.EqualTo(0x8706)); + } + + [Test] + public void TestPacking() + { + SceneMessage message = new SceneMessage() + { + sceneName = "Hello world", + sceneOperation = SceneOperation.LoadAdditive + }; + + byte[] data = PackToByteArray(message); + + SceneMessage unpacked = UnpackFromByteArray(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(() => + { + ReadyMessage unpacked = UnpackFromByteArray(data); + }); + } + + [Test] + public void TestUnpackIdMismatch() + { + // Unpack 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(() => + { + SceneMessage unpacked = UnpackFromByteArray(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 segment = writer.ToArraySegment(); + + Assert.That(segment.Count, Is.EqualTo(MessagePacking.HeaderSize), "Empty message should have same size as HeaderSize"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/MessagePackingTest.cs.meta b/Assets/Mirror/Tests/Editor/MessagePackingTest.cs.meta new file mode 100644 index 000000000..70e62403b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MessagePackingTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d57c17d9ee7c49e6bacc54ddbeac751 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/MiddlewareTransportTest.cs b/Assets/Mirror/Tests/Editor/MiddlewareTransportTest.cs new file mode 100644 index 000000000..bb24fd8d9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MiddlewareTransportTest.cs @@ -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(); + + GameObject gameObject = new GameObject(); + + middleware = gameObject.AddComponent(); + 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()).Returns(packageSize); + + Assert.That(middleware.GetMaxPacketSize(channel), Is.EqualTo(packageSize)); + + inner.Received(1).GetMaxPacketSize(Arg.Is(x => x == channel)); + inner.Received(0).GetMaxPacketSize(Arg.Is(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(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 segment = new ArraySegment(array, offset, count); + + middleware.ClientSend(segment, channel); + + inner.Received(1).ClientSend(Arg.Is>(x => x.Array == array && x.Offset == offset && x.Count == count), channel); + inner.Received(0).ClientSend(Arg.Any>(), Arg.Is(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 segment = new ArraySegment(array, offset, count); + + middleware.ServerSend(id, segment, channel); + + inner.Received(1).ServerSend(id, Arg.Is>(x => x.Array == array && x.Offset == offset && x.Count == count), channel); + // only need to check first arg, + inner.Received(0).ServerSend(Arg.Is(x => x != id), Arg.Any>(), Arg.Any()); + } + + [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(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 segment = new ArraySegment(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(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 segment = new ArraySegment(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(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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/MiddlewareTransportTest.cs.meta b/Assets/Mirror/Tests/Editor/MiddlewareTransportTest.cs.meta new file mode 100644 index 000000000..9766d9cdd --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MiddlewareTransportTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1ee4c7efa89013a41aeee942e60af4e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef b/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef new file mode 100644 index 000000000..60c6c9a96 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef @@ -0,0 +1,34 @@ +{ + "name": "Mirror.Tests", + "rootNamespace": "", + "references": [ + "Mirror", + "Mirror.Editor", + "Mirror.Components", + "Mirror.Tests.Common", + "Telepathy", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner", + "Unity.Mirror.CodeGen", + "WeaverTestExtraAssembly" + ], + "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 +} \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef.meta b/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef.meta new file mode 100644 index 000000000..4f2302303 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8b489029f75e64a7bbf6918bf1a49e39 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/MultiplexTest.cs b/Assets/Mirror/Tests/Editor/MultiplexTest.cs new file mode 100644 index 000000000..795e7a1e0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MultiplexTest.cs @@ -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(); + transport2 = Substitute.For(); + 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()); + 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 segment = new ArraySegment(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(); + // 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(); + // 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 segment = new ArraySegment(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); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/MultiplexTest.cs.meta b/Assets/Mirror/Tests/Editor/MultiplexTest.cs.meta new file mode 100644 index 000000000..a4ec69160 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/MultiplexTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0d8bef6afd464247bf433cdc5d3ff23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkBehaviourDirtyBitsTests.cs b/Assets/Mirror/Tests/Editor/NetworkBehaviourDirtyBitsTests.cs new file mode 100644 index 000000000..9edc8087a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkBehaviourDirtyBitsTests.cs @@ -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 list = new SyncList(); + public readonly SyncDictionary dict = new SyncDictionary(); + } + + 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); + } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkBehaviourDirtyBitsTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkBehaviourDirtyBitsTests.cs.meta new file mode 100644 index 000000000..c9e0879cf --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkBehaviourDirtyBitsTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 71cd60f7177a4d3da75bb87f5ac13a18 +timeCreated: 1631772886 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/NetworkBehaviourSerializeTest.cs b/Assets/Mirror/Tests/Editor/NetworkBehaviourSerializeTest.cs new file mode 100644 index 000000000..99dbbd3e9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkBehaviourSerializeTest.cs @@ -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 syncListInAbstract = new SyncList(); + + [SyncVar] + public int SyncFieldInAbstract; + } + + class BehaviourWithSyncVar : NetworkBehaviour + { + public readonly SyncList syncList = new SyncList(); + + [SyncVar] + public int SyncField; + } + + class OverrideBehaviourFromSyncVar : AbstractBehaviour {} + + class OverrideBehaviourWithSyncVarFromSyncVar : AbstractBehaviour + { + public readonly SyncList syncListInOverride = new SyncList(); + + [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 syncList = new SyncList(); + + [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 syncListInOverride = new SyncList(); + + [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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkBehaviourSerializeTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkBehaviourSerializeTest.cs.meta new file mode 100644 index 000000000..b76e74fed --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkBehaviourSerializeTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 253d419ce2900134482c3c8b76883c60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkBehaviourTests.cs b/Assets/Mirror/Tests/Editor/NetworkBehaviourTests.cs new file mode 100644 index 000000000..343f0c828 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkBehaviourTests.cs @@ -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(); + InitSyncObject(syncObject); + Assert.That(syncObjects.Count, Is.EqualTo(1)); + Assert.That(syncObjects[0], Is.EqualTo(syncObject)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkBehaviourTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkBehaviourTests.cs.meta new file mode 100644 index 000000000..49167f61a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkBehaviourTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e4164d273d6e4d71bd39cc246f454b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkClientTests.cs b/Assets/Mirror/Tests/Editor/NetworkClientTests.cs new file mode 100644 index 000000000..729d23256 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkClientTests.cs @@ -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); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkClientTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkClientTests.cs.meta new file mode 100644 index 000000000..548cf375e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkClientTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e55c00322b97542828f7d27c8182c60a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkConnectionToClientTests.cs b/Assets/Mirror/Tests/Editor/NetworkConnectionToClientTests.cs new file mode 100644 index 000000000..fe150e468 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkConnectionToClientTests.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Mirror.Tests +{ + public class NetworkConnectionToClientTests : MirrorEditModeTest + { + List clientReceived = new List(); + + [SetUp] + public override void SetUp() + { + base.SetUp(); + transport.OnClientDataReceived = (message, channelId) => { + byte[] array = new byte[message.Count]; + Buffer.BlockCopy(message.Array, message.Offset, array, 0, message.Count); + clientReceived.Add(array); + }; + transport.ServerStart(); + transport.ClientConnect("localhost"); + Assert.That(transport.ServerActive, Is.True); + Assert.That(transport.ClientConnected, Is.True); + } + + [TearDown] + public override void TearDown() + { + clientReceived.Clear(); + base.TearDown(); + } + + [Test] + public void Send_BatchesUntilUpdate() + { + // create connection and send + NetworkConnectionToClient connection = new NetworkConnectionToClient(42); + byte[] message = {0x01, 0x02}; + connection.Send(new ArraySegment(message)); + + // Send() should only add to batch, not send anything yet + UpdateTransport(); + Assert.That(clientReceived.Count, Is.EqualTo(0)); + + // updating the connection should now send + connection.Update(); + UpdateTransport(); + Assert.That(clientReceived.Count, Is.EqualTo(1)); + } + + // IMPORTANT + // + // there was a bug where batching resets .Position instead of .Length, + // resulting in extremely high bandwidth where if the last message's + // Length was 2, and the current message's Length was 1, then we would + // still send a writer with Length = 2 because we did not reset .Length! + // -> let's try to send a big message, update, then send a small message + [Test] + public void SendBatchingResetsPreviousWriter() + { + // batching adds 8 byte timestamp header + const int BatchHeader = 8; + + // create connection + NetworkConnectionToClient connection = new NetworkConnectionToClient(42); + + // send and update big message + byte[] message = {0x01, 0x02}; + connection.Send(new ArraySegment(message)); + connection.Update(); + UpdateTransport(); + Assert.That(clientReceived.Count, Is.EqualTo(1)); + Assert.That(clientReceived[0].Length, Is.EqualTo(BatchHeader + 2)); + Assert.That(clientReceived[0][BatchHeader + 0], Is.EqualTo(0x01)); + Assert.That(clientReceived[0][BatchHeader + 1], Is.EqualTo(0x02)); + + // clear previous + clientReceived.Clear(); + + // send a smaller message + message = new byte[]{0xFF}; + connection.Send(new ArraySegment(message)); + connection.Update(); + UpdateTransport(); + Assert.That(clientReceived.Count, Is.EqualTo(1)); + Assert.That(clientReceived[0].Length, Is.EqualTo(BatchHeader + 1)); + Assert.That(clientReceived[0][BatchHeader + 0], Is.EqualTo(0xFF)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkConnectionToClientTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkConnectionToClientTests.cs.meta new file mode 100644 index 000000000..94948fcd6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkConnectionToClientTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 22aee0cf91224ec9925f7faabc073b09 +timeCreated: 1611722538 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/NetworkIdentitySerializationTests.cs b/Assets/Mirror/Tests/Editor/NetworkIdentitySerializationTests.cs new file mode 100644 index 000000000..ecc1a4460 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkIdentitySerializationTests.cs @@ -0,0 +1,180 @@ +// OnDe/SerializeSafely tests. +using System; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests +{ + public class NetworkIdentitySerializationTests : MirrorEditModeTest + { + // writers are always needed. create in setup for convenience. + NetworkWriter ownerWriter; + NetworkWriter observersWriter; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + ownerWriter = new NetworkWriter(); + observersWriter = new NetworkWriter(); + } + + // serialize -> deserialize. multiple components to be sure. + // one for Owner, one for Observer + [Test] + public void OnSerializeAndDeserializeAllSafely() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out SerializeTest1NetworkBehaviour comp1, + out SerializeTest2NetworkBehaviour comp2); + + // set some unique values to serialize + comp1.value = 12345; + comp1.syncMode = SyncMode.Observers; + comp2.value = "67890"; + comp2.syncMode = SyncMode.Owner; + + // serialize all + identity.OnSerializeAllSafely(true, ownerWriter, observersWriter); + + // owner & observers should have written something + Assert.That(ownerWriter.Position, Is.GreaterThan(0)); + Assert.That(observersWriter.Position, Is.GreaterThan(0)); + Debug.Log($"ownerWriter: {BitConverter.ToString(ownerWriter.ToArray())}"); + Debug.Log($"observersWriter: {BitConverter.ToString(observersWriter.ToArray())}"); + + // reset component values + comp1.value = 0; + comp2.value = null; + + // deserialize all for owner + NetworkReader reader = new NetworkReader(ownerWriter.ToArray()); + identity.OnDeserializeAllSafely(reader, true); + Assert.That(comp1.value, Is.EqualTo(12345)); + Assert.That(comp2.value, Is.EqualTo("67890")); + + // reset component values + comp1.value = 0; + comp2.value = null; + + // deserialize all for observers + reader = new NetworkReader(observersWriter.ToArray()); + identity.OnDeserializeAllSafely(reader, true); + // observers mode, should be in data + Assert.That(comp1.value, Is.EqualTo(12345)); + // owner mode, should not be in data + Assert.That(comp2.value, Is.EqualTo(null)); + } + + // serialization should work even if a component throws an exception. + // so if first component throws, second should still be serialized fine. + [Test] + public void SerializationException() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out SerializeExceptionNetworkBehaviour compExc, + out SerializeTest2NetworkBehaviour comp2); + + // set some unique values to serialize + compExc.syncMode = SyncMode.Observers; + comp2.value = "67890"; + comp2.syncMode = SyncMode.Owner; + + // serialize all - should work even if compExc throws an exception + // error log because of the exception is expected + LogAssert.ignoreFailingMessages = true; + identity.OnSerializeAllSafely(true, ownerWriter, observersWriter); + LogAssert.ignoreFailingMessages = false; + + // owner & observers should have written something + Assert.That(ownerWriter.Position, Is.GreaterThan(0)); + Assert.That(observersWriter.Position, Is.GreaterThan(0)); + + // reset component values + comp2.value = null; + + // deserialize all for owner - should work even if compExc throws an exception + NetworkReader reader = new NetworkReader(ownerWriter.ToArray()); + // error log because of the exception is expected + LogAssert.ignoreFailingMessages = true; + identity.OnDeserializeAllSafely(reader, true); + LogAssert.ignoreFailingMessages = false; + Assert.That(comp2.value, Is.EqualTo("67890")); + + // reset component values + comp2.value = null; + + // deserialize all for observers - should work even if compExc throws an exception + reader = new NetworkReader(observersWriter.ToArray()); + // error log because of the exception is expected + LogAssert.ignoreFailingMessages = true; + identity.OnDeserializeAllSafely(reader, true); + LogAssert.ignoreFailingMessages = false; + // owner mode, should not be in data + Assert.That(comp2.value, Is.EqualTo(null)); + } + + // OnSerializeAllSafely supports at max 64 components, because our + // dirty mask is ulong and can only handle so many bits. + [Test] + public void TooManyComponents() + { + CreateNetworked(out GameObject gameObject, out NetworkIdentity identity); + + // add 65 components + for (int i = 0; i < 65; ++i) + gameObject.AddComponent(); + + // CreateNetworked already initializes the components. + // let's reset and initialize again with the added ones. + identity.Reset(); + identity.Awake(); + + // ignore error from creating cache (has its own test) + LogAssert.ignoreFailingMessages = true; + _ = identity.NetworkBehaviours; + LogAssert.ignoreFailingMessages = false; + + // try to serialize + identity.OnSerializeAllSafely(true, ownerWriter, observersWriter); + + // Should still write with too many Components because NetworkBehavioursCache should handle the error + Assert.That(ownerWriter.Position, Is.GreaterThan(0)); + Assert.That(observersWriter.Position, Is.GreaterThan(0)); + } + + // OnDeserializeSafely should be able to detect and handle serialization + // mismatches (= if compA writes 10 bytes but only reads 8 or 12, it + // shouldn't break compB's serialization. otherwise we end up with + // insane runtime errors like monsters that look like npcs. that's what + // happened back in the day with UNET). + [Test] + public void SerializationMismatch() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out SerializeMismatchNetworkBehaviour compMiss, + out SerializeTest2NetworkBehaviour comp); + + // set some unique values to serialize + comp.value = "67890"; + + // serialize + identity.OnSerializeAllSafely(true, ownerWriter, observersWriter); + + // reset component values + comp.value = null; + + // deserialize all + NetworkReader reader = new NetworkReader(ownerWriter.ToArray()); + // warning log because of serialization mismatch + LogAssert.ignoreFailingMessages = true; + identity.OnDeserializeAllSafely(reader, true); + LogAssert.ignoreFailingMessages = false; + + // the mismatch component will fail, but the one before and after + // should still work fine. that's the whole point. + Assert.That(comp.value, Is.EqualTo("67890")); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkIdentitySerializationTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkIdentitySerializationTests.cs.meta new file mode 100644 index 000000000..4ab5b3b19 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkIdentitySerializationTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0ac8a004d23d4b91bbfd910183bdb89d +timeCreated: 1628417849 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/NetworkIdentityTests.cs b/Assets/Mirror/Tests/Editor/NetworkIdentityTests.cs new file mode 100644 index 000000000..d0b8dba43 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkIdentityTests.cs @@ -0,0 +1,967 @@ +using System; +using System.Text.RegularExpressions; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests +{ + class StartServerNetworkBehaviour : NetworkBehaviour + { + internal bool onStartServerInvoked; + public override void OnStartServer() => onStartServerInvoked = true; + } + + class StartServerExceptionNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStartServer() + { + ++called; + throw new Exception("some exception"); + } + } + + class StartClientExceptionNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStartClient() + { + ++called; + throw new Exception("some exception"); + } + } + + class StartAuthorityExceptionNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStartAuthority() + { + ++called; + throw new Exception("some exception"); + } + } + + class StartAuthorityCalledNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStartAuthority() => ++called; + } + + class StopAuthorityExceptionNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStopAuthority() + { + ++called; + throw new Exception("some exception"); + } + } + + class StopAuthorityCalledNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStopAuthority() => ++called; + } + + class StartLocalPlayerExceptionNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStartLocalPlayer() + { + ++called; + throw new Exception("some exception"); + } + } + + class StartLocalPlayerCalledNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStartLocalPlayer() => ++called; + } + + class NetworkDestroyExceptionNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStopClient() + { + ++called; + throw new Exception("some exception"); + } + } + + class NetworkDestroyCalledNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStopClient() => ++called; + } + + class StopServerCalledNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStopServer() => ++called; + } + + class StopServerExceptionNetworkBehaviour : NetworkBehaviour + { + public int called; + public override void OnStopServer() + { + ++called; + throw new Exception("some exception"); + } + } + + class SerializeTest1NetworkBehaviour : NetworkBehaviour + { + public int value; + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + writer.WriteInt(value); + return true; + } + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + value = reader.ReadInt(); + } + } + + class SerializeTest2NetworkBehaviour : NetworkBehaviour + { + public string value; + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + writer.WriteString(value); + return true; + } + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + value = reader.ReadString(); + } + } + + class SerializeExceptionNetworkBehaviour : NetworkBehaviour + { + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + throw new Exception("some exception"); + } + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + throw new Exception("some exception"); + } + } + + class SerializeMismatchNetworkBehaviour : NetworkBehaviour + { + public int value; + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + writer.WriteInt(value); + // one too many + writer.WriteInt(value); + return true; + } + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + value = reader.ReadInt(); + } + } + + class IsClientServerCheckComponent : NetworkBehaviour + { + // OnStartClient + internal bool OnStartClient_isClient; + internal bool OnStartClient_isServer; + internal bool OnStartClient_isLocalPlayer; + public override void OnStartClient() + { + OnStartClient_isClient = isClient; + OnStartClient_isServer = isServer; + OnStartClient_isLocalPlayer = isLocalPlayer; + } + + // OnStartServer + internal bool OnStartServer_isClient; + internal bool OnStartServer_isServer; + internal bool OnStartServer_isLocalPlayer; + public override void OnStartServer() + { + OnStartServer_isClient = isClient; + OnStartServer_isServer = isServer; + OnStartServer_isLocalPlayer = isLocalPlayer; + } + + // OnStartLocalPlayer + internal bool OnStartLocalPlayer_isClient; + internal bool OnStartLocalPlayer_isServer; + internal bool OnStartLocalPlayer_isLocalPlayer; + public override void OnStartLocalPlayer() + { + OnStartLocalPlayer_isClient = isClient; + OnStartLocalPlayer_isServer = isServer; + OnStartLocalPlayer_isLocalPlayer = isLocalPlayer; + } + + // Start + internal bool Start_isClient; + internal bool Start_isServer; + internal bool Start_isLocalPlayer; + public void Start() + { + Start_isClient = isClient; + Start_isServer = isServer; + Start_isLocalPlayer = isLocalPlayer; + } + + // OnDestroy + internal bool OnDestroy_isClient; + internal bool OnDestroy_isServer; + internal bool OnDestroy_isLocalPlayer; + public void OnDestroy() + { + OnDestroy_isClient = isClient; + OnDestroy_isServer = isServer; + OnDestroy_isLocalPlayer = isLocalPlayer; + } + } + + public class NetworkIdentityTests : MirrorEditModeTest + { + [Test] + public void OnStartServerTest() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, out StartServerNetworkBehaviour component1, out StartServerNetworkBehaviour component2); + identity.OnStartServer(); + + Assert.That(component1.onStartServerInvoked); + Assert.That(component2.onStartServerInvoked); + } + + // check isClient/isServer/isLocalPlayer in server-only mode + [Test] + public void ServerMode_IsFlags_Test() + { + CreateNetworked(out GameObject gameObject, out NetworkIdentity _, out IsClientServerCheckComponent component); + + // start the server + NetworkServer.Listen(1000); + + // spawn it + NetworkServer.Spawn(gameObject); + + // OnStartServer should have been called. check the flags. + Assert.That(component.OnStartServer_isClient, Is.EqualTo(false)); + Assert.That(component.OnStartServer_isLocalPlayer, Is.EqualTo(false)); + Assert.That(component.OnStartServer_isServer, Is.EqualTo(true)); + } + + // check isClient/isServer/isLocalPlayer in host mode + [Test] + public void HostMode_IsFlags_Test() + { + CreateNetworked(out GameObject gameObject, out NetworkIdentity identity, out IsClientServerCheckComponent component); + + // start the server + NetworkServer.Listen(1000); + + // start the client + NetworkClient.ConnectHost(); + + // set is as local player + NetworkClient.InternalAddPlayer(identity); + + // spawn it + NetworkServer.Spawn(gameObject); + + // OnStartServer should have been called. check the flags. + Assert.That(component.OnStartServer_isClient, Is.EqualTo(true)); + Assert.That(component.OnStartServer_isLocalPlayer, Is.EqualTo(true)); + Assert.That(component.OnStartServer_isServer, Is.EqualTo(true)); + + // stop the client + NetworkServer.RemoveLocalConnection(); + } + + [Test] + public void GetSetAssetId() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // assign a guid + Guid guid = new Guid(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B); + identity.assetId = guid; + + // did it work? + Assert.That(identity.assetId, Is.EqualTo(guid)); + } + + [Test] + public void SetAssetId_GivesErrorIfOneExists() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + if (identity.assetId == Guid.Empty) + { + identity.assetId = Guid.NewGuid(); + } + + Guid guid1 = identity.assetId; + + // assign a guid + Guid guid2 = Guid.NewGuid(); + LogAssert.Expect(LogType.Error, $"Can not Set AssetId on NetworkIdentity '{identity.name}' because it already had an assetId, current assetId '{guid1:N}', attempted new assetId '{guid2:N}'"); + identity.assetId = guid2; + + // guid was changed + Assert.That(identity.assetId, Is.EqualTo(guid1)); + } + + [Test] + public void SetAssetId_GivesErrorForEmptyGuid() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + if (identity.assetId == Guid.Empty) + { + identity.assetId = Guid.NewGuid(); + } + + Guid guid1 = identity.assetId; + + // assign a guid + Guid guid2 = new Guid(); + LogAssert.Expect(LogType.Error, $"Can not set AssetId to empty guid on NetworkIdentity '{identity.name}', old assetId '{guid1:N}'"); + identity.assetId = guid2; + + // guid was NOT changed + Assert.That(identity.assetId, Is.EqualTo(guid1)); + } + + [Test] + public void SetAssetId_DoesNotGiveErrorIfBothOldAndNewAreEmpty() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + Debug.Assert(identity.assetId == Guid.Empty, "assetId needs to be empty at the start of this test"); + // assign a guid + Guid guid2 = new Guid(); + // expect no errors + identity.assetId = guid2; + + // guid was still empty + Assert.That(identity.assetId, Is.EqualTo(Guid.Empty)); + } + + [Test] + public void SetClientOwner() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // SetClientOwner + LocalConnectionToClient original = new LocalConnectionToClient(); + identity.SetClientOwner(original); + Assert.That(identity.connectionToClient, Is.EqualTo(original)); + + // setting it when it's already set shouldn't overwrite the original + LocalConnectionToClient overwrite = new LocalConnectionToClient(); + // will log a warning + LogAssert.ignoreFailingMessages = true; + identity.SetClientOwner(overwrite); + Assert.That(identity.connectionToClient, Is.EqualTo(original)); + LogAssert.ignoreFailingMessages = false; + } + + [Test] + public void RemoveObserver() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // call OnStartServer so that observers dict is created + identity.OnStartServer(); + + // add an observer connection + NetworkConnectionToClient connection = new NetworkConnectionToClient(42); + identity.observers[connection.connectionId] = connection; + + // RemoveObserver with invalid connection should do nothing + identity.RemoveObserver(new NetworkConnectionToClient(43)); + Assert.That(identity.observers.Count, Is.EqualTo(1)); + + // RemoveObserver with existing connection should remove it + identity.RemoveObserver(connection); + Assert.That(identity.observers.Count, Is.EqualTo(0)); + } + + [Test] + public void AssignSceneID() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // Awake will have assigned a random sceneId of format 0x00000000FFFFFFFF + // -> make sure that one was assigned, and that the left part was + // left empty for scene hash + Assert.That(identity.sceneId, !Is.Zero); + Assert.That(identity.sceneId & 0xFFFFFFFF00000000, Is.EqualTo(0x0000000000000000)); + + // make sure that Awake added it to sceneIds dict + Assert.That(NetworkIdentity.GetSceneIdentity(identity.sceneId), !Is.Null); + } + + [Test] + public void SetSceneIdSceneHashPartInternal() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // Awake will have assigned a random sceneId of format 0x00000000FFFFFFFF + // -> make sure that one was assigned, and that the left part was + // left empty for scene hash + Assert.That(identity.sceneId, !Is.Zero); + Assert.That(identity.sceneId & 0xFFFFFFFF00000000, Is.EqualTo(0x0000000000000000)); + ulong rightPart = identity.sceneId; + + // set scene hash + identity.SetSceneIdSceneHashPartInternal(); + + // make sure that the right part is still the random sceneid + Assert.That(identity.sceneId & 0x00000000FFFFFFFF, Is.EqualTo(rightPart)); + + // make sure that the left part is a scene hash now + Assert.That(identity.sceneId & 0xFFFFFFFF00000000, !Is.Zero); + ulong finished = identity.sceneId; + + // calling it again should said the exact same hash again + identity.SetSceneIdSceneHashPartInternal(); + Assert.That(identity.sceneId, Is.EqualTo(finished)); + } + + [Test] + public void OnValidateSetupIDsSetsEmptyAssetIDForSceneObject() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // OnValidate will have been called. make sure that assetId was set + // to 0 empty and not anything else, because this is a scene object + Assert.That(identity.assetId, Is.EqualTo(Guid.Empty)); + } + + [Test] + public void OnStartServerCallsComponentsAndCatchesExceptions() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, out StartServerExceptionNetworkBehaviour comp); + + // make sure that comp.OnStartServer was called and make sure that + // the exception was caught and not thrown in here. + // an exception in OnStartServer should be caught, so that one + // component's exception doesn't stop all other components from + // being initialized + // (an error log is expected though) + LogAssert.ignoreFailingMessages = true; + // should catch the exception internally and not throw it + identity.OnStartServer(); + Assert.That(comp.called, Is.EqualTo(1)); + LogAssert.ignoreFailingMessages = false; + } + + [Test] + public void OnStartClientCallsComponentsAndCatchesExceptions() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, out StartClientExceptionNetworkBehaviour comp); + + // make sure that comp.OnStartClient was called and make sure that + // the exception was caught and not thrown in here. + // an exception in OnStartClient should be caught, so that one + // component's exception doesn't stop all other components from + // being initialized + // (an error log is expected though) + LogAssert.ignoreFailingMessages = true; + // should catch the exception internally and not throw it + identity.OnStartClient(); + Assert.That(comp.called, Is.EqualTo(1)); + LogAssert.ignoreFailingMessages = false; + + // we have checks to make sure that it's only called once. + // let's see if they work. + identity.OnStartClient(); + // same as before? + Assert.That(comp.called, Is.EqualTo(1)); + } + + [Test] + public void OnStartAuthorityCallsComponentsAndCatchesExceptions() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, out StartAuthorityExceptionNetworkBehaviour comp); + + // make sure that comp.OnStartAuthority was called and make sure that + // the exception was caught and not thrown in here. + // an exception in OnStartAuthority should be caught, so that one + // component's exception doesn't stop all other components from + // being initialized + // (an error log is expected though) + LogAssert.ignoreFailingMessages = true; + // should catch the exception internally and not throw it + identity.OnStartAuthority(); + Assert.That(comp.called, Is.EqualTo(1)); + LogAssert.ignoreFailingMessages = false; + } + + [Test] + public void OnStopAuthorityCallsComponentsAndCatchesExceptions() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, out StopAuthorityExceptionNetworkBehaviour comp); + + // make sure that comp.OnStopAuthority was called and make sure that + // the exception was caught and not thrown in here. + // an exception in OnStopAuthority should be caught, so that one + // component's exception doesn't stop all other components from + // being initialized + // (an error log is expected though) + LogAssert.ignoreFailingMessages = true; + // should catch the exception internally and not throw it + identity.OnStopAuthority(); + Assert.That(comp.called, Is.EqualTo(1)); + LogAssert.ignoreFailingMessages = false; + } + + [Test] + public void AssignAndRemoveClientAuthority() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // test the callback too + int callbackCalled = 0; + NetworkConnection callbackConnection = null; + NetworkIdentity callbackIdentity = null; + bool callbackState = false; + NetworkIdentity.clientAuthorityCallback += (conn, networkIdentity, state) => + { + ++callbackCalled; + callbackConnection = conn; + callbackIdentity = identity; + callbackState = state; + }; + + // create connections + CreateLocalConnectionPair(out LocalConnectionToClient owner, out LocalConnectionToServer clientConnection); + owner.isReady = true; + + // setup NetworkServer/Client connections so messages are handled + NetworkClient.connection = clientConnection; + NetworkServer.connections[owner.connectionId] = owner; + + // add client handlers + int spawnCalled = 0; + void Handler(SpawnMessage _) => ++spawnCalled; + NetworkClient.RegisterHandler(Handler, false); + + // assigning authority should only work on server. + // if isServer is false because server isn't running yet then it + // should fail. + // error log is expected + LogAssert.ignoreFailingMessages = true; + bool result = identity.AssignClientAuthority(owner); + LogAssert.ignoreFailingMessages = false; + Assert.That(result, Is.False); + + // server is needed + NetworkServer.Listen(1); + + // call OnStartServer so that isServer is true + identity.OnStartServer(); + Assert.That(identity.isServer, Is.True); + + // assign authority + result = identity.AssignClientAuthority(owner); + Assert.That(result, Is.True); + Assert.That(identity.connectionToClient, Is.EqualTo(owner)); + Assert.That(callbackCalled, Is.EqualTo(1)); + Assert.That(callbackConnection, Is.EqualTo(owner)); + Assert.That(callbackIdentity, Is.EqualTo(identity)); + Assert.That(callbackState, Is.EqualTo(true)); + + // shouldn't be able to assign authority while already owned by + // another connection + // error log is expected + LogAssert.ignoreFailingMessages = true; + result = identity.AssignClientAuthority(new NetworkConnectionToClient(43)); + LogAssert.ignoreFailingMessages = false; + Assert.That(result, Is.False); + Assert.That(identity.connectionToClient, Is.EqualTo(owner)); + Assert.That(callbackCalled, Is.EqualTo(1)); + + // someone might try to remove authority by assigning null. + // make sure this fails. + // error log is expected + LogAssert.ignoreFailingMessages = true; + result = identity.AssignClientAuthority(null); + LogAssert.ignoreFailingMessages = false; + Assert.That(result, Is.False); + + // removing authority while not isServer shouldn't work. + // only allow it on server. + identity.isServer = false; + + // error log is expected + LogAssert.ignoreFailingMessages = true; + identity.RemoveClientAuthority(); + LogAssert.ignoreFailingMessages = false; + Assert.That(identity.connectionToClient, Is.EqualTo(owner)); + Assert.That(callbackCalled, Is.EqualTo(1)); + + // enable isServer again + identity.isServer = true; + + // removing authority for the main player object shouldn't work + // set connection's player object + owner.identity = identity; + // error log is expected + LogAssert.ignoreFailingMessages = true; + identity.RemoveClientAuthority(); + LogAssert.ignoreFailingMessages = false; + Assert.That(identity.connectionToClient, Is.EqualTo(owner)); + Assert.That(callbackCalled, Is.EqualTo(1)); + + // removing authority for a non-main-player object should work + owner.identity = null; + identity.RemoveClientAuthority(); + Assert.That(identity.connectionToClient, Is.Null); + Assert.That(callbackCalled, Is.EqualTo(2)); + // the one that was removed + Assert.That(callbackConnection, Is.EqualTo(owner)); + Assert.That(callbackIdentity, Is.EqualTo(identity)); + Assert.That(callbackState, Is.EqualTo(false)); + } + + [Test] + public void NotifyAuthorityCallsOnStartStopAuthority() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, out StartAuthorityCalledNetworkBehaviour compStart, out StopAuthorityCalledNetworkBehaviour compStop); + + // set authority from false to true, which should call OnStartAuthority + identity.hasAuthority = true; + identity.NotifyAuthority(); + // shouldn't be touched + Assert.That(identity.hasAuthority, Is.True); + // start should be called + Assert.That(compStart.called, Is.EqualTo(1)); + // stop shouldn't + Assert.That(compStop.called, Is.EqualTo(0)); + + // set it to true again, should do nothing because already true + identity.hasAuthority = true; + identity.NotifyAuthority(); + // shouldn't be touched + Assert.That(identity.hasAuthority, Is.True); + // same as before + Assert.That(compStart.called, Is.EqualTo(1)); + // same as before + Assert.That(compStop.called, Is.EqualTo(0)); + + // set it to false, should call OnStopAuthority + identity.hasAuthority = false; + identity.NotifyAuthority(); + // should be changed + Assert.That(identity.hasAuthority, Is.False); + // same as before + Assert.That(compStart.called, Is.EqualTo(1)); + // stop should be called + Assert.That(compStop.called, Is.EqualTo(1)); + + // set it to false again, should do nothing because already false + identity.hasAuthority = false; + identity.NotifyAuthority(); + // shouldn't be touched + Assert.That(identity.hasAuthority, Is.False); + // same as before + Assert.That(compStart.called, Is.EqualTo(1)); + // same as before + Assert.That(compStop.called, Is.EqualTo(1)); + } + + // OnStartServer in host mode should set isClient=true + [Test] + public void OnStartServerInHostModeSetsIsClientTrue() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // call client connect so that internals are set up + // (it won't actually successfully connect) + NetworkClient.Connect("localhost"); + + // manually invoke transport.OnConnected so that NetworkClient.active is set to true + Transport.activeTransport.OnClientConnected.Invoke(); + Assert.That(NetworkClient.active, Is.True); + + // isClient needs to be true in OnStartServer if in host mode. + // this is a test for a bug that we fixed, where isClient was false + // in OnStartServer if in host mode because in host mode, we only + // connect the client after starting the server, hence isClient would + // be false in OnStartServer until way later. + // -> we have the workaround in OnStartServer, so let's also test to + // make sure that nobody ever breaks it again + Assert.That(identity.isClient, Is.False); + identity.OnStartServer(); + Assert.That(identity.isClient, Is.True); + } + + [Test] + public void CreatingNetworkBehavioursCacheShouldLogErrorForTooComponents() + { + CreateNetworked(out GameObject gameObject, out NetworkIdentity identity); + + // add byte.MaxValue+1 components + for (int i = 0; i < byte.MaxValue + 1; ++i) + { + gameObject.AddComponent(); + } + + // CreateNetworked already initializes the components. + // let's reset and initialize again with the added ones. + identity.Reset(); + identity.Awake(); + + // call NetworkBehaviours property to create the cache + LogAssert.Expect(LogType.Error, new Regex($"Only {byte.MaxValue} NetworkBehaviour components are allowed for NetworkIdentity.+")); + _ = identity.NetworkBehaviours; + } + + [Test] + public void OnStartLocalPlayer() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out StartLocalPlayerExceptionNetworkBehaviour compEx, + out StartLocalPlayerCalledNetworkBehaviour comp); + + // make sure our test values are set to 0 + Assert.That(compEx.called, Is.EqualTo(0)); + Assert.That(comp.called, Is.EqualTo(0)); + + // call OnStartLocalPlayer in identity + // one component will throw an exception, but that shouldn't stop + // OnStartLocalPlayer from being called in the second one + // exception will log an error + LogAssert.ignoreFailingMessages = true; + identity.OnStartLocalPlayer(); + LogAssert.ignoreFailingMessages = false; + Assert.That(compEx.called, Is.EqualTo(1)); + Assert.That(comp.called, Is.EqualTo(1)); + + // we have checks to make sure that it's only called once. + // let's see if they work. + identity.OnStartLocalPlayer(); + // same as before? + Assert.That(compEx.called, Is.EqualTo(1)); + // same as before? + Assert.That(comp.called, Is.EqualTo(1)); + } + + [Test] + public void OnStopClient() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out NetworkDestroyCalledNetworkBehaviour comp); + + // call OnStopClient in identity + identity.OnStopClient(); + Assert.That(comp.called, Is.EqualTo(1)); + } + + [Test] + public void OnStopClientException() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out NetworkDestroyExceptionNetworkBehaviour compEx, + out NetworkDestroyCalledNetworkBehaviour comp); + + // call OnStopClient in identity + // one component will throw an exception, but that shouldn't stop + // OnStopClient from being called in the second one + // exception will log an error + LogAssert.ignoreFailingMessages = true; + identity.OnStopClient(); + LogAssert.ignoreFailingMessages = false; + Assert.That(compEx.called, Is.EqualTo(1)); + Assert.That(comp.called, Is.EqualTo(1)); + } + + [Test] + public void OnStopServer() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out StopServerCalledNetworkBehaviour comp); + + identity.OnStopServer(); + Assert.That(comp.called, Is.EqualTo(1)); + } + + [Test] + public void OnStopServerException() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, out StopServerExceptionNetworkBehaviour compEx); + + // make sure our test values are set to 0 + Assert.That(compEx.called, Is.EqualTo(0)); + + // call OnStopClient in identity + // one component will throw an exception, but that shouldn't stop + // OnStopClient from being called in the second one + // exception will log an error + LogAssert.ignoreFailingMessages = true; + identity.OnStopServer(); + LogAssert.ignoreFailingMessages = false; + Assert.That(compEx.called, Is.EqualTo(1)); + } + + [Test] + public void AddObserver() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // create some connections + NetworkConnectionToClient connection1 = new NetworkConnectionToClient(42); + NetworkConnectionToClient connection2 = new NetworkConnectionToClient(43); + + // AddObserver should return early if called before .observers was + // created + Assert.That(identity.observers, Is.Null); + // error log is expected + LogAssert.ignoreFailingMessages = true; + identity.AddObserver(connection1); + LogAssert.ignoreFailingMessages = false; + Assert.That(identity.observers, Is.Null); + + // call OnStartServer so that observers dict is created + identity.OnStartServer(); + + // call AddObservers + identity.AddObserver(connection1); + identity.AddObserver(connection2); + Assert.That(identity.observers.Count, Is.EqualTo(2)); + Assert.That(identity.observers.ContainsKey(connection1.connectionId)); + Assert.That(identity.observers[connection1.connectionId], Is.EqualTo(connection1)); + Assert.That(identity.observers.ContainsKey(connection2.connectionId)); + Assert.That(identity.observers[connection2.connectionId], Is.EqualTo(connection2)); + + // adding a duplicate connectionId shouldn't overwrite the original + NetworkConnectionToClient duplicate = new NetworkConnectionToClient(connection1.connectionId); + identity.AddObserver(duplicate); + Assert.That(identity.observers.Count, Is.EqualTo(2)); + Assert.That(identity.observers.ContainsKey(connection1.connectionId)); + Assert.That(identity.observers[connection1.connectionId], Is.EqualTo(connection1)); + Assert.That(identity.observers.ContainsKey(connection2.connectionId)); + Assert.That(identity.observers[connection2.connectionId], Is.EqualTo(connection2)); + } + + [Test] + public void ClearObservers() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // call OnStartServer so that observers dict is created + identity.OnStartServer(); + + // add some observers + identity.observers[42] = new NetworkConnectionToClient(42); + identity.observers[43] = new NetworkConnectionToClient(43); + + // call ClearObservers + identity.ClearObservers(); + Assert.That(identity.observers.Count, Is.EqualTo(0)); + } + + [Test] + public void ClearDirtyComponentsDirtyBits() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out OnStartClientTestNetworkBehaviour compA, + out OnStartClientTestNetworkBehaviour compB); + + // set syncintervals so one is always dirty, one is never dirty + compA.syncInterval = 0; + compB.syncInterval = Mathf.Infinity; + + // set components dirty bits + compA.SetSyncVarDirtyBit(0x0001); + compB.SetSyncVarDirtyBit(0x1001); + // dirty because interval reached and mask != 0 + Assert.That(compA.IsDirty(), Is.True); + // not dirty because syncinterval not reached + Assert.That(compB.IsDirty(), Is.False); + + // call identity.ClearDirtyComponentsDirtyBits + identity.ClearDirtyComponentsDirtyBits(); + // should be cleared now + Assert.That(compA.IsDirty(), Is.False); + // should be untouched + Assert.That(compB.IsDirty(), Is.False); + + // set compB syncinterval to 0 to check if the masks were untouched + // (if they weren't, then it should be dirty now) + compB.syncInterval = 0; + Assert.That(compB.IsDirty(), Is.True); + } + + [Test] + public void ClearAllComponentsDirtyBits() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity, + out OnStartClientTestNetworkBehaviour compA, + out OnStartClientTestNetworkBehaviour compB); + + // set syncintervals so one is always dirty, one is never dirty + compA.syncInterval = 0; + compB.syncInterval = Mathf.Infinity; + + // set components dirty bits + compA.SetSyncVarDirtyBit(0x0001); + compB.SetSyncVarDirtyBit(0x1001); + // dirty because interval reached and mask != 0 + Assert.That(compA.IsDirty(), Is.True); + // not dirty because syncinterval not reached + Assert.That(compB.IsDirty(), Is.False); + + // call identity.ClearAllComponentsDirtyBits + identity.ClearAllComponentsDirtyBits(); + // should be cleared now + Assert.That(compA.IsDirty(), Is.False); + // should be cleared now + Assert.That(compB.IsDirty(), Is.False); + + // set compB syncinterval to 0 to check if the masks were cleared + // (if they weren't, then it would still be dirty now) + compB.syncInterval = 0; + Assert.That(compB.IsDirty(), Is.False); + } + + [Test] + public void Reset() + { + CreateNetworked(out GameObject _, out NetworkIdentity identity); + + // modify it a bit + identity.isClient = true; + // creates .observers and generates a netId + identity.OnStartServer(); + identity.connectionToClient = new NetworkConnectionToClient(1); + identity.connectionToServer = new NetworkConnectionToServer(); + identity.observers[43] = new NetworkConnectionToClient(2); + + // mark for reset and reset + identity.Reset(); + Assert.That(identity.isServer, Is.False); + Assert.That(identity.isClient, Is.False); + Assert.That(identity.isLocalPlayer, Is.False); + Assert.That(identity.netId, Is.EqualTo(0)); + Assert.That(identity.connectionToClient, Is.Null); + Assert.That(identity.connectionToServer, Is.Null); + Assert.That(identity.hasAuthority, Is.False); + Assert.That(identity.observers, Is.Empty); + } + + [Test, Ignore("NetworkServerTest.SendCommand does it already")] + public void HandleCommand() {} + + [Test, Ignore("RpcTests do it already")] + public void HandleRpc() {} + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkIdentityTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkIdentityTests.cs.meta new file mode 100644 index 000000000..6ae479b04 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkIdentityTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9b6d86d91cc74ce58e690371d4f3828 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkLoopTests.cs b/Assets/Mirror/Tests/Editor/NetworkLoopTests.cs new file mode 100644 index 000000000..eb77bd7e8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkLoopTests.cs @@ -0,0 +1,110 @@ +using NUnit.Framework; +#if UNITY_2019_1_OR_NEWER +using UnityEngine.LowLevel; +using UnityEngine.PlayerLoop; +#else +using UnityEngine.Experimental.LowLevel; +using UnityEngine.Experimental.PlayerLoop; +#endif + +namespace Mirror.Tests +{ + public class NetworkLoopTests + { + // all tests need a PlayerLoopSystem to work with + PlayerLoopSystem playerLoop; + + [SetUp] + public void SetUp() + { + // we get the main player loop to work with. + // we don't actually set it. no need to. + playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + } + + // simple test to see if it finds and adds to EarlyUpdate() loop + [Test] + public void AddToPlayerLoop_EarlyUpdate_Beginning() + { + void Function() {} + + // add our function + bool result = NetworkLoop.AddToPlayerLoop(Function, typeof(NetworkLoopTests), ref playerLoop, typeof(EarlyUpdate), NetworkLoop.AddMode.Beginning); + Assert.That(result, Is.True); + + // was it added to the beginning? + int index = NetworkLoop.FindPlayerLoopEntryIndex(Function, playerLoop, typeof(EarlyUpdate)); + Assert.That(index, Is.EqualTo(0)); + } + + [Test] + public void AddToPlayerLoop_EarlyUpdate_End() + { + void Function() {} + + // add our function + bool result = NetworkLoop.AddToPlayerLoop(Function, typeof(NetworkLoopTests), ref playerLoop, typeof(EarlyUpdate), NetworkLoop.AddMode.End); + Assert.That(result, Is.True); + + // was it added to the end? we don't know the exact index, but it should be >0 + int index = NetworkLoop.FindPlayerLoopEntryIndex(Function, playerLoop, typeof(EarlyUpdate)); + Assert.That(index, Is.GreaterThan(0)); + } + + [Test] + public void AddToPlayerLoop_Update_Beginning() + { + void Function() {} + + // add our function + bool result = NetworkLoop.AddToPlayerLoop(Function, typeof(NetworkLoopTests), ref playerLoop, typeof(Update), NetworkLoop.AddMode.Beginning); + Assert.That(result, Is.True); + + // was it added to the beginning? + int index = NetworkLoop.FindPlayerLoopEntryIndex(Function, playerLoop, typeof(Update)); + Assert.That(index, Is.EqualTo(0)); + } + + [Test] + public void AddToPlayerLoop_Update_End() + { + void Function() {} + + // add our function + bool result = NetworkLoop.AddToPlayerLoop(Function, typeof(NetworkLoopTests), ref playerLoop, typeof(Update), NetworkLoop.AddMode.End); + Assert.That(result, Is.True); + + // was it added to the end? we don't know the exact index, but it should be >0 + int index = NetworkLoop.FindPlayerLoopEntryIndex(Function, playerLoop, typeof(Update)); + Assert.That(index, Is.GreaterThan(0)); + } + + [Test] + public void AddToPlayerLoop_PreLateUpdate_Beginning() + { + void Function() {} + + // add our function + bool result = NetworkLoop.AddToPlayerLoop(Function, typeof(NetworkLoopTests), ref playerLoop, typeof(PreLateUpdate), NetworkLoop.AddMode.Beginning); + Assert.That(result, Is.True); + + // was it added to the beginning? + int index = NetworkLoop.FindPlayerLoopEntryIndex(Function, playerLoop, typeof(PreLateUpdate)); + Assert.That(index, Is.EqualTo(0)); + } + + [Test] + public void AddToPlayerLoop_PreLateUpdate_End() + { + void Function() {} + + // add our function + bool result = NetworkLoop.AddToPlayerLoop(Function, typeof(NetworkLoopTests), ref playerLoop, typeof(PreLateUpdate), NetworkLoop.AddMode.End); + Assert.That(result, Is.True); + + // was it added to the end? we don't know the exact index, but it should be >0 + int index = NetworkLoop.FindPlayerLoopEntryIndex(Function, playerLoop, typeof(PreLateUpdate)); + Assert.That(index, Is.GreaterThan(0)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkLoopTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkLoopTests.cs.meta new file mode 100644 index 000000000..4fb7e3e0c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkLoopTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eedb822be9a941428259caf9f707bb45 +timeCreated: 1614573407 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/NetworkManagerStopHostOnServerDisconnectTest.cs b/Assets/Mirror/Tests/Editor/NetworkManagerStopHostOnServerDisconnectTest.cs new file mode 100644 index 000000000..21702edee --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkManagerStopHostOnServerDisconnectTest.cs @@ -0,0 +1,39 @@ +using NUnit.Framework; + +namespace Mirror.Tests +{ + class NetworkManagerOnServerDisconnect : NetworkManager + { + public int called; + public override void OnServerDisconnect(NetworkConnection conn) + { + base.OnServerDisconnect(conn); + ++called; + } +} + + [TestFixture] + public class NetworkManagerStopHostOnServerDisconnectTest : MirrorEditModeTest + { + NetworkManagerOnServerDisconnect manager; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + manager = transport.gameObject.AddComponent(); + } + + // test to prevent https://github.com/vis2k/Mirror/issues/1515 + [Test] + public void StopHostCallsOnServerDisconnectForHostClient() + { + // OnServerDisconnect is always called when a client disconnects. + // it should also be called for the host client when we stop the host + Assert.That(manager.called, Is.EqualTo(0)); + manager.StartHost(); + manager.StopHost(); + Assert.That(manager.called, Is.EqualTo(1)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkManagerStopHostOnServerDisconnectTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkManagerStopHostOnServerDisconnectTest.cs.meta new file mode 100644 index 000000000..b21084477 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkManagerStopHostOnServerDisconnectTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2f583081473a64b92b971678e571382a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkManagerTest.cs b/Assets/Mirror/Tests/Editor/NetworkManagerTest.cs new file mode 100644 index 000000000..6d912a9e3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkManagerTest.cs @@ -0,0 +1,148 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + [TestFixture] + public class NetworkManagerTest : MirrorEditModeTest + { + GameObject gameObject; + NetworkManager manager; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + gameObject = transport.gameObject; + manager = gameObject.AddComponent(); + } + + [Test] + public void StartServerTest() + { + Assert.That(NetworkServer.active, Is.False); + + manager.StartServer(); + + Assert.That(manager.mode == NetworkManagerMode.ServerOnly); + Assert.That(NetworkServer.active, Is.True); + } + + [Test] + public void StopServerTest() + { + manager.StartServer(); + manager.StopServer(); + + Assert.That(manager.mode == NetworkManagerMode.Offline); + } + + [Test] + public void StartClientTest() + { + manager.StartClient(); + + Assert.That(manager.mode == NetworkManagerMode.ClientOnly); + + manager.StopClient(); + } + + [Test] + public void StopClientTest() + { + manager.StartClient(); + manager.StopClient(); + + Assert.That(manager.mode == NetworkManagerMode.Offline); + } + + [Test] + public void StartHostTest() + { + manager.StartHost(); + + Assert.That(manager.mode == NetworkManagerMode.Host); + Assert.That(NetworkServer.active, Is.True); + Assert.That(NetworkClient.active, Is.True); + } + + [Test] + public void StopHostTest() + { + manager.StartHost(); + manager.StopHost(); + + Assert.That(manager.mode == NetworkManagerMode.Offline); + Assert.That(NetworkServer.active, Is.False); + Assert.That(NetworkClient.active, Is.False); + } + + [Test] + public void ShutdownTest() + { + manager.StartClient(); + NetworkManager.ResetStatics(); + + Assert.That(NetworkManager.startPositions.Count, Is.Zero); + Assert.That(NetworkManager.startPositionIndex, Is.Zero); + Assert.That(NetworkManager.clientReadyConnection, Is.Null); + Assert.That(NetworkManager.loadingSceneAsync, Is.Null); + Assert.That(NetworkManager.singleton, Is.Null); + Assert.That(NetworkManager.networkSceneName, Is.Empty); + } + + [Test] + public void RegisterStartPositionTest() + { + Assert.That(NetworkManager.startPositions.Count, Is.Zero); + + NetworkManager.RegisterStartPosition(gameObject.transform); + Assert.That(NetworkManager.startPositions.Count, Is.EqualTo(1)); + Assert.That(NetworkManager.startPositions, Has.Member(gameObject.transform)); + + NetworkManager.UnRegisterStartPosition(gameObject.transform); + } + + [Test] + public void UnRegisterStartPositionTest() + { + Assert.That(NetworkManager.startPositions.Count, Is.Zero); + + NetworkManager.RegisterStartPosition(gameObject.transform); + Assert.That(NetworkManager.startPositions.Count, Is.EqualTo(1)); + Assert.That(NetworkManager.startPositions, Has.Member(gameObject.transform)); + + NetworkManager.UnRegisterStartPosition(gameObject.transform); + Assert.That(NetworkManager.startPositions.Count, Is.Zero); + } + + [Test] + public void GetStartPositionTest() + { + Assert.That(NetworkManager.startPositions.Count, Is.Zero); + + NetworkManager.RegisterStartPosition(gameObject.transform); + Assert.That(NetworkManager.startPositions.Count, Is.EqualTo(1)); + Assert.That(NetworkManager.startPositions, Has.Member(gameObject.transform)); + + Assert.That(manager.GetStartPosition(), Is.SameAs(gameObject.transform)); + + NetworkManager.UnRegisterStartPosition(gameObject.transform); + } + + [Test] + public void StartClientUriTest() + { + UriBuilder uriBuilder = new UriBuilder + { + Host = "localhost", + Scheme = "local" + }; + manager.StartClient(uriBuilder.Uri); + + Assert.That(manager.mode, Is.EqualTo(NetworkManagerMode.ClientOnly)); + Assert.That(manager.networkAddress, Is.EqualTo(uriBuilder.Uri.Host)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkManagerTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkManagerTest.cs.meta new file mode 100644 index 000000000..ad10e831e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkManagerTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11f1d5fee03c2764691201aabc5dda98 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkMessageTests.cs b/Assets/Mirror/Tests/Editor/NetworkMessageTests.cs new file mode 100644 index 000000000..50e6cea19 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkMessageTests.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; + +namespace Mirror.Tests.MessageTests +{ + struct TestMessage : NetworkMessage + { + public int IntValue; + public string StringValue; + public double DoubleValue; + + public TestMessage(int i, string s, double d) + { + IntValue = i; + StringValue = s; + DoubleValue = d; + } + + public void Deserialize(NetworkReader reader) + { + IntValue = reader.ReadInt(); + StringValue = reader.ReadString(); + DoubleValue = reader.ReadDouble(); + } + + public void Serialize(NetworkWriter writer) + { + writer.WriteInt(IntValue); + writer.WriteString(StringValue); + writer.WriteDouble(DoubleValue); + } + } + + struct StructWithEmptyMethodMessage : NetworkMessage + { + public int IntValue; + public string StringValue; + public double DoubleValue; + } + + [TestFixture] + public class NetworkMessageTests + { + // with Serialize/Deserialize + [Test] + public void StructWithMethods() + { + byte[] bytes = MessagePackingTest.PackToByteArray(new TestMessage(1, "2", 3.3)); + TestMessage message = MessagePackingTest.UnpackFromByteArray(bytes); + + Assert.AreEqual(1, message.IntValue); + } + + // without Serialize/Deserialize. Weaver should handle it. + [Test] + public void StructWithEmptyMethods() + { + byte[] bytes = MessagePackingTest.PackToByteArray(new StructWithEmptyMethodMessage { IntValue = 1, StringValue = "2", DoubleValue = 3.3 }); + StructWithEmptyMethodMessage message = MessagePackingTest.UnpackFromByteArray(bytes); + + Assert.AreEqual(1, message.IntValue); + Assert.AreEqual("2", message.StringValue); + Assert.AreEqual(3.3, message.DoubleValue); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkMessageTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkMessageTests.cs.meta new file mode 100644 index 000000000..14117769c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkMessageTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ecf93fcf0386fee4e85f981d5ca9259d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkReaderTest.cs b/Assets/Mirror/Tests/Editor/NetworkReaderTest.cs new file mode 100644 index 000000000..49bf1bd48 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkReaderTest.cs @@ -0,0 +1,84 @@ +using System.IO; +using NUnit.Framework; + +namespace Mirror.Tests +{ + // NetworkWriterTest already covers most cases for NetworkReader. + // only a few are left + [TestFixture] + public class NetworkReaderTest + { + /* uncomment if needed. commented for faster test workflow. this takes >3s. + [Test] + public void Benchmark() + { + // 10 million reads, Unity 2019.3, code coverage disabled + // 4049ms (+GC later) + byte[] bytes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C }; + for (int i = 0; i < 10000000; ++i) + { + ArraySegment segment = new ArraySegment(bytes); + NetworkReader reader = new NetworkReader(segment); + Vector3 value = reader.ReadVector3(); + } + } + */ + + [Test] + public void SetBuffer() + { + // start with an initial buffer + byte[] firstBuffer = {0xFF}; + NetworkReader reader = new NetworkReader(firstBuffer); + + // read one byte so we modify position + reader.ReadByte(); + + // set another buffer + byte[] secondBuffer = {0x42}; + reader.SetBuffer(secondBuffer); + + // was position reset? + Assert.That(reader.Position, Is.EqualTo(0)); + + // are we really on the second buffer now? + Assert.That(reader.ReadByte(), Is.EqualTo(0x42)); + } + + [Test] + public void Remaining() + { + byte[] bytes = {0x00, 0x01}; + NetworkReader reader = new NetworkReader(bytes); + Assert.That(reader.Remaining, Is.EqualTo(2)); + + reader.ReadByte(); + Assert.That(reader.Remaining, Is.EqualTo(1)); + + reader.ReadByte(); + Assert.That(reader.Remaining, Is.EqualTo(0)); + } + + [Test] + public void ReadBytesCountTooBigTest() + { + // calling ReadBytes with a count bigger than what is in Reader + // should throw an exception + byte[] bytes = { 0x00, 0x01 }; + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(bytes)) + { + try + { + byte[] result = reader.ReadBytes(bytes, bytes.Length + 1); + // BAD: IF WE GOT HERE, THEN NO EXCEPTION WAS THROWN + Assert.Fail(); + } + catch (EndOfStreamException) + { + // GOOD + } + } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkReaderTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkReaderTest.cs.meta new file mode 100644 index 000000000..ce21bed1d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkReaderTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3799af27efdf144909de8790851053a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkReaderWriterTest.cs b/Assets/Mirror/Tests/Editor/NetworkReaderWriterTest.cs new file mode 100644 index 000000000..32fa7838f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkReaderWriterTest.cs @@ -0,0 +1,31 @@ +using NUnit.Framework; + +namespace Mirror.Tests +{ + [TestFixture] + public class NetworkReaderWriterTest + { + [Test] + public void TestIntWriterNotNull() + { + Assert.That(Writer.write, Is.Not.Null); + } + + [Test] + public void TestIntReaderNotNull() + { + Assert.That(Reader.read, Is.Not.Null); + } + + [Test] + public void TestAccessingCustomWriterAndReader() + { + NetworkWriter writer = new NetworkWriter(); + writer.Write(3); + NetworkReader reader = new NetworkReader(writer.ToArray()); + int copy = reader.Read(); + + Assert.That(copy, Is.EqualTo(3)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkReaderWriterTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkReaderWriterTest.cs.meta new file mode 100644 index 000000000..1e4a29f9b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkReaderWriterTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4db12e3e883ac4c3aac177c638ee93e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkServerTest.cs b/Assets/Mirror/Tests/Editor/NetworkServerTest.cs new file mode 100644 index 000000000..bc126e84d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkServerTest.cs @@ -0,0 +1,1323 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Threading; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests +{ + struct TestMessage1 : NetworkMessage {} + + struct VariableSizedMessage : NetworkMessage + { + // weaver serializes byte[] wit WriteBytesAndSize + public byte[] payload; + // so payload := size - 4 + // then the message is exactly maxed size. + // + // NOTE: we have a LargerMaxMessageSize test which guarantees that + // variablesized + 1 is exactly transport.max + 1 + public VariableSizedMessage(int size) => payload = new byte[size - 4]; + } + + public class CommandTestNetworkBehaviour : NetworkBehaviour + { + // counter to make sure that it's called exactly once + public int called; + + [Command] + public void TestCommand() => ++called; + } + + public class RpcTestNetworkBehaviour : NetworkBehaviour + { + // counter to make sure that it's called exactly once + public int called; + // weaver generates this from [Rpc] + // but for tests we need to add it manually + public static void RpcGenerated(NetworkBehaviour comp, NetworkReader reader, NetworkConnection senderConnection) + { + ++((RpcTestNetworkBehaviour)comp).called; + } + } + + public class OnStartClientTestNetworkBehaviour : NetworkBehaviour + { + // counter to make sure that it's called exactly once + public int called; + public override void OnStartClient() => ++called; + } + + public class OnStopClientTestNetworkBehaviour : NetworkBehaviour + { + // counter to make sure that it's called exactly once + public int called; + public override void OnStopClient() => ++called; + } + + [TestFixture] + public class NetworkServerTest : MirrorEditModeTest + { + [Test] + public void IsActive() + { + Assert.That(NetworkServer.active, Is.False); + NetworkServer.Listen(1); + Assert.That(NetworkServer.active, Is.True); + NetworkServer.Shutdown(); + Assert.That(NetworkServer.active, Is.False); + } + + [Test] + public void MaxConnections() + { + // listen with maxconnections=1 + NetworkServer.Listen(1); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + + // connect first: should work + transport.OnServerConnected.Invoke(42); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + + // connect second: should fail + transport.OnServerConnected.Invoke(43); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + } + + [Test] + public void OnConnectedEventCalled() + { + // message handlers + bool connectCalled = false; + NetworkServer.OnConnectedEvent = conn => connectCalled = true; + + // listen & connect + NetworkServer.Listen(1); + transport.OnServerConnected.Invoke(42); + Assert.That(connectCalled, Is.True); + } + + [Test] + public void OnDisconnectedEventCalled() + { + // message handlers + bool disconnectCalled = false; + NetworkServer.OnDisconnectedEvent = conn => disconnectCalled = true; + + // listen & connect + NetworkServer.Listen(1); + transport.OnServerConnected.Invoke(42); + + // disconnect + transport.OnServerDisconnected.Invoke(42); + Assert.That(disconnectCalled, Is.True); + } + + [Test] + public void ConnectionsDict() + { + // listen + NetworkServer.Listen(2); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + + // connect first + transport.OnServerConnected.Invoke(42); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + Assert.That(NetworkServer.connections.ContainsKey(42), Is.True); + + // connect second + transport.OnServerConnected.Invoke(43); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(2)); + Assert.That(NetworkServer.connections.ContainsKey(43), Is.True); + + // disconnect second + transport.OnServerDisconnected.Invoke(43); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + Assert.That(NetworkServer.connections.ContainsKey(42), Is.True); + + // disconnect first + transport.OnServerDisconnected.Invoke(42); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + } + + [Test] + public void OnConnectedOnlyAllowsNonZeroConnectionIds() + { + // OnConnected should only allow connectionIds >= 0 + // 0 is for local player + // <0 is never used + + // listen + NetworkServer.Listen(2); + + // connect with connectionId == 0 should fail + // (it will show an error message, which is expected) + LogAssert.ignoreFailingMessages = true; + transport.OnServerConnected.Invoke(0); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + LogAssert.ignoreFailingMessages = false; + } + + [Test] + public void ConnectDuplicateConnectionIds() + { + // listen + NetworkServer.Listen(2); + + // connect first + transport.OnServerConnected.Invoke(42); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + NetworkConnectionToClient original = NetworkServer.connections[42]; + + // connect duplicate - shouldn't overwrite first one + transport.OnServerConnected.Invoke(42); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + Assert.That(NetworkServer.connections[42], Is.EqualTo(original)); + } + + [Test] + public void SetLocalConnection() + { + // listen + NetworkServer.Listen(1); + + // set local connection + LocalConnectionToClient localConnection = new LocalConnectionToClient(); + NetworkServer.SetLocalConnection(localConnection); + Assert.That(NetworkServer.localConnection, Is.EqualTo(localConnection)); + } + + [Test] + public void SetLocalConnection_PreventsOverwrite() + { + // listen + NetworkServer.Listen(1); + + // set local connection + LocalConnectionToClient localConnection = new LocalConnectionToClient(); + NetworkServer.SetLocalConnection(localConnection); + + // try to overwrite it, which should not work + // (it will show an error message, which is expected) + LogAssert.ignoreFailingMessages = true; + NetworkServer.SetLocalConnection(new LocalConnectionToClient()); + Assert.That(NetworkServer.localConnection, Is.EqualTo(localConnection)); + LogAssert.ignoreFailingMessages = false; + } + + [Test] + public void RemoveLocalConnection() + { + // listen + NetworkServer.Listen(1); + + // set local connection + CreateLocalConnectionPair(out LocalConnectionToClient connectionToClient, out _); + NetworkServer.SetLocalConnection(connectionToClient); + + // remove local connection + NetworkServer.RemoveLocalConnection(); + Assert.That(NetworkServer.localConnection, Is.Null); + } + + [Test] + public void LocalClientActive() + { + // listen + NetworkServer.Listen(1); + Assert.That(NetworkServer.localClientActive, Is.False); + + // set local connection + NetworkServer.SetLocalConnection(new LocalConnectionToClient()); + Assert.That(NetworkServer.localClientActive, Is.True); + } + + [Test] + public void AddConnection() + { + // listen + NetworkServer.Listen(1); + + // add first connection + NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); + Assert.That(NetworkServer.AddConnection(conn42), Is.True); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); + + // add second connection + NetworkConnectionToClient conn43 = new NetworkConnectionToClient(43); + Assert.That(NetworkServer.AddConnection(conn43), Is.True); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(2)); + Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); + Assert.That(NetworkServer.connections[43], Is.EqualTo(conn43)); + } + + [Test] + public void AddConnection_PreventsDuplicates() + { + // listen + NetworkServer.Listen(1); + + // add a connection + NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); + Assert.That(NetworkServer.AddConnection(conn42), Is.True); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); + + // add duplicate connectionId + NetworkConnectionToClient connDup = new NetworkConnectionToClient(42); + Assert.That(NetworkServer.AddConnection(connDup), Is.False); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); + } + + [Test] + public void RemoveConnection() + { + // listen + NetworkServer.Listen(1); + + // add connection + NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); + Assert.That(NetworkServer.AddConnection(conn42), Is.True); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + + // remove connection + Assert.That(NetworkServer.RemoveConnection(42), Is.True); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + } + + [Test] + public void DisconnectAllTest_RemoteConnection() + { + // listen + NetworkServer.Listen(1); + + // add connection + NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); + NetworkServer.AddConnection(conn42); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + + // disconnect all connections + NetworkServer.DisconnectAll(); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + } + + [Test] + public void DisconnectAllTest_LocalConnection() + { + // listen + NetworkServer.Listen(1); + + // set local connection + LocalConnectionToClient localConnection = new LocalConnectionToClient(); + NetworkServer.SetLocalConnection(localConnection); + + // disconnect all connections should remove local connection + NetworkServer.DisconnectAll(); + Assert.That(NetworkServer.localConnection, Is.Null); + } + + // test to reproduce https://github.com/vis2k/Mirror/pull/2797 + [Test] + public void Destroy_HostMode_CallsOnStopAuthority() + { + // listen & connect a HOST client + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // spawn a player(!) object + // otherwise client wouldn't receive spawn / authority messages + CreateNetworkedAndSpawnPlayer(out _, out NetworkIdentity player, + out StopAuthorityCalledNetworkBehaviour comp, + NetworkServer.localConnection); + + // need to have authority for this test + Assert.That(player.hasAuthority, Is.True); + + // destroy and ignore 'Destroy called in Edit mode' error + LogAssert.ignoreFailingMessages = true; + NetworkServer.Destroy(player.gameObject); + LogAssert.ignoreFailingMessages = false; + + // destroy should call OnStopAuthority + Assert.That(comp.called, Is.EqualTo(1)); + } + + // send a message all the way from client to server + [Test] + public void Send_ClientToServerMessage() + { + // register a message handler + int called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // send message & process + NetworkClient.Send(new TestMessage1()); + ProcessMessages(); + + // did it get through? + Assert.That(called, Is.EqualTo(1)); + } + + [Test] + public void Send_ServerToClientMessage() + { + // register a message handler + int called = 0; + NetworkClient.RegisterHandler(msg => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send message & process + connectionToClient.Send(new TestMessage1()); + ProcessMessages(); + + // did it get through? + Assert.That(called, Is.EqualTo(1)); + } + + // guarantee that exactly max packet size messages work + [Test] + public void Send_ClientToServerMessage_MaxMessageSize() + { + // register a message handler + int called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // send message & process + int max = MessagePacking.MaxContentSize; + NetworkClient.Send(new VariableSizedMessage(max)); + ProcessMessages(); + + // did it get through? + Assert.That(called, Is.EqualTo(1)); + } + + // guarantee that exactly max packet size messages work + [Test] + public void Send_ServerToClientMessage_MaxMessageSize() + { + // register a message handler + int called = 0; + NetworkClient.RegisterHandler(msg => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send message & process + int max = MessagePacking.MaxContentSize; + connectionToClient.Send(new VariableSizedMessage(max)); + ProcessMessages(); + + // did it get through? + Assert.That(called, Is.EqualTo(1)); + } + + // guarantee that exactly max message size + 1 doesn't work anymore + [Test] + public void Send_ClientToServerMessage_LargerThanMaxMessageSize() + { + // register a message handler + int called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // calculate max := transport.max - message header + + // send message & process + int transportMax = transport.GetMaxPacketSize(Channels.Reliable); + int messageMax = MessagePacking.MaxContentSize; + LogAssert.Expect(LogType.Error, $"NetworkConnection.ValidatePacketSize: cannot send packet larger than {transportMax} bytes, was {transportMax + 1} bytes"); + NetworkClient.Send(new VariableSizedMessage(messageMax + 1)); + ProcessMessages(); + + // should be too big to send + Assert.That(called, Is.EqualTo(0)); + } + + // guarantee that exactly max message size + 1 doesn't work anymore + [Test] + public void Send_ServerToClientMessage_LargerThanMaxMessageSize() + { + // register a message handler + int called = 0; + NetworkClient.RegisterHandler(msg => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send message & process + int transportMax = transport.GetMaxPacketSize(Channels.Reliable); + int messageMax = MessagePacking.MaxContentSize; + LogAssert.Expect(LogType.Error, $"NetworkConnection.ValidatePacketSize: cannot send packet larger than {transportMax} bytes, was {transportMax + 1} bytes"); + connectionToClient.Send(new VariableSizedMessage(messageMax + 1)); + ProcessMessages(); + + // should be too big to send + Assert.That(called, Is.EqualTo(0)); + } + + // transport recommends a max batch size. + // but we support up to max packet size. + // for example, with KCP it makes sense to always send MTU sized batches. + // but we can send up to 144 KB messages. + // => make sure this works. it's a special path in the code and used to + // cause a bug in uMMORPG where SpawnMessage would be > MTU, the + // timestamp would not be included because > max batch, hence client + // couldn't parse it properly. + [Test] + public void Send_ClientToServerMessage_LargerThanBatchThreshold() + { + // register a message handler + int called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // send message & process + int threshold = transport.GetBatchThreshold(Channels.Reliable); + NetworkClient.Send(new VariableSizedMessage(threshold + 1)); + ProcessMessages(); + + // did it get through? + Assert.That(called, Is.EqualTo(1)); + } + + // transport recommends a max batch size. + // but we support up to max packet size. + // for example, with KCP it makes sense to always send MTU sized batches. + // but we can send up to 144 KB messages. + // => make sure this works. it's a special path in the code and used to + // cause a bug in uMMORPG where SpawnMessage would be > MTU, the + // timestamp would not be included because > max batch, hence client + // couldn't parse it properly. + [Test] + public void Send_ServerToClientMessage_LargerThanBatchThreshold() + { + // register handler + int called = 0; + NetworkClient.RegisterHandler(msg => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send large message & process + int threshold = transport.GetBatchThreshold(Channels.Reliable); + connectionToClient.Send(new VariableSizedMessage(threshold + 1)); + ProcessMessages(); + + // did it get through? + Assert.That(called, Is.EqualTo(1)); + } + + // there used to be a data race where messages > batch threshold would + // be sent directly, instead of being flushed at the end of the frame + // like all the smaller messages. + // make sure this never happens again. + [Test] + public void Send_ClientToServerMessage_LargerThanBatchThreshold_SentInOrder() + { + // register two message handlers + List received = new List(); + NetworkServer.RegisterHandler((conn, msg) => received.Add("smol"), false); + NetworkServer.RegisterHandler((conn, msg) => received.Add("big"), false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // send small message first + NetworkClient.Send(new TestMessage1()); + + // send large message + int threshold = transport.GetBatchThreshold(Channels.Reliable); + NetworkClient.Send(new VariableSizedMessage(threshold + 1)); + + // process everything + ProcessMessages(); + + // both arrived, and small arrived before large? + Assert.That(received.Count, Is.EqualTo(2)); + Assert.That(received[0], Is.EqualTo("smol")); + Assert.That(received[1], Is.EqualTo("big")); + } + + // there used to be a data race where messages > batch threshold would + // be sent directly, instead of being flushed at the end of the frame + // like all the smaller messages. + // make sure this never happens again. + [Test] + public void Send_ServerToClientMessage_LargerThanBatchThreshold_SentInOrder() + { + // register two message handlers + List received = new List(); + NetworkClient.RegisterHandler(msg => received.Add("smol"), false); + NetworkClient.RegisterHandler(msg => received.Add("big"), false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send small message first + connectionToClient.Send(new TestMessage1()); + + // send large message + int threshold = transport.GetBatchThreshold(Channels.Reliable); + connectionToClient.Send(new VariableSizedMessage(threshold + 1)); + + // process everything + ProcessMessages(); + + // both arrived, and small arrived before large? + Assert.That(received.Count, Is.EqualTo(2)); + Assert.That(received[0], Is.EqualTo("smol")); + Assert.That(received[1], Is.EqualTo("big")); + } + + // make sure NetworkConnection.remoteTimeStamp is always the time on the + // remote end when the message was sent + [Test] + public void Send_ClientToServerMessage_SetsRemoteTimeStamp() + { + // register a message handler + int called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send message + NetworkClient.Send(new TestMessage1()); + + // remember current time & update NetworkClient IMMEDIATELY so the + // batch is finished with timestamp. + double sendTime = NetworkTime.localTime; + NetworkClient.NetworkLateUpdate(); + + // let some time pass before processing + const int waitTime = 100; + Thread.Sleep(waitTime); + ProcessMessages(); + + // is the remote timestamp set to when we sent it? + // remember the time when we sent the message + // (within 1/10th of the time we waited. we need some tolerance + // because we don't capture NetworkTime.localTime exactly when we + // finish the batch. but the difference should not be > 'waitTime') + Assert.That(called, Is.EqualTo(1)); + Assert.That(connectionToClient.remoteTimeStamp, Is.EqualTo(sendTime).Within(waitTime / 10)); + } + + // test to avoid https://github.com/vis2k/Mirror/issues/2882 + // messages in a batch aren't length prefixed. + // if we can't read one, we need to warn and disconnect. + // otherwise it overlaps to the next message and causes undefined behaviour. + [Test] + public void Send_ClientToServerMessage_UnknownMessageIdDisconnects() + { + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send a message without a registered handler + NetworkClient.Send(new TestMessage1()); + ProcessMessages(); + + // should have been disconnected + Assert.That(NetworkServer.connections.ContainsKey(connectionToClient.connectionId), Is.False); + } + + [Test] + public void Send_ServerToClientMessage_SetsRemoteTimeStamp() + { + // register a message handler + int called = 0; + NetworkClient.RegisterHandler(msg => ++called, false); + + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send message + connectionToClient.Send(new TestMessage1()); + + // remember current time & update NetworkClient IMMEDIATELY so the + // batch is finished with timestamp. + double sendTime = NetworkTime.localTime; + NetworkServer.NetworkLateUpdate(); + + // let some time pass before processing + const int waitTime = 100; + Thread.Sleep(waitTime); + ProcessMessages(); + + // is the remote timestamp set to when we sent it? + // remember the time when we sent the message + // (within 1/10th of the time we waited. we need some tolerance + // because we don't capture NetworkTime.localTime exactly when we + // finish the batch. but the difference should not be > 'waitTime') + Assert.That(called, Is.EqualTo(1)); + Assert.That(NetworkClient.connection.remoteTimeStamp, Is.EqualTo(sendTime).Within(waitTime / 10)); + } + + // test to avoid https://github.com/vis2k/Mirror/issues/2882 + // messages in a batch aren't length prefixed. + // if we can't read one, we need to warn and disconnect. + // otherwise it overlaps to the next message and causes undefined behaviour. + [Test] + public void Send_ServerToClientMessage_UnknownMessageIdDisconnects() + { + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient connectionToClient); + + // send a message without a registered handler + connectionToClient.Send(new TestMessage1()); + ProcessMessages(); + + // should have been disconnected + Assert.That(NetworkClient.active, Is.False); + } + + [Test] + public void OnDataReceivedInvalidConnectionId() + { + // register a message handler + int called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++called, false); + + // listen + NetworkServer.Listen(1); + + // serialize a test message into an arraysegment + byte[] message = MessagePackingTest.PackToByteArray(new TestMessage1()); + + // call transport.OnDataReceived with an invalid connectionId + // an error log is expected. + LogAssert.ignoreFailingMessages = true; + transport.OnServerDataReceived.Invoke(42, new ArraySegment(message), 0); + LogAssert.ignoreFailingMessages = false; + + // message handler should never be called + Assert.That(called, Is.EqualTo(0)); + } + + [Test] + public void SetClientReadyAndNotReady() + { + CreateLocalConnectionPair(out LocalConnectionToClient connectionToClient, out _); + Assert.That(connectionToClient.isReady, Is.False); + + NetworkServer.SetClientReady(connectionToClient); + Assert.That(connectionToClient.isReady, Is.True); + + NetworkServer.SetClientNotReady(connectionToClient); + Assert.That(connectionToClient.isReady, Is.False); + } + + [Test] + public void SetAllClientsNotReady() + { + // add first ready client + CreateLocalConnectionPair(out LocalConnectionToClient first, out _); + first.isReady = true; + NetworkServer.connections[42] = first; + + // add second ready client + CreateLocalConnectionPair(out LocalConnectionToClient second, out _); + second.isReady = true; + NetworkServer.connections[43] = second; + + // set all not ready + NetworkServer.SetAllClientsNotReady(); + Assert.That(first.isReady, Is.False); + Assert.That(second.isReady, Is.False); + } + + [Test] + public void ReadyMessageSetsClientReady() + { + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlockingAuthenticatedAndReady(out NetworkConnectionToClient connectionToClient); + Assert.That(connectionToClient.isReady, Is.True); + } + + // simply send a [Command] from client to server + [Test] + public void SendCommand() + { + // listen & connect + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // add an identity with two networkbehaviour components + // spawned, otherwise command handler won't find it in .spawned. + // WITH OWNER = WITH AUTHORITY + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out CommandTestNetworkBehaviour comp, NetworkServer.localConnection); + + // call the command + comp.TestCommand(); + ProcessMessages(); + Assert.That(comp.called, Is.EqualTo(1)); + } + + // send a [Command] to an entity with TWO command components. + // make sure the correct one is called. + [Test] + public void SendCommand_CalledOnCorrectComponent() + { + // listen & connect + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // add an identity with two networkbehaviour components. + // spawned, otherwise command handler won't find it in .spawned. + // WITH OWNER = WITH AUTHORITY + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out CommandTestNetworkBehaviour comp0, out CommandTestNetworkBehaviour comp1, NetworkServer.localConnection); + + // call the command + comp1.TestCommand(); + ProcessMessages(); + Assert.That(comp0.called, Is.EqualTo(0)); + Assert.That(comp1.called, Is.EqualTo(1)); + } + + [Test] + public void SendCommand_OnlyAllowedOnOwnedObjects() + { + // listen & connect + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // add an identity with two networkbehaviour components + // spawned, otherwise command handler won't find it in .spawned. + // WITH OWNER = WITH AUTHORITY + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity identity, out CommandTestNetworkBehaviour comp, NetworkServer.localConnection); + + // change identity's owner connection so we can't call [Commands] on it + identity.connectionToClient = new LocalConnectionToClient(); + + // call the command + comp.TestCommand(); + ProcessMessages(); + Assert.That(comp.called, Is.EqualTo(0)); + } + + [Test] + public void SendCommand_RequiresAuthority() + { + // listen & connect + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // add an identity with two networkbehaviour components + // spawned, otherwise command handler won't find it in .spawned. + // WITHOUT OWNER = WITHOUT AUTHORITY for this test + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out CommandTestNetworkBehaviour comp); + + // call the command + comp.TestCommand(); + ProcessMessages(); + Assert.That(comp.called, Is.EqualTo(0)); + } + + [Test] + public void ActivateHostSceneCallsOnStartClient() + { + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlockingAuthenticatedAndReady(out _); + + // spawn identity with a networkbehaviour. + // (needs to be in .spawned for ActivateHostScene) + CreateNetworkedAndSpawn( + out _, out NetworkIdentity serverIdentity, out OnStartClientTestNetworkBehaviour serverComp, + out _, out _, out _); + + // ActivateHostScene calls OnStartClient for spawned objects where + // isClient is still false. set it to false first. + serverIdentity.isClient = false; + NetworkServer.ActivateHostScene(); + + // was OnStartClient called for all .spawned networkidentities? + Assert.That(serverComp.called, Is.EqualTo(1)); + } + + [Test] + public void SendToAll() + { + // message handler + int called = 0; + NetworkClient.RegisterHandler(msg => ++called, false); + + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // send & process + NetworkServer.SendToAll(new TestMessage1()); + ProcessMessages(); + + // called? + Assert.That(called, Is.EqualTo(1)); + } + + [Test] + public void UnregisterHandler() + { + // RegisterHandler(conn, msg) variant + int variant1Called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++variant1Called, false); + + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // send a message, check if it was handled + NetworkClient.Send(new TestMessage1()); + ProcessMessages(); + Assert.That(variant1Called, Is.EqualTo(1)); + + // unregister, send again, should not be called again + NetworkServer.UnregisterHandler(); + NetworkClient.Send(new TestMessage1()); + ProcessMessages(); + Assert.That(variant1Called, Is.EqualTo(1)); + } + + [Test] + public void ClearHandler() + { + // RegisterHandler(conn, msg) variant + int variant1Called = 0; + NetworkServer.RegisterHandler((conn, msg) => ++variant1Called, false); + + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlocking(out _); + + // send a message, check if it was handled + NetworkClient.Send(new TestMessage1()); + ProcessMessages(); + Assert.That(variant1Called, Is.EqualTo(1)); + + // clear handlers, send again, should not be called again + NetworkServer.ClearHandlers(); + NetworkClient.Send(new TestMessage1()); + ProcessMessages(); + Assert.That(variant1Called, Is.EqualTo(1)); + } + + [Test] + public void GetNetworkIdentity() + { + // create a GameObject with NetworkIdentity + CreateNetworked(out GameObject go, out NetworkIdentity identity); + + // GetNetworkIdentity + bool result = NetworkServer.GetNetworkIdentity(go, out NetworkIdentity value); + Assert.That(result, Is.True); + Assert.That(value, Is.EqualTo(identity)); + } + + [Test] + public void GetNetworkIdentity_ErrorIfNotFound() + { + // create a GameObject without NetworkIdentity + CreateGameObject(out GameObject goWithout); + + // GetNetworkIdentity for GO without identity + LogAssert.Expect(LogType.Error, $"GameObject {goWithout.name} doesn't have NetworkIdentity."); + bool result = NetworkServer.GetNetworkIdentity(goWithout, out NetworkIdentity value); + Assert.That(result, Is.False); + Assert.That(value, Is.Null); + } + + [Test] + public void ShowForConnection() + { + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlockingAuthenticatedAndReady(out NetworkConnectionToClient connectionToClient); + + // overwrite spawn message handler + int called = 0; + NetworkClient.ReplaceHandler(msg => ++called, false); + + // create a gameobject and networkidentity and some unique values + CreateNetworked(out GameObject _, out NetworkIdentity identity); + identity.connectionToClient = connectionToClient; + + // call ShowForConnection + NetworkServer.ShowForConnection(identity, connectionToClient); + ProcessMessages(); + Assert.That(called, Is.EqualTo(1)); + + // destroy manually to avoid 'Destroy can't be called in edit mode' + GameObject.DestroyImmediate(identity.gameObject); + } + + [Test] + public void ShowForConnection_OnlyWorksIfReady() + { + // listen & connect + // DO NOT set ready this time + NetworkServer.Listen(1); + ConnectClientBlockingAuthenticated(out NetworkConnectionToClient connectionToClient); + + // overwrite spawn message handler + int called = 0; + NetworkClient.ReplaceHandler(msg => ++called, false); + + // create a gameobject and networkidentity and some unique values + CreateNetworked(out GameObject _, out NetworkIdentity identity); + identity.connectionToClient = connectionToClient; + + // call ShowForConnection - should not work if not ready + NetworkServer.ShowForConnection(identity, connectionToClient); + ProcessMessages(); + Assert.That(called, Is.EqualTo(0)); + + // destroy manually to avoid 'Destroy can't be called in edit mode' + GameObject.DestroyImmediate(identity.gameObject); + } + + [Test] + public void HideForConnection() + { + // listen & connect + NetworkServer.Listen(1); + ConnectClientBlockingAuthenticatedAndReady(out NetworkConnectionToClient connectionToClient); + + // overwrite spawn message handler + int called = 0; + NetworkClient.ReplaceHandler(msg => ++called, false); + + // create a gameobject and networkidentity and some unique values + CreateNetworked(out GameObject _, out NetworkIdentity identity); + identity.connectionToClient = connectionToClient; + + // call HideForConnection + NetworkServer.HideForConnection(identity, connectionToClient); + ProcessMessages(); + Assert.That(called, Is.EqualTo(1)); + + // destroy manually to avoid 'Destroy can't be called in edit mode' + GameObject.DestroyImmediate(identity.gameObject); + } + + [Test] + public void ValidateSceneObject() + { + // create a gameobject and networkidentity + CreateNetworked(out GameObject go, out NetworkIdentity identity); + identity.sceneId = 42; + + // should be valid as long as it has a sceneId + Assert.That(NetworkServer.ValidateSceneObject(identity), Is.True); + + // shouldn't be valid with 0 sceneID + identity.sceneId = 0; + Assert.That(NetworkServer.ValidateSceneObject(identity), Is.False); + identity.sceneId = 42; + + // shouldn't be valid for certain hide flags + go.hideFlags = HideFlags.NotEditable; + Assert.That(NetworkServer.ValidateSceneObject(identity), Is.False); + go.hideFlags = HideFlags.HideAndDontSave; + Assert.That(NetworkServer.ValidateSceneObject(identity), Is.False); + } + + [Test] + public void SpawnObjects() + { + // create a scene object and set inactive before spawning + CreateNetworked(out GameObject go, out NetworkIdentity identity); + identity.sceneId = 42; + go.SetActive(false); + + // create a NON scene object and set inactive before spawning + CreateNetworked(out GameObject go2, out NetworkIdentity identity2); + identity2.sceneId = 0; + go2.SetActive(false); + + // start server + NetworkServer.Listen(1); + + // SpawnObjects() should return true and activate the scene object + Assert.That(NetworkServer.SpawnObjects(), Is.True); + Assert.That(go.activeSelf, Is.True); + Assert.That(go2.activeSelf, Is.False); + + // reset isServer to avoid Destroy instead of DestroyImmediate + identity.isServer = false; + identity2.isServer = false; + } + + [Test] + public void SpawnObjects_OnlyIfServerActive() + { + // calling SpawnObjects while server isn't active should do nothing + Assert.That(NetworkServer.SpawnObjects(), Is.False); + } + + [Test] + public void UnSpawn() + { + // create scene object with valid netid and set active + CreateNetworked(out GameObject go, out NetworkIdentity identity); + identity.sceneId = 42; + identity.netId = 123; + go.SetActive(true); + + // unspawn should reset netid + NetworkServer.UnSpawn(go); + Assert.That(identity.netId, Is.Zero); + } + + [Test] + public void UnSpawnAndClearAuthority() + { + // create scene object with valid netid and set active + CreateNetworked(out GameObject go, out NetworkIdentity identity, out StartAuthorityCalledNetworkBehaviour compStart, out StopAuthorityCalledNetworkBehaviour compStop); + identity.sceneId = 42; + identity.netId = 123; + go.SetActive(true); + + // set authority from false to true, which should call OnStartAuthority + identity.hasAuthority = true; + identity.NotifyAuthority(); + + // shouldn't be touched + Assert.That(identity.hasAuthority, Is.True); + // start should be called + Assert.That(compStart.called, Is.EqualTo(1)); + // stop shouldn't + Assert.That(compStop.called, Is.EqualTo(0)); + + // unspawn should reset netid and remove authority + NetworkServer.UnSpawn(go); + Assert.That(identity.netId, Is.Zero); + + // should be changed + Assert.That(identity.hasAuthority, Is.False); + // same as before + Assert.That(compStart.called, Is.EqualTo(1)); + // stop should be called + Assert.That(compStop.called, Is.EqualTo(1)); + } + + // test to reproduce a bug where stopping the server would not call + // OnStopServer on scene objects: + // https://github.com/vis2k/Mirror/issues/2119 + [Test] + public void Shutdown_CallsSceneObjectsOnStopServer() + { + // listen & connect a client + NetworkServer.Listen(1); + ConnectClientBlocking(out NetworkConnectionToClient _); + + // create & spawn an object + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity identity, + out StopServerCalledNetworkBehaviour comp); + + // make sure it was spawned as a scene object. + // they don't come from prefabs, so they always are. + Assert.That(identity.sceneId, !Is.Null); + + // shutdown should call OnStopServer etc. + NetworkServer.Shutdown(); + Assert.That(comp.called, Is.EqualTo(1)); + } + + [Test] + public void ShutdownCleanup() + { + // listen + NetworkServer.Listen(1); + + // 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. + NetworkServer.OnConnectedEvent = connection => {}; + NetworkServer.OnDisconnectedEvent = connection => {}; + + // set local connection + NetworkServer.SetLocalConnection(new LocalConnectionToClient()); + + // connect a client + transport.ClientConnect("localhost"); + UpdateTransport(); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + + // shutdown + NetworkServer.Shutdown(); + + // state cleared? + Assert.That(NetworkServer.dontListen, Is.False); + Assert.That(NetworkServer.active, Is.False); + Assert.That(NetworkServer.isLoadingScene, Is.False); + + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + Assert.That(NetworkServer.connectionsCopy.Count, Is.EqualTo(0)); + Assert.That(NetworkServer.handlers.Count, Is.EqualTo(0)); + Assert.That(NetworkServer.newObservers.Count, Is.EqualTo(0)); + Assert.That(NetworkServer.spawned.Count, Is.EqualTo(0)); + + Assert.That(NetworkServer.localConnection, Is.Null); + Assert.That(NetworkServer.localClientActive, Is.False); + + Assert.That(NetworkServer.OnConnectedEvent, Is.Null); + Assert.That(NetworkServer.OnDisconnectedEvent, Is.Null); + Assert.That(NetworkServer.OnErrorEvent, Is.Null); + } + + [Test] + public void SendToAll_CalledWhileNotActive_ShouldGiveWarning() + { + LogAssert.Expect(LogType.Warning, $"Can not send using NetworkServer.SendToAll(T msg) because NetworkServer is not active"); + NetworkServer.SendToAll(new NetworkPingMessage {}); + } + + [Test] + public void SendToReady_CalledWhileNotActive_ShouldGiveWarning() + { + LogAssert.Expect(LogType.Warning, $"Can not send using NetworkServer.SendToReady(T msg) because NetworkServer is not active"); + NetworkServer.SendToReady(new NetworkPingMessage {}); + } + + [Test] + public void NoExternalConnections_WithNoConnection() + { + Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); + Assert.That(NetworkServer.NoExternalConnections(), Is.True); + } + + [Test] + public void NoExternalConnections_WithConnections() + { + NetworkServer.connections.Add(1, null); + NetworkServer.connections.Add(2, null); + Assert.That(NetworkServer.NoExternalConnections(), Is.False); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(2)); + } + + [Test] + public void NoExternalConnections_WithHostOnly() + { + CreateLocalConnectionPair(out LocalConnectionToClient connectionToClient, out _); + + NetworkServer.SetLocalConnection(connectionToClient); + NetworkServer.connections.Add(0, connectionToClient); + + Assert.That(NetworkServer.NoExternalConnections(), Is.True); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); + + NetworkServer.RemoveLocalConnection(); + } + + [Test] + public void NoExternalConnections_WithHostAndConnection() + { + CreateLocalConnectionPair(out LocalConnectionToClient connectionToClient, out _); + + NetworkServer.SetLocalConnection(connectionToClient); + NetworkServer.connections.Add(0, connectionToClient); + NetworkServer.connections.Add(1, null); + + Assert.That(NetworkServer.NoExternalConnections(), Is.False); + Assert.That(NetworkServer.connections.Count, Is.EqualTo(2)); + + NetworkServer.RemoveLocalConnection(); + } + + // updating NetworkServer with a null entry in connection.observing + // should log a warning. someone probably used GameObject.Destroy + // instead of NetworkServer.Destroy. + [Test] + public void UpdateDetectsNullEntryInObserving() + { + // start + NetworkServer.Listen(1); + + // add a connection that is observed by a null entity + NetworkServer.connections[42] = new FakeNetworkConnection{isReady=true}; + NetworkServer.connections[42].observing.Add(null); + + // update + LogAssert.Expect(LogType.Warning, new Regex("Found 'null' entry in observing list.*")); + NetworkServer.NetworkLateUpdate(); + } + + // updating NetworkServer with a null entry in connection.observing + // should log a warning. someone probably used GameObject.Destroy + // instead of NetworkServer.Destroy. + // + // => need extra test because of Unity's custom null check + [Test] + public void UpdateDetectsDestroyedEntryInObserving() + { + // start + NetworkServer.Listen(1); + + // add a connection that is observed by a destroyed entity + CreateNetworked(out GameObject go, out NetworkIdentity ni); + NetworkServer.connections[42] = new FakeNetworkConnection{isReady=true}; + NetworkServer.connections[42].observing.Add(ni); + GameObject.DestroyImmediate(go); + + // update + LogAssert.Expect(LogType.Warning, new Regex("Found 'null' entry in observing list.*")); + NetworkServer.NetworkLateUpdate(); + } + + // SyncLists/Dict/Set .changes are only flushed when serializing. + // if an object has no observers, then serialize is never called. + // if we still keep changing the lists, then .changes would grow forever. + // => need to make sure that .changes doesn't grow while no observers. + [Test] + public void SyncObjectChanges_DontGrowWithoutObservers() + { + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // one monster + CreateNetworkedAndSpawn(out _, out NetworkIdentity identity, out NetworkBehaviourWithSyncVarsAndCollections comp); + + // without AOI, connections observer everything. + // clear the observers first. + identity.ClearObservers(); + + // insert into a synclist, which would add to .changes + comp.list.Add(42); + + // update everything once + ProcessMessages(); + + // changes should be empty since we have no observers + Assert.That(comp.list.GetChangeCount(), Is.EqualTo(0)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkServerTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkServerTest.cs.meta new file mode 100644 index 000000000..35d033b31 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkServerTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a47dc0cc3af7c49a18a4a1b8d7de9caa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkTransform2kTests.cs b/Assets/Mirror/Tests/Editor/NetworkTransform2kTests.cs new file mode 100644 index 000000000..748ae3fb9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkTransform2kTests.cs @@ -0,0 +1,362 @@ +// TODO add true over-the-network movement tests. +// but we need to split NetworkIdentity.spawned in server/client first. +// atm we can't spawn an object on both server & client separately yet. +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.NetworkTransform2k +{ + // helper class to expose some of the protected methods + public class NetworkTransformExposed : NetworkTransform + { + public new NTSnapshot ConstructSnapshot() => base.ConstructSnapshot(); + public new void ApplySnapshot(NTSnapshot start, NTSnapshot goal, NTSnapshot interpolated) => + base.ApplySnapshot(start, goal, interpolated); + public new void OnClientToServerSync(Vector3? position, Quaternion? rotation, Vector3? scale) => + base.OnClientToServerSync(position, rotation, scale); + public new void OnServerToClientSync(Vector3? position, Quaternion? rotation, Vector3? scale) => + base.OnServerToClientSync(position, rotation, scale); + } + + public class NetworkTransform2kTests : MirrorTest + { + // networked and spawned NetworkTransform + NetworkConnectionToClient connectionToClient; + Transform transform; + NetworkTransformExposed component; + + [SetUp] + public override void SetUp() + { + // set up world with server & client + // host mode for now. + // TODO separate client & server after .spawned split. + // we can use CreateNetworkedAndSpawn that creates on sv & cl. + // then move on server, update, verify client position etc. + base.SetUp(); + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + connectionToClient = NetworkServer.localConnection; + + // create a networked object with NetworkTransform + CreateNetworkedAndSpawn(out GameObject go, out NetworkIdentity _, out component, connectionToClient); + // sync immediately + component.syncInterval = 0; + // remember transform for convenience + transform = go.transform; + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + NetworkClient.Disconnect(); + } + + // TODO move to NTSnapshot tests? + [Test] + public void Interpolate() + { + NTSnapshot from = new NTSnapshot( + 1, + 1, + new Vector3(1, 1, 1), + Quaternion.Euler(new Vector3(0, 0, 0)), + new Vector3(3, 3, 3) + ); + + NTSnapshot to = new NTSnapshot( + 2, + 2, + new Vector3(2, 2, 2), + Quaternion.Euler(new Vector3(0, 90, 0)), + new Vector3(4, 4, 4) + ); + + // interpolate + NTSnapshot between = NTSnapshot.Interpolate(from, to, 0.5); + + // note: timestamp interpolation isn't needed. we don't use it. + //Assert.That(between.remoteTimestamp, Is.EqualTo(1.5).Within(Mathf.Epsilon)); + //Assert.That(between.localTimestamp, Is.EqualTo(1.5).Within(Mathf.Epsilon)); + + // check position + Assert.That(between.position.x, Is.EqualTo(1.5).Within(Mathf.Epsilon)); + Assert.That(between.position.y, Is.EqualTo(1.5).Within(Mathf.Epsilon)); + Assert.That(between.position.z, Is.EqualTo(1.5).Within(Mathf.Epsilon)); + + // check rotation + // (epsilon is slightly too small) + Assert.That(between.rotation.eulerAngles.x, Is.EqualTo(0).Within(Mathf.Epsilon)); + Assert.That(between.rotation.eulerAngles.y, Is.EqualTo(45).Within(0.001)); + Assert.That(between.rotation.eulerAngles.z, Is.EqualTo(0).Within(Mathf.Epsilon)); + + // check scale + Assert.That(between.scale.x, Is.EqualTo(3.5).Within(Mathf.Epsilon)); + Assert.That(between.scale.y, Is.EqualTo(3.5).Within(Mathf.Epsilon)); + Assert.That(between.scale.z, Is.EqualTo(3.5).Within(Mathf.Epsilon)); + } + + [Test] + public void ConstructSnapshot() + { + // set unique position/rotation/scale + transform.position = new Vector3(1, 2, 3); + transform.rotation = Quaternion.identity; + transform.localScale = new Vector3(4, 5, 6); + + // construct snapshot + double time = NetworkTime.localTime; + NTSnapshot snapshot = component.ConstructSnapshot(); + Assert.That(snapshot.remoteTimestamp, Is.EqualTo(time).Within(0.01)); + Assert.That(snapshot.position, Is.EqualTo(new Vector3(1, 2, 3))); + Assert.That(snapshot.rotation, Is.EqualTo(Quaternion.identity)); + Assert.That(snapshot.scale, Is.EqualTo(new Vector3(4, 5, 6))); + } + + [Test] + public void ApplySnapshot_Interpolated() + { + // construct snapshot with unique position/rotation/scale + Vector3 position = new Vector3(1, 2, 3); + Quaternion rotation = Quaternion.Euler(45, 90, 45); + Vector3 scale = new Vector3(4, 5, 6); + + // apply snapshot with interpolation + component.syncPosition = true; + component.syncRotation = true; + component.syncScale = true; + component.interpolatePosition = true; + component.interpolateRotation = true; + component.interpolateScale = true; + component.ApplySnapshot(default, default, new NTSnapshot(0, 0, position, rotation, scale)); + + // was it applied? + Assert.That(transform.position, Is.EqualTo(position)); + Assert.That(Quaternion.Angle(transform.rotation, rotation), Is.EqualTo(0).Within(Mathf.Epsilon)); + Assert.That(transform.localScale, Is.EqualTo(scale)); + } + + [Test] + public void ApplySnapshot_Direct() + { + // construct snapshot with unique position/rotation/scale + Vector3 position = new Vector3(1, 2, 3); + Quaternion rotation = Quaternion.Euler(45, 90, 45); + Vector3 scale = new Vector3(4, 5, 6); + + // apply snapshot without interpolation + component.syncPosition = true; + component.syncRotation = true; + component.syncScale = true; + component.interpolatePosition = false; + component.interpolateRotation = false; + component.interpolateScale = false; + component.ApplySnapshot(default, new NTSnapshot(0, 0, position, rotation, scale), default); + + // was it applied? + Assert.That(transform.position, Is.EqualTo(position)); + Assert.That(Quaternion.Angle(transform.rotation, rotation), Is.EqualTo(0).Within(Mathf.Epsilon)); + Assert.That(transform.localScale, Is.EqualTo(scale)); + } + + [Test] + public void ApplySnapshot_DontSyncPosition() + { + // construct snapshot with unique position/rotation/scale + Vector3 position = new Vector3(1, 2, 3); + Quaternion rotation = Quaternion.Euler(45, 90, 45); + Vector3 scale = new Vector3(4, 5, 6); + + // apply snapshot without position sync should not apply position + component.syncPosition = false; + component.syncRotation = true; + component.syncScale = true; + component.interpolatePosition = false; + component.interpolateRotation = true; + component.interpolateScale = true; + component.ApplySnapshot(default, default, new NTSnapshot(0, 0, position, rotation, scale)); + + // was it applied? + Assert.That(transform.position, Is.EqualTo(Vector3.zero)); + Assert.That(Quaternion.Angle(transform.rotation, rotation), Is.EqualTo(0).Within(Mathf.Epsilon)); + Assert.That(transform.localScale, Is.EqualTo(scale)); + } + + [Test] + public void ApplySnapshot_DontSyncRotation() + { + // construct snapshot with unique position/rotation/scale + Vector3 position = new Vector3(1, 2, 3); + Quaternion rotation = Quaternion.Euler(45, 90, 45); + Vector3 scale = new Vector3(4, 5, 6); + + // apply snapshot without position sync should not apply position + component.syncPosition = true; + component.syncRotation = false; + component.syncScale = true; + component.interpolatePosition = true; + component.interpolateRotation = false; + component.interpolateScale = true; + component.ApplySnapshot(default, default, new NTSnapshot(0, 0, position, rotation, scale)); + + // was it applied? + Assert.That(transform.position, Is.EqualTo(position)); + Assert.That(transform.rotation, Is.EqualTo(Quaternion.identity)); + Assert.That(transform.localScale, Is.EqualTo(scale)); + } + + [Test] + public void ApplySnapshot_DontSyncScale() + { + // construct snapshot with unique position/rotation/scale + Vector3 position = new Vector3(1, 2, 3); + Quaternion rotation = Quaternion.Euler(45, 90, 45); + Vector3 scale = new Vector3(4, 5, 6); + + // apply snapshot without position sync should not apply position + component.syncPosition = true; + component.syncRotation = true; + component.syncScale = false; + component.interpolatePosition = true; + component.interpolateRotation = true; + component.interpolateScale = false; + component.ApplySnapshot(default, default, new NTSnapshot(0, 0, position, rotation, scale)); + + // was it applied? + Assert.That(transform.position, Is.EqualTo(position)); + Assert.That(Quaternion.Angle(transform.rotation, rotation), Is.EqualTo(0).Within(Mathf.Epsilon)); + Assert.That(transform.localScale, Is.EqualTo(Vector3.one)); + } + + [Test] + public void OnClientToServerSync_WithoutClientAuthority() + { + // call OnClientToServerSync without authority + component.clientAuthority = false; + component.OnClientToServerSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.serverBuffer.Count, Is.EqualTo(0)); + } + + [Test] + public void OnClientToServerSync_WithClientAuthority() + { + // call OnClientToServerSync with authority + component.clientAuthority = true; + component.OnClientToServerSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.serverBuffer.Count, Is.EqualTo(1)); + } + + [Test] + public void OnClientToServerSync_WithClientAuthority_BufferSizeLimit() + { + component.bufferSizeLimit = 1; + + // authority is required + component.clientAuthority = true; + + // add first should work + component.OnClientToServerSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.serverBuffer.Count, Is.EqualTo(1)); + + // add second should be too much + component.OnClientToServerSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.serverBuffer.Count, Is.EqualTo(1)); + } + + [Test] + public void OnClientToServerSync_WithClientAuthority_Nullables_Uses_Last() + { + // set some defaults + transform.position = Vector3.left; + transform.rotation = Quaternion.identity; + transform.localScale = Vector3.right; + + // call OnClientToServerSync with authority and nullable types + // to make sure it uses the last valid position then. + component.clientAuthority = true; + component.OnClientToServerSync(new Vector3?(), new Quaternion?(), new Vector3?()); + Assert.That(component.serverBuffer.Count, Is.EqualTo(1)); + NTSnapshot first = component.serverBuffer.Values[0]; + Assert.That(first.position, Is.EqualTo(Vector3.left)); + Assert.That(first.rotation, Is.EqualTo(Quaternion.identity)); + Assert.That(first.scale, Is.EqualTo(Vector3.right)); + } + + // server->client sync should only work if client doesn't have authority + [Test] + public void OnServerToClientSync_WithoutClientAuthority() + { + // pretend to be the client object + component.netIdentity.isServer = false; + component.netIdentity.isClient = true; + component.netIdentity.isLocalPlayer = true; + + // call OnServerToClientSync without authority + component.clientAuthority = false; + component.OnServerToClientSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.clientBuffer.Count, Is.EqualTo(1)); + } + + // server->client sync shouldn't work if client has authority + [Test] + public void OnServerToClientSync_WithoutClientAuthority_bufferSizeLimit() + { + component.bufferSizeLimit = 1; + + // pretend to be the client object + component.netIdentity.isServer = false; + component.netIdentity.isClient = true; + component.netIdentity.isLocalPlayer = true; + + // client authority has to be disabled + component.clientAuthority = false; + + // add first should work + component.OnServerToClientSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.clientBuffer.Count, Is.EqualTo(1)); + + // add second should be too much + component.OnServerToClientSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.clientBuffer.Count, Is.EqualTo(1)); + } + + // server->client sync shouldn't work if client has authority + [Test] + public void OnServerToClientSync_WithClientAuthority() + { + // pretend to be the client object + component.netIdentity.isServer = false; + component.netIdentity.isClient = true; + component.netIdentity.isLocalPlayer = true; + + // call OnServerToClientSync with authority + component.clientAuthority = true; + component.OnServerToClientSync(Vector3.zero, Quaternion.identity, Vector3.zero); + Assert.That(component.clientBuffer.Count, Is.EqualTo(0)); + } + + [Test] + public void OnServerToClientSync_WithClientAuthority_Nullables_Uses_Last() + { + // set some defaults + transform.position = Vector3.left; + transform.rotation = Quaternion.identity; + transform.localScale = Vector3.right; + + // pretend to be the client object + component.netIdentity.isServer = false; + component.netIdentity.isClient = true; + component.netIdentity.isLocalPlayer = true; + + // call OnClientToServerSync with authority and nullable types + // to make sure it uses the last valid position then. + component.OnServerToClientSync(new Vector3?(), new Quaternion?(), new Vector3?()); + Assert.That(component.clientBuffer.Count, Is.EqualTo(1)); + NTSnapshot first = component.clientBuffer.Values[0]; + Assert.That(first.position, Is.EqualTo(Vector3.left)); + Assert.That(first.rotation, Is.EqualTo(Quaternion.identity)); + Assert.That(first.scale, Is.EqualTo(Vector3.right)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkTransform2kTests.cs.meta b/Assets/Mirror/Tests/Editor/NetworkTransform2kTests.cs.meta new file mode 100644 index 000000000..89de25521 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkTransform2kTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 757a5c351d7046538812958112ce74f3 +timeCreated: 1624953125 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/NetworkWriterCollectionTest.cs b/Assets/Mirror/Tests/Editor/NetworkWriterCollectionTest.cs new file mode 100644 index 000000000..93d6ce211 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkWriterCollectionTest.cs @@ -0,0 +1,67 @@ +using System; +using Mirror.Tests.RemoteAttrributeTest; +using NUnit.Framework; + +namespace Mirror.Tests +{ + public class NetworkWriterCollectionTest + { + [Test] + public void HasWriteFunctionForInt() + { + Assert.That(Writer.write, Is.Not.Null, "int write function was not found"); + + Action action = NetworkWriterExtensions.WriteInt; + Assert.That(Writer.write, Is.EqualTo(action), "int write function was incorrect value"); + } + + [Test] + public void HasReadFunctionForInt() + { + Assert.That(Reader.read, Is.Not.Null, "int read function was not found"); + + Func action = NetworkReaderExtensions.ReadInt; + Assert.That(Reader.read, Is.EqualTo(action), "int read function was incorrect value"); + } + + [Test] + public void HasWriteNetworkBehaviourFunction() + { + Assert.That(Writer.write, Is.Not.Null, "NetworkBehaviour read function was not found"); + + Action action = NetworkWriterExtensions.WriteNetworkBehaviour; + Assert.That(Writer.write, Is.EqualTo(action), "NetworkBehaviour read function was incorrect value"); + } + + [Test] + public void HasReadNetworkBehaviourFunction() + { + Assert.That(Reader.read, Is.Not.Null, "NetworkBehaviour read function was not found"); + + Func actionNonGeneric = NetworkReaderExtensions.ReadNetworkBehaviour; + Func actionGeneric = NetworkReaderExtensions.ReadNetworkBehaviour; + Assert.That(Reader.read, Is.EqualTo(actionNonGeneric).Or.EqualTo(actionGeneric), + "NetworkBehaviour read function was incorrect value, should be generic or non-generic"); + } + + [Test] + public void HasWriteNetworkBehaviourDerivedFunction() + { + // needs a networkbehaviour that is included in an Message/Rpc/syncvar for this test + Assert.That(Writer.write, Is.Not.Null, "RpcNetworkIdentityBehaviour read function was not found"); + + Action action = NetworkWriterExtensions.WriteNetworkBehaviour; + Assert.That(Writer.write, Is.EqualTo(action), "RpcNetworkIdentityBehaviour read function was incorrect value"); + } + + [Test] + public void HasReadNetworkBehaviourDerivedFunction() + { + Func reader = Reader.read; + Assert.That(reader, Is.Not.Null, "RpcNetworkIdentityBehaviour read function was not found"); + + Func action = NetworkReaderExtensions.ReadNetworkBehaviour; + Assert.That(reader, Is.EqualTo(action), "RpcNetworkIdentityBehaviour read function was incorrect value"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkWriterCollectionTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkWriterCollectionTest.cs.meta new file mode 100644 index 000000000..2dffba4eb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkWriterCollectionTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bfc7cd9211e6145418289e9c9572ce55 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs b/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs new file mode 100644 index 000000000..b40211e6e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs @@ -0,0 +1,1389 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Mirror.Tests.RemoteAttrributeTest; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + [TestFixture] + public class NetworkWriterTest : MirrorEditModeTest + { + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // start server & connect client because we need spawn functions + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + } + + /* uncomment if needed. commented for faster test workflow. this takes >3s. + [Test] + public void Benchmark() + { + // 10 million reads, Unity 2019.3, code coverage disabled + // 4014ms ms + NetworkWriter writer = new NetworkWriter(); + for (int i = 0; i < 10000000; ++i) + { + writer.SetLength(0); + writer.WriteVector3(new Vector3(1, 2, 3)); + } + } + */ + + // Write/ReadBlittable assumes same endianness on server & client. + [Test] + public void LittleEndianPlatform() + { + Assert.That(BitConverter.IsLittleEndian, Is.True); + } + + // some platforms may not support unaligned *(T*) reads/writes. + // but it still needs to work with our workaround. + // let's have an editor test to maybe catch it early. + // Editor runs Win/Mac/Linux and atm the issue only exists on Android, + // but let's have a test anyway. + // see also: https://github.com/vis2k/Mirror/issues/3044 + [Test] + public void WriteUnaligned() + { + NetworkWriter writer = new NetworkWriter(); + // make unaligned + writer.WriteByte(0xFF); + // write a double + writer.WriteDouble(Math.PI); + // should have written 9 bytes without throwing exceptions + Assert.That(writer.Position, Is.EqualTo(9)); + } + + [Test] + public void TestWritingSmallMessage() + { + // try serializing less than 32kb and see what happens + NetworkWriter writer = new NetworkWriter(); + for (int i = 0; i < 30000 / 4; ++i) + writer.WriteInt(i); + Assert.That(writer.Position, Is.EqualTo(30000)); + } + + [Test] + public void TestWritingLargeMessage() + { + // try serializing more than 32kb and see what happens + NetworkWriter writer = new NetworkWriter(); + for (int i = 0; i < 40000 / 4; ++i) + writer.WriteInt(i); + Assert.That(writer.Position, Is.EqualTo(40000)); + } + + [Test] + public void TestWritingHugeArray() + { + // try serializing array more than 64KB large and see what happens + NetworkWriter writer = new NetworkWriter(); + writer.WriteBytesAndSize(new byte[100000]); + byte[] data = writer.ToArray(); + + NetworkReader reader = new NetworkReader(data); + byte[] deserialized = reader.ReadBytesAndSize(); + Assert.That(deserialized.Length, Is.EqualTo(100000)); + } + + [Test] + public void TestWritingBytesSegment() + { + byte[] data = { 1, 2, 3 }; + NetworkWriter writer = new NetworkWriter(); + writer.WriteBytes(data, 0, data.Length); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + ArraySegment deserialized = reader.ReadBytesSegment(data.Length); + Assert.That(deserialized.Count, Is.EqualTo(data.Length)); + for (int i = 0; i < data.Length; ++i) + Assert.That(deserialized.Array[deserialized.Offset + i], Is.EqualTo(data[i])); + } + + // write byte[], read segment + [Test] + public void TestWritingBytesAndReadingSegment() + { + byte[] data = { 1, 2, 3 }; + NetworkWriter writer = new NetworkWriter(); + writer.WriteBytesAndSize(data); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + ArraySegment deserialized = reader.ReadBytesAndSizeSegment(); + Assert.That(deserialized.Count, Is.EqualTo(data.Length)); + for (int i = 0; i < data.Length; ++i) + Assert.That(deserialized.Array[deserialized.Offset + i], Is.EqualTo(data[i])); + } + + // write segment, read segment + [Test] + public void TestWritingSegmentAndReadingSegment() + { + byte[] data = { 1, 2, 3, 4 }; + // [2, 3] + ArraySegment segment = new ArraySegment(data, 1, 1); + NetworkWriter writer = new NetworkWriter(); + writer.WriteBytesAndSizeSegment(segment); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + ArraySegment deserialized = reader.ReadBytesAndSizeSegment(); + Assert.That(deserialized.Count, Is.EqualTo(segment.Count)); + for (int i = 0; i < segment.Count; ++i) + Assert.That(deserialized.Array[deserialized.Offset + i], Is.EqualTo(segment.Array[segment.Offset + i])); + } + + [Test] + public void TestResetSetsPotionAndLength() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteString("I saw"); + writer.WriteLong(0xA_FADED_DEAD_EEL); + writer.WriteString("and ate it"); + writer.Reset(); + + Assert.That(writer.Position, Is.EqualTo(0)); + + byte[] data = writer.ToArray(); + Assert.That(data, Is.Empty); + } + + [Test] + public void TestReading0LengthBytesAndSize() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteBytesAndSize(new byte[] {}); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Assert.That(reader.ReadBytesAndSize().Length, Is.EqualTo(0)); + } + + [Test] + public void TestReading0LengthBytes() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteBytes(new byte[] {}, 0, 0); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Assert.That(reader.ReadBytes(0).Length, Is.EqualTo(0)); + } + + [Test] + public void TestWritingNegativeBytesAndSizeFailure() + { + NetworkWriter writer = new NetworkWriter(); + Assert.Throws(() => writer.WriteBytesAndSize(new byte[0], 0, -1)); + Assert.That(writer.Position, Is.EqualTo(0)); + } + + [Test] + public void TestReadingTooMuch() + { + void EnsureThrows(Action read, byte[] data = null) + { + Assert.Throws(() => read(new NetworkReader(data ?? new byte[] {}))); + } + // Try reading more than there is data to be read from + // This should throw EndOfStreamException always + EnsureThrows(r => r.ReadByte()); + EnsureThrows(r => r.ReadSByte()); + EnsureThrows(r => r.ReadChar()); + EnsureThrows(r => r.ReadBool()); + EnsureThrows(r => r.ReadShort()); + EnsureThrows(r => r.ReadUShort()); + EnsureThrows(r => r.ReadInt()); + EnsureThrows(r => r.ReadUInt()); + EnsureThrows(r => r.ReadLong()); + EnsureThrows(r => r.ReadULong()); + EnsureThrows(r => r.ReadDecimal()); + EnsureThrows(r => r.ReadFloat()); + EnsureThrows(r => r.ReadDouble()); + EnsureThrows(r => r.ReadString()); + EnsureThrows(r => r.ReadBytes(1)); + EnsureThrows(r => r.ReadBytes(2)); + EnsureThrows(r => r.ReadBytes(3)); + EnsureThrows(r => r.ReadBytes(4)); + EnsureThrows(r => r.ReadBytes(8)); + EnsureThrows(r => r.ReadBytes(16)); + EnsureThrows(r => r.ReadBytes(32)); + EnsureThrows(r => r.ReadBytes(100)); + EnsureThrows(r => r.ReadBytes(1000)); + EnsureThrows(r => r.ReadBytes(10000)); + EnsureThrows(r => r.ReadBytes(1000000)); + EnsureThrows(r => r.ReadBytes(10000000)); + EnsureThrows(r => r.ReadBytesAndSize()); + EnsureThrows(r => r.ReadVector2()); + EnsureThrows(r => r.ReadVector3()); + EnsureThrows(r => r.ReadVector4()); + EnsureThrows(r => r.ReadVector2Int()); + EnsureThrows(r => r.ReadVector3Int()); + EnsureThrows(r => r.ReadColor()); + EnsureThrows(r => r.ReadColor32()); + EnsureThrows(r => r.ReadQuaternion()); + EnsureThrows(r => r.ReadRect()); + EnsureThrows(r => r.ReadPlane()); + EnsureThrows(r => r.ReadRay()); + EnsureThrows(r => r.ReadMatrix4x4()); + EnsureThrows(r => r.ReadGuid()); + } + + [Test] + public void TestBool() + { + bool[] inputs = { true, false }; + foreach (bool input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteBool(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + bool output = reader.ReadBool(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestBoolNullable() + { + bool? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteBoolNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + bool? output = reader.ReadBoolNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestByte() + { + byte[] inputs = { 1, 2, 3, 4 }; + foreach (byte input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteByte(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + byte output = reader.ReadByte(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestByteNullable() + { + byte? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteByteNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + byte? output = reader.ReadByteNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestSByte() + { + sbyte[] inputs = { 1, 2, 3, 4 }; + foreach (sbyte input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteSByte(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + sbyte output = reader.ReadSByte(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestSByteNullable() + { + sbyte? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteSByteNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + sbyte? output = reader.ReadSByteNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestVector2() + { + Vector2[] inputs = { + Vector2.right, + Vector2.up, + Vector2.zero, + Vector2.one, + Vector2.positiveInfinity, + new Vector2(0.1f,3.1f) + }; + foreach (Vector2 input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector2(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector2 output = reader.ReadVector2(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestVector2Nullable() + { + Vector2? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector2Nullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector2? output = reader.ReadVector2Nullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestVector3() + { + Vector3[] inputs = { + Vector3.right, + Vector3.up, + Vector3.zero, + Vector3.one, + Vector3.positiveInfinity, + Vector3.forward, + new Vector3(0.1f,3.1f,1.4f) + }; + foreach (Vector3 input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector3(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector3 output = reader.ReadVector3(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestVector3Nullable() + { + Vector3? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector3Nullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector3? output = reader.ReadVector3Nullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestVector4() + { + Vector4[] inputs = { + Vector3.right, + Vector3.up, + Vector4.zero, + Vector4.one, + Vector4.positiveInfinity, + new Vector4(0.1f,3.1f,1.4f,4.9f) + }; + foreach (Vector4 input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector4(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector4 output = reader.ReadVector4(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestVector4Nullable() + { + Vector4? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector4Nullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector4? output = reader.ReadVector4Nullable(); + Assert.That(output, Is.EqualTo(output)); + } + + [Test] + public void TestVector2Int() + { + Vector2Int[] inputs = { + Vector2Int.down, + Vector2Int.up, + Vector2Int.left, + Vector2Int.zero, + new Vector2Int(-1023,-999999), + new Vector2Int(257,12345), + new Vector2Int(0x7fffffff,-12345), + }; + foreach (Vector2Int input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector2Int(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector2Int output = reader.ReadVector2Int(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestVector2IntNullable() + { + Vector2Int? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector2IntNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector2Int? output = reader.ReadVector2IntNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestVector3Int() + { + Vector3Int[] inputs = { + Vector3Int.down, + Vector3Int.up, + Vector3Int.left, + Vector3Int.one, + Vector3Int.zero, + new Vector3Int(-1023,-999999,1392), + new Vector3Int(257,12345,-6132), + new Vector3Int(0x7fffffff,-12345,-1), + }; + foreach (Vector3Int input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector3Int(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector3Int output = reader.ReadVector3Int(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestVector3IntNullable() + { + Vector3Int? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteVector3IntNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Vector3Int? output = reader.ReadVector3IntNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestColor() + { + Color[] inputs = { + Color.black, + Color.blue, + Color.cyan, + Color.yellow, + Color.magenta, + Color.white, + new Color(0.401f,0.2f,1.0f,0.123f) + }; + foreach (Color input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteColor(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Color output = reader.ReadColor(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestColorNullable() + { + Color? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteColorNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Color? output = reader.ReadColorNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestColor32() + { + Color32[] inputs = { + Color.black, + Color.blue, + Color.cyan, + Color.yellow, + Color.magenta, + Color.white, + new Color32(0xab,0xcd,0xef,0x12), + new Color32(125,126,0,255) + }; + foreach (Color32 input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteColor32(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Color32 output = reader.ReadColor32(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestColor32Nullable() + { + Color32? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteColor32Nullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Color32? output = reader.ReadColor32Nullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestQuaternion() + { + Quaternion[] inputs = { + Quaternion.identity, + default, + Quaternion.LookRotation(new Vector3(0.3f,0.4f,0.5f)), + Quaternion.Euler(45f,56f,Mathf.PI) + }; + foreach (Quaternion input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteQuaternion(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Quaternion output = reader.ReadQuaternion(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestQuaternionNullable() + { + Quaternion? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteQuaternionNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Quaternion? output = reader.ReadQuaternionNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestRect() + { + Rect[] inputs = { + Rect.zero, + new Rect(1004.1f,2.001f,4636,400f), + new Rect(-100.622f,-200f,300f,975.6f), + new Rect(-100f,435,-30.04f,400f), + new Rect(55,-200f,-44,-123), + }; + foreach (Rect input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteRect(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Rect output = reader.ReadRect(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestRectNullable() + { + Rect? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteRectNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Rect? output = reader.ReadRectNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestPlane() + { + Plane[] inputs = { + new Plane(new Vector3(-0.24f,0.34f,0.2f), 120.2f), + new Plane(new Vector3(0.133f,0.34f,0.122f), -10.135f), + new Plane(new Vector3(0.133f,-0.0f,float.MaxValue), -13.3f), + new Plane(new Vector3(0.1f,-0.2f,0.3f), 14.5f), + }; + foreach (Plane input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WritePlane(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Plane output = reader.ReadPlane(); + // note: Plane constructor does math internally, resulting in + // floating point precision loss that causes exact comparison + // to fail the test. So we test that the difference is small. + Assert.That((output.normal - input.normal).magnitude, Is.LessThan(1e-6f)); + Assert.That(output.distance, Is.EqualTo(input.distance)); + } + } + + [Test] + public void TestPlaneNullable() + { + Plane? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WritePlaneNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Plane? output = reader.ReadPlaneNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestRay() + { + Ray[] inputs = { + new Ray(Vector3.up,Vector3.down), + new Ray(new Vector3(0.1f,0.2f,0.3f), new Vector3(0.4f,0.5f,0.6f)), + new Ray(new Vector3(-0.3f,0.5f,0.999f), new Vector3(1f,100.1f,20f)), + }; + foreach (Ray input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteRay(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Ray output = reader.ReadRay(); + Assert.That((output.direction - input.direction).magnitude, Is.LessThan(1e-6f)); + Assert.That(output.origin, Is.EqualTo(input.origin)); + } + } + + [Test] + public void TestRayNullable() + { + Ray? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteRayNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Ray? output = reader.ReadRayNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestMatrix4x4() + { + Matrix4x4[] inputs = { + Matrix4x4.identity, + Matrix4x4.zero, + Matrix4x4.Scale(Vector3.one * 0.12345f), + Matrix4x4.LookAt(Vector2.up,Vector3.right,Vector3.forward), + Matrix4x4.Rotate(Quaternion.LookRotation(Vector3.one)), + }; + foreach (Matrix4x4 input in inputs) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteMatrix4x4(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Matrix4x4 output = reader.ReadMatrix4x4(); + Assert.That(output, Is.EqualTo(input)); + } + } + + [Test] + public void TestMatrix4x4Nullable() + { + Matrix4x4? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteMatrix4x4Nullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Matrix4x4? output = reader.ReadMatrix4x4Nullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestReadingInvalidString() + { + // These are all bytes which never show up in valid UTF8 encodings. + // NetworkReader should gracefully handle maliciously crafted input. + byte[] invalidUTF8bytes = { + 0xC0, 0xC1, 0xF5, 0xF6, + 0xF7, 0xF8, 0xF9, 0xFA, + 0xFB, 0xFC, 0xFD, 0xFE, + 0xFF, + }; + foreach (byte invalid in invalidUTF8bytes) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteString("an uncorrupted string"); + byte[] data = writer.ToArray(); + data[10] = invalid; + NetworkReader reader = new NetworkReader(data); + Assert.Throws(() => reader.ReadString()); + } + } + + [Test] + public void TestReadingTruncatedString() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteString("a string longer than 10 bytes"); + writer.Reset(); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Assert.Throws(() => reader.ReadString()); + } + + [Test] + public void TestToArray() + { + // write 2 bytes + NetworkWriter writer = new NetworkWriter(); + writer.WriteByte(1); + writer.WriteByte(2); + + // .ToArray() length is 2? + Assert.That(writer.ToArray().Length, Is.EqualTo(2)); + + // set position back by one + writer.Position = 1; + + // Changing the position alter the size of the data + Assert.That(writer.ToArray().Length, Is.EqualTo(1)); + } + + [Test] + public void TestToArraySegment() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteString("hello"); + writer.WriteString("world"); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadString(), Is.EqualTo("hello")); + Assert.That(reader.ReadString(), Is.EqualTo("world")); + } + + // sometimes we may serialize nothing, then call ToArraySegment. + // make sure this works even if empty. + [Test] + public void TestToArraySegment_EmptyContent() + { + NetworkWriter writer = new NetworkWriter(); + ArraySegment segment = writer.ToArraySegment(); + Assert.That(segment.Count, Is.EqualTo(0)); + } + + [Test] + public void TestChar() + { + char a = 'a'; + char u = 'ⓤ'; + + NetworkWriter writer = new NetworkWriter(); + writer.WriteChar(a); + writer.WriteChar(u); + NetworkReader reader = new NetworkReader(writer.ToArray()); + char a2 = reader.ReadChar(); + Assert.That(a2, Is.EqualTo(a)); + char u2 = reader.ReadChar(); + Assert.That(u2, Is.EqualTo(u)); + } + + [Test] + public void TestCharNullable() + { + char? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteCharNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + char? output = reader.ReadCharNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestUnicodeString() + { + string[] weirdUnicode = { + "𝔲𝔫𝔦𝔠𝔬𝔡𝔢 𝔱𝔢𝔰𝔱", + "𝖚𝖓𝖎𝖈𝖔𝖉𝖊 𝖙𝖊𝖘𝖙", + "𝐮𝐧𝐢𝐜𝐨𝐝𝐞 𝐭𝐞𝐬𝐭", + "𝘶𝘯𝘪𝘤𝘰𝘥𝘦 𝘵𝘦𝘴𝘵", + "𝙪𝙣𝙞𝙘𝙤𝙙𝙚 𝙩𝙚𝙨𝙩", + "𝚞𝚗𝚒𝚌𝚘𝚍𝚎 𝚝𝚎𝚜𝚝", + "𝓊𝓃𝒾𝒸𝑜𝒹𝑒 𝓉𝑒𝓈𝓉", + "𝓾𝓷𝓲𝓬𝓸𝓭𝓮 𝓽𝓮𝓼𝓽", + "𝕦𝕟𝕚𝕔𝕠𝕕𝕖 𝕥𝕖𝕤𝕥", + "ЦПIᄃӨDΣ ƬΣƧƬ", + "ㄩ几丨匚ㄖᗪ乇 ㄒ乇丂ㄒ", + "ひ刀ノᄃのり乇 イ乇丂イ", + "Ʉ₦ł₵ØĐɆ ₮Ɇ₴₮", + "unicode test", + "ᴜɴɪᴄᴏᴅᴇ ᴛᴇꜱᴛ", + "ʇsǝʇ ǝpoɔıun", + "ยภเς๏๔є ՇєรՇ", + "ᑘᘉᓰᑢᓍᕲᘿ ᖶᘿSᖶ", + "υɳιƈσԃҽ ƚҽʂƚ", + "ʊռɨƈօɖɛ ȶɛֆȶ", + "🆄🅽🅸🅲🅾🅳🅴 🆃🅴🆂🆃", + "ⓤⓝⓘⓒⓞⓓⓔ ⓣⓔⓢⓣ", + "̶̝̳̥͈͖̝͌̈͛̽͊̏̚͠", + // test control codes + "\r\n", "\n", "\r", "\t", + "\\", "\"", "\'", + "\u0000\u0001\u0002\u0003", + "\u0004\u0005\u0006\u0007", + "\u0008\u0009\u000A\u000B", + "\u000C\u000D\u000E\u000F", + // test invalid bytes as characters + "\u00C0\u00C1\u00F5\u00F6", + "\u00F7\u00F8\u00F9\u00FA", + "\u00FB\u00FC\u00FD\u00FE", + "\u00FF", + }; + foreach (string weird in weirdUnicode) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteString(weird); + byte[] data = writer.ToArray(); + NetworkReader reader = new NetworkReader(data); + string str = reader.ReadString(); + Assert.That(str, Is.EqualTo(weird)); + } + } + + [Test] + public void TestGuid() + { + Guid originalGuid = new Guid("0123456789abcdef9876543210fedcba"); + NetworkWriter writer = new NetworkWriter(); + writer.WriteGuid(originalGuid); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + Guid readGuid = reader.ReadGuid(); + Assert.That(readGuid, Is.EqualTo(originalGuid)); + } + + [Test] + public void TestGuidNullable() + { + Guid? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteGuidNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + Guid? output = reader.ReadGuidNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestFloats() + { + float[] weirdFloats = { + 0f, + -0f, + float.Epsilon, + -float.Epsilon, + float.MaxValue, + float.MinValue, + float.NaN, + -float.NaN, + float.PositiveInfinity, + float.NegativeInfinity, + (float) double.MaxValue, + (float) double.MinValue, + (float) decimal.MaxValue, + (float) decimal.MinValue, + (float) Math.PI, + (float) Math.E + }; + foreach (float weird in weirdFloats) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteFloat(weird); + NetworkReader reader = new NetworkReader(writer.ToArray()); + float readFloat = reader.ReadFloat(); + Assert.That(readFloat, Is.EqualTo(weird)); + } + } + + [Test] + public void TestFloatNullable() + { + float? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteFloatNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + float? output = reader.ReadFloatNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestDoubles() + { + double[] weirdDoubles = { + 0d, + -0d, + double.Epsilon, + -double.Epsilon, + double.MaxValue, + double.MinValue, + double.NaN, + -double.NaN, + double.PositiveInfinity, + double.NegativeInfinity, + float.MaxValue, + float.MinValue, + (double) decimal.MaxValue, + (double) decimal.MinValue, + Math.PI, + Math.E + }; + foreach (double weird in weirdDoubles) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteDouble(weird); + NetworkReader reader = new NetworkReader(writer.ToArray()); + double readDouble = reader.ReadDouble(); + Assert.That(readDouble, Is.EqualTo(weird)); + } + } + + [Test] + public void TestDoubleNullable() + { + double? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteDoubleNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + double? output = reader.ReadDoubleNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestDecimals() + { + decimal[] weirdDecimals = { + decimal.Zero, + -decimal.Zero, + decimal.MaxValue, + decimal.MinValue, + (decimal) Math.PI, + (decimal) Math.E + }; + foreach (decimal weird in weirdDecimals) + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteDecimal(weird); + NetworkReader reader = new NetworkReader(writer.ToArray()); + decimal readDecimal = reader.ReadDecimal(); + Assert.That(readDecimal, Is.EqualTo(weird)); + } + } + + [Test] + public void TestDecimalNullable() + { + decimal? input = null; + NetworkWriter writer = new NetworkWriter(); + writer.WriteDecimalNullable(input); + NetworkReader reader = new NetworkReader(writer.ToArray()); + decimal? output = reader.ReadDecimalNullable(); + Assert.That(output, Is.EqualTo(input)); + } + + [Test] + public void TestFloatBinaryCompatibility() + { + float[] weirdFloats = { + ((float) Math.PI) / 3.0f, + ((float) Math.E) / 3.0f + }; + byte[] expected = { + 146, 10,134, 63, + 197,245,103, 63, + }; + NetworkWriter writer = new NetworkWriter(); + foreach (float weird in weirdFloats) + { + writer.WriteFloat(weird); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestDoubleBinaryCompatibility() + { + double[] weirdDoubles = { + Math.PI / 3.0d, + Math.E / 3.0d + }; + byte[] expected = { + 101,115, 45, 56, 82,193,240, 63, + 140,116,112,185,184,254,236, 63, + }; + NetworkWriter writer = new NetworkWriter(); + foreach (double weird in weirdDoubles) + { + writer.WriteDouble(weird); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestDecimalBinaryCompatibility() + { + decimal[] weirdDecimals = { + ((decimal) Math.PI) / 3.0m, + ((decimal) Math.E) / 3.0m + }; + byte[] expected = { + 0x00, 0x00, 0x1C, 0x00, 0x12, 0x37, 0xD6, 0x21, 0xAB, 0xEA, + 0x84, 0x0A, 0x5B, 0x5E, 0xB1, 0x03, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x6D, 0xC2, 0xA4, 0x68, 0x52, + 0x00, 0x00 + }; + NetworkWriter writer = new NetworkWriter(); + foreach (decimal weird in weirdDecimals) + { + writer.WriteDecimal(weird); + } + //Debug.Log(BitConverter.ToString(writer.ToArray())); + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestByteEndianness() + { + byte[] values = { 0x12, 0x43, 0x00, 0xff, 0xab, 0x02, 0x20 }; + byte[] expected = { 0x12, 0x43, 0x00, 0xff, 0xab, 0x02, 0x20 }; + NetworkWriter writer = new NetworkWriter(); + foreach (byte value in values) + { + writer.WriteByte(value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestUShortEndianness() + { + ushort[] values = { 0x0000, 0x1234, 0xabcd, 0xF00F, 0x0FF0, 0xbeef }; + byte[] expected = { 0x00, 0x00, 0x34, 0x12, 0xcd, 0xab, 0x0F, 0xF0, 0xF0, 0x0F, 0xef, 0xbe }; + NetworkWriter writer = new NetworkWriter(); + foreach (ushort value in values) + { + writer.WriteUShort(value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestUIntEndianness() + { + uint[] values = { 0x12345678, 0xabcdef09, 0xdeadbeef }; + byte[] expected = { 0x78, 0x56, 0x34, 0x12, 0x09, 0xef, 0xcd, 0xab, 0xef, 0xbe, 0xad, 0xde }; + NetworkWriter writer = new NetworkWriter(); + foreach (uint value in values) + { + writer.WriteUInt(value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestULongEndianness() + { + ulong[] values = { 0x0123456789abcdef, 0xdeaded_beef_c0ffee }; + byte[] expected = { 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0xee, 0xff, 0xc0, 0xef, 0xbe, 0xed, 0xad, 0xde }; + NetworkWriter writer = new NetworkWriter(); + foreach (ulong value in values) + { + writer.WriteULong(value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestSbyteEndianness() + { + byte[] values = { 0x12, 0x43, 0x00, 0xff, 0xab, 0x02, 0x20 }; + byte[] expected = { 0x12, 0x43, 0x00, 0xff, 0xab, 0x02, 0x20 }; + NetworkWriter writer = new NetworkWriter(); + foreach (byte value in values) + { + writer.WriteSByte((sbyte)value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestShortEndianness() + { + ushort[] values = { 0x0000, 0x1234, 0xabcd, 0xF00F, 0x0FF0, 0xbeef }; + byte[] expected = { 0x00, 0x00, 0x34, 0x12, 0xcd, 0xab, 0x0F, 0xF0, 0xF0, 0x0F, 0xef, 0xbe }; + NetworkWriter writer = new NetworkWriter(); + foreach (ushort value in values) + { + writer.WriteShort((short)value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestIntEndianness() + { + uint[] values = { 0x12345678, 0xabcdef09, 0xdeadbeef }; + byte[] expected = { 0x78, 0x56, 0x34, 0x12, 0x09, 0xef, 0xcd, 0xab, 0xef, 0xbe, 0xad, 0xde }; + NetworkWriter writer = new NetworkWriter(); + foreach (uint value in values) + { + writer.WriteInt((int)value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestLongEndianness() + { + ulong[] values = { 0x0123456789abcdef, 0xdeaded_beef_c0ffee }; + byte[] expected = { 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0xee, 0xff, 0xc0, 0xef, 0xbe, 0xed, 0xad, 0xde }; + NetworkWriter writer = new NetworkWriter(); + foreach (ulong value in values) + { + writer.WriteLong((long)value); + } + Assert.That(writer.ToArray(), Is.EqualTo(expected)); + } + + [Test] + public void TestWritingAndReading() + { + // write all simple types once + NetworkWriter writer = new NetworkWriter(); + writer.WriteChar((char)1); + writer.WriteByte(2); + writer.WriteSByte(3); + writer.WriteBool(true); + writer.WriteShort(4); + writer.WriteUShort(5); + writer.WriteInt(6); + writer.WriteUInt(7U); + writer.WriteLong(8L); + writer.WriteULong(9UL); + writer.WriteFloat(10.0F); + writer.WriteDouble(11.0D); + writer.WriteDecimal(12); + writer.WriteString(null); + writer.WriteString(""); + writer.WriteString("13"); + // just the byte array, no size info etc. + writer.WriteBytes(new byte[] { 14, 15 }, 0, 2); + // [SyncVar] struct values can have uninitialized byte arrays, null needs to be supported + writer.WriteBytesAndSize(null); + // buffer, no-offset, count + writer.WriteBytesAndSize(new byte[] { 17, 18 }, 0, 2); + // buffer, offset, count + writer.WriteBytesAndSize(new byte[] { 19, 20, 21 }, 1, 2); + // size, buffer + writer.WriteBytesAndSize(new byte[] { 22, 23 }, 0, 2); + + // read them + NetworkReader reader = new NetworkReader(writer.ToArray()); + + Assert.That(reader.ReadChar(), Is.EqualTo(1)); + Assert.That(reader.ReadByte(), Is.EqualTo(2)); + Assert.That(reader.ReadSByte(), Is.EqualTo(3)); + Assert.That(reader.ReadBool(), Is.True); + Assert.That(reader.ReadShort(), Is.EqualTo(4)); + Assert.That(reader.ReadUShort(), Is.EqualTo(5)); + Assert.That(reader.ReadInt(), Is.EqualTo(6)); + Assert.That(reader.ReadUInt(), Is.EqualTo(7)); + Assert.That(reader.ReadLong(), Is.EqualTo(8)); + Assert.That(reader.ReadULong(), Is.EqualTo(9)); + Assert.That(reader.ReadFloat(), Is.EqualTo(10)); + Assert.That(reader.ReadDouble(), Is.EqualTo(11)); + Assert.That(reader.ReadDecimal(), Is.EqualTo(12)); + // writing null string should write null in Mirror ("" in original HLAPI) + Assert.That(reader.ReadString(), Is.Null); + Assert.That(reader.ReadString(), Is.EqualTo("")); + Assert.That(reader.ReadString(), Is.EqualTo("13")); + + Assert.That(reader.ReadBytes(2), Is.EqualTo(new byte[] { 14, 15 })); + + Assert.That(reader.ReadBytesAndSize(), Is.Null); + + Assert.That(reader.ReadBytesAndSize(), Is.EqualTo(new byte[] { 17, 18 })); + + Assert.That(reader.ReadBytesAndSize(), Is.EqualTo(new byte[] { 20, 21 })); + + Assert.That(reader.ReadBytesAndSize(), Is.EqualTo(new byte[] { 22, 23 })); + } + + [Test] + public void TestWritingUri() + { + + Uri testUri = new Uri("https://www.mirror-networking.com?somthing=other"); + + NetworkWriter writer = new NetworkWriter(); + writer.WriteUri(testUri); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + Assert.That(reader.ReadUri(), Is.EqualTo(testUri)); + } + + // URI null support test for https://github.com/vis2k/Mirror/pull/2796/ + [Test] + public void TestWritingNullUri() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUri(null); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + Assert.That(reader.ReadUri(), Is.EqualTo(null)); + } + + [Test] + public void TestList() + { + List original = new List() { 1, 2, 3, 4, 5 }; + NetworkWriter writer = new NetworkWriter(); + writer.Write(original); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + List readList = reader.Read>(); + Assert.That(readList, Is.EqualTo(original)); + } + + [Test] + public void TestNullList() + { + NetworkWriter writer = new NetworkWriter(); + writer.Write>(null); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + List readList = reader.Read>(); + Assert.That(readList, Is.Null); + } + + + const int testArraySize = 4; + [Test] + [Description("ReadArray should throw if it is trying to read more than length of segment, this is to stop allocation attacks")] + public void TestArrayDoesNotThrowWithCorrectLength() + { + NetworkWriter writer = new NetworkWriter(); + WriteGoodArray(); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + Assert.DoesNotThrow(() => + { + _ = reader.ReadArray(); + }); + + void WriteGoodArray() + { + writer.WriteInt(testArraySize); + int[] array = new int[testArraySize] { 1, 2, 3, 4 }; + for (int i = 0; i < array.Length; i++) + writer.Write(array[i]); + } + } + [Test] + [Description("ReadArray should throw if it is trying to read more than length of segment, this is to stop allocation attacks")] + [TestCase(testArraySize * sizeof(int), Description = "max allowed value to allocate array")] + [TestCase(testArraySize * 2)] + [TestCase(testArraySize + 1, Description = "min allowed to allocate")] + public void TestArrayThrowsIfLengthIsWrong(int badLength) + { + NetworkWriter writer = new NetworkWriter(); + WriteBadArray(); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + Assert.Throws(() => { + _ = reader.ReadArray(); + }); + + void WriteBadArray() + { + writer.WriteInt(badLength); + int[] array = new int[testArraySize] { 1, 2, 3, 4 }; + for (int i = 0; i < array.Length; i++) + writer.Write(array[i]); + } + } + + [Test] + [Description("ReadArray should throw if it is trying to read more than length of segment, this is to stop allocation attacks")] + [TestCase(testArraySize * sizeof(int) + 1, Description = "min read count is 1 byte, 16 array bytes are writen so 17 should throw error")] + [TestCase(20_000)] + [TestCase(int.MaxValue)] + [TestCase(int.MaxValue - 1)] + // todo add fuzzy testing to check more values + public void TestArrayThrowsIfLengthIsTooBig(int badLength) + { + NetworkWriter writer = new NetworkWriter(); + WriteBadArray(); + + NetworkReader reader = new NetworkReader(writer.ToArray()); + EndOfStreamException exception = Assert.Throws(() => + { + _ = reader.ReadArray(); + }); + Assert.That(exception, Has.Message.EqualTo($"Received array that is too large: {badLength}")); + + void WriteBadArray() + { + writer.WriteInt(badLength); + int[] array = new int[testArraySize] { 1, 2, 3, 4 }; + for (int i = 0; i < array.Length; i++) + writer.Write(array[i]); + } + } + + [Test] + public void TestNetworkBehaviour() + { + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn(out _, out _, out RpcNetworkIdentityBehaviour behaviour); + + NetworkWriter writer = new NetworkWriter(); + writer.WriteNetworkBehaviour(behaviour); + + byte[] bytes = writer.ToArray(); + + Assert.That(bytes.Length, Is.EqualTo(5), "Networkbehaviour should be 5 bytes long."); + + NetworkReader reader = new NetworkReader(bytes); + RpcNetworkIdentityBehaviour actual = reader.ReadNetworkBehaviour(); + Assert.That(actual, Is.EqualTo(behaviour), "Read should find the same behaviour as written"); + } + + [Test] + public void TestNetworkBehaviourNull() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteNetworkBehaviour(null); + + byte[] bytes = writer.ToArray(); + + Assert.That(bytes.Length, Is.EqualTo(4), "null Networkbehaviour should be 4 bytes long."); + + NetworkReader reader = new NetworkReader(bytes); + RpcNetworkIdentityBehaviour actual = reader.ReadNetworkBehaviour(); + Assert.That(actual, Is.Null, "should read null"); + + Assert.That(reader.Position, Is.EqualTo(4), "should read 4 bytes when netid is 0"); + } + + [Test] + [Description("Uses Generic read function to check weaver correctly creates it")] + public void TestNetworkBehaviourWeaverGenerated() + { + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn(out _, out _, out RpcNetworkIdentityBehaviour behaviour); + + NetworkWriter writer = new NetworkWriter(); + writer.Write(behaviour); + + byte[] bytes = writer.ToArray(); + + Assert.That(bytes.Length, Is.EqualTo(5), "Networkbehaviour should be 5 bytes long."); + + NetworkReader reader = new NetworkReader(bytes); + RpcNetworkIdentityBehaviour actual = reader.Read(); + Assert.That(actual, Is.EqualTo(behaviour), "Read should find the same behaviour as written"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs.meta b/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs.meta new file mode 100644 index 000000000..394706e04 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f7c59e9071cf4a64a9bd207465e3f1b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/OverloadMethodTest.cs b/Assets/Mirror/Tests/Editor/OverloadMethodTest.cs new file mode 100644 index 000000000..56313714d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/OverloadMethodTest.cs @@ -0,0 +1,53 @@ +using NUnit.Framework; + +namespace Mirror.Tests.MessageTests +{ + struct NoArgMethodMessage : NetworkMessage + { + public int someValue; + + // Weaver should ignore these methods because they have no args + public void Serialize() { /* method with no arg */ } + public void Deserialize() { /* method with no arg */ } + } + + struct TwoArgMethodMessage : NetworkMessage + { + public int someValue; + + // Weaver should ignore these methods because they have two args + public void Serialize(NetworkWriter writer, int AnotherValue) {} + public void Deserialize(NetworkReader reader, int AnotherValue) {} + } + + public class OverloadMethodTest + { + [Test] + public void MethodsWithNoArgs() + { + const int value = 10; + NoArgMethodMessage intMessage = new NoArgMethodMessage + { + someValue = value + }; + + byte[] data = MessagePackingTest.PackToByteArray(intMessage); + NoArgMethodMessage unpacked = MessagePackingTest.UnpackFromByteArray(data); + Assert.That(unpacked.someValue, Is.EqualTo(value)); + } + + [Test] + public void MethodsWithTwoArgs() + { + const int value = 10; + TwoArgMethodMessage intMessage = new TwoArgMethodMessage + { + someValue = value + }; + + byte[] data = MessagePackingTest.PackToByteArray(intMessage); + TwoArgMethodMessage unpacked = MessagePackingTest.UnpackFromByteArray(data); + Assert.That(unpacked.someValue, Is.EqualTo(value)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/OverloadMethodTest.cs.meta b/Assets/Mirror/Tests/Editor/OverloadMethodTest.cs.meta new file mode 100644 index 000000000..227a26a8b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/OverloadMethodTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bbe7affc888ce1041a8d6752b0f3f94b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/PoolTests.cs b/Assets/Mirror/Tests/Editor/PoolTests.cs new file mode 100644 index 000000000..2002c8de5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/PoolTests.cs @@ -0,0 +1,45 @@ +using NUnit.Framework; + +namespace Mirror.Tests +{ + public class PoolTests + { + Pool pool; + + [SetUp] + public void SetUp() + { + pool = new Pool(() => "new string", 0); + } + + [TearDown] + public void TearDown() + { + pool = null; + } + + [Test] + public void TakeFromEmpty() + { + // taking from an empty pool should give us a completely new string + Assert.That(pool.Take(), Is.EqualTo("new string")); + } + + [Test] + public void ReturnAndTake() + { + // returning and then taking should get the returned one, not a + // newly generated one. + pool.Return("returned"); + Assert.That(pool.Take(), Is.EqualTo("returned")); + } + + [Test] + public void Count() + { + Assert.That(pool.Count, Is.EqualTo(0)); + pool.Return("returned"); + Assert.That(pool.Count, Is.EqualTo(1)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/PoolTests.cs.meta b/Assets/Mirror/Tests/Editor/PoolTests.cs.meta new file mode 100644 index 000000000..73ac928ce --- /dev/null +++ b/Assets/Mirror/Tests/Editor/PoolTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 566efeb4786e4449bb70c041baf39b42 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/RemoteTestBase.cs b/Assets/Mirror/Tests/Editor/RemoteTestBase.cs new file mode 100644 index 000000000..05cb4fc36 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/RemoteTestBase.cs @@ -0,0 +1,15 @@ +using NUnit.Framework; + +namespace Mirror.Tests.RemoteAttrributeTest +{ + public class RemoteTestBase : MirrorEditModeTest + { + [SetUp] + public void Setup() + { + // start server/client + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/RemoteTestBase.cs.meta b/Assets/Mirror/Tests/Editor/RemoteTestBase.cs.meta new file mode 100644 index 000000000..45e4de8ea --- /dev/null +++ b/Assets/Mirror/Tests/Editor/RemoteTestBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45bb26f0ce04fb749ac83a28d7590ebc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/RpcNetworkIdentityTest.cs b/Assets/Mirror/Tests/Editor/RpcNetworkIdentityTest.cs new file mode 100644 index 000000000..4916b8874 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/RpcNetworkIdentityTest.cs @@ -0,0 +1,114 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.RemoteAttrributeTest +{ + class RpcNetworkIdentityBehaviour : NetworkBehaviour + { + public event Action onSendNetworkIdentityCalled; + public event Action onSendGameObjectCalled; + public event Action onSendNetworkBehaviourCalled; + public event Action onSendNetworkBehaviourDerivedCalled; + + [ClientRpc] + public void SendNetworkIdentity(NetworkIdentity value) + { + onSendNetworkIdentityCalled?.Invoke(value); + } + + [ClientRpc] + public void SendGameObject(GameObject value) + { + onSendGameObjectCalled?.Invoke(value); + } + + [ClientRpc] + public void SendNetworkBehaviour(NetworkBehaviour value) + { + onSendNetworkBehaviourCalled?.Invoke(value); + } + + [ClientRpc] + public void SendNetworkBehaviourDerived(RpcNetworkIdentityBehaviour value) + { + onSendNetworkBehaviourDerivedCalled?.Invoke(value); + } + } + + [Description("Test for sending NetworkIdentity fields (NI/GO/NB) in RPC")] + public class RpcNetworkIdentityTest : RemoteTestBase + { + [Test] + public void RpcCanSendNetworkIdentity() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out RpcNetworkIdentityBehaviour hostBehaviour, NetworkServer.localConnection); + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity expected, out RpcNetworkIdentityBehaviour _, NetworkServer.localConnection); + + int callCount = 0; + hostBehaviour.onSendNetworkIdentityCalled += actual => + { + callCount++; + Assert.That(actual, Is.EqualTo(expected)); + }; + hostBehaviour.SendNetworkIdentity(expected); + ProcessMessages(); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + public void RpcCanSendGameObject() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out RpcNetworkIdentityBehaviour hostBehaviour, NetworkServer.localConnection); + CreateNetworkedAndSpawn(out GameObject expected, out NetworkIdentity _, out RpcNetworkIdentityBehaviour _, NetworkServer.localConnection); + + int callCount = 0; + hostBehaviour.onSendGameObjectCalled += actual => + { + callCount++; + Assert.That(actual, Is.EqualTo(expected)); + }; + hostBehaviour.SendGameObject(expected); + ProcessMessages(); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + public void RpcCanSendNetworkBehaviour() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out RpcNetworkIdentityBehaviour hostBehaviour, NetworkServer.localConnection); + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out RpcNetworkIdentityBehaviour expected, NetworkServer.localConnection); + + int callCount = 0; + hostBehaviour.onSendNetworkBehaviourCalled += actual => + { + callCount++; + Assert.That(actual, Is.EqualTo(expected)); + }; + hostBehaviour.SendNetworkBehaviour(expected); + ProcessMessages(); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + public void RpcCanSendNetworkBehaviourDerived() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out RpcNetworkIdentityBehaviour hostBehaviour, NetworkServer.localConnection); + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out RpcNetworkIdentityBehaviour expected, NetworkServer.localConnection); + + int callCount = 0; + hostBehaviour.onSendNetworkBehaviourDerivedCalled += actual => + { + callCount++; + Assert.That(actual, Is.EqualTo(expected)); + }; + hostBehaviour.SendNetworkBehaviourDerived(expected); + ProcessMessages(); + Assert.That(callCount, Is.EqualTo(1)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/RpcNetworkIdentityTest.cs.meta b/Assets/Mirror/Tests/Editor/RpcNetworkIdentityTest.cs.meta new file mode 100644 index 000000000..af93365f5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/RpcNetworkIdentityTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6cd69a1f4a3c74e4fa03b9ab816d392c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs new file mode 100644 index 000000000..b5b83b9e8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs @@ -0,0 +1,43 @@ +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + public class MyScriptableObject : ScriptableObject + { + public int someData; + } + + [TestFixture] + public class ScriptableObjectWriterTest + { + + // ArraySegment is a special case, optimized for no copy and no allocation + // other types are generated by the weaver + + + public struct ScriptableObjectMessage : NetworkMessage + { + public MyScriptableObject scriptableObject; + } + + [Test] + public void TestWriteScriptableObject() + { + ScriptableObjectMessage message = new ScriptableObjectMessage + { + scriptableObject = ScriptableObject.CreateInstance() + }; + + message.scriptableObject.someData = 10; + + byte[] data = MessagePackingTest.PackToByteArray(message); + + ScriptableObjectMessage unpacked = MessagePackingTest.UnpackFromByteArray(data); + + Assert.That(unpacked.scriptableObject, Is.Not.Null); + Assert.That(unpacked.scriptableObject.someData, Is.EqualTo(10)); + } + + } +} diff --git a/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta new file mode 100644 index 000000000..03cd254bd --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6c10e2d494114e9190f56d13a894c82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SnapshotInterpolationTests.cs b/Assets/Mirror/Tests/Editor/SnapshotInterpolationTests.cs new file mode 100644 index 000000000..1ba07b1f9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SnapshotInterpolationTests.cs @@ -0,0 +1,724 @@ + +using System; +using NUnit.Framework; +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror.Tests +{ + // a simple snapshot with timestamp & interpolation + struct SimpleSnapshot : Snapshot + { + public double remoteTimestamp { get; set; } + public double localTimestamp { get; set; } + public double value; + + public SimpleSnapshot(double remoteTimestamp, double localTimestamp, double value) + { + this.remoteTimestamp = remoteTimestamp; + this.localTimestamp = localTimestamp; + this.value = value; + } + + public static SimpleSnapshot Interpolate(SimpleSnapshot from, SimpleSnapshot to, double t) => + new SimpleSnapshot( + // interpolated snapshot is applied directly. don't need timestamps. + 0, 0, + // lerp unclamped in case we ever need to extrapolate. + // atm SnapshotInterpolation never does. + Mathd.LerpUnclamped(from.value, to.value, t)); + } + + public class SnapshotInterpolationTests + { + // buffer for convenience so we don't have to create it manually each time + SortedList buffer; + + [SetUp] + public void SetUp() + { + buffer = new SortedList(); + } + + [Test] + public void InsertIfNewEnough() + { + // inserting a first value should always work + SimpleSnapshot first = new SimpleSnapshot(1, 1, 0); + SnapshotInterpolation.InsertIfNewEnough(first, buffer); + Assert.That(buffer.Count, Is.EqualTo(1)); + + // insert before first should not work + SimpleSnapshot before = new SimpleSnapshot(0.5, 0.5, 0); + SnapshotInterpolation.InsertIfNewEnough(before, buffer); + Assert.That(buffer.Count, Is.EqualTo(1)); + + // insert after first should work + SimpleSnapshot second = new SimpleSnapshot(2, 2, 0); + SnapshotInterpolation.InsertIfNewEnough(second, buffer); + Assert.That(buffer.Count, Is.EqualTo(2)); + Assert.That(buffer.Values[0], Is.EqualTo(first)); + Assert.That(buffer.Values[1], Is.EqualTo(second)); + + // insert after second should work + SimpleSnapshot after = new SimpleSnapshot(2.5, 2.5, 0); + SnapshotInterpolation.InsertIfNewEnough(after, buffer); + Assert.That(buffer.Count, Is.EqualTo(3)); + Assert.That(buffer.Values[0], Is.EqualTo(first)); + Assert.That(buffer.Values[1], Is.EqualTo(second)); + Assert.That(buffer.Values[2], Is.EqualTo(after)); + } + + // the 'ACB' problem: + // if we have a snapshot A at t=0 and C at t=2, + // we start interpolating between them. + // if suddenly B at t=1 comes in unexpectely, + // we should NOT suddenly steer towards B. + // => inserting between the first two snapshot should never be allowed + // in order to avoid all kinds of edge cases. + [Test] + public void InsertIfNewEnough_ACB_Problem() + { + SimpleSnapshot a = new SimpleSnapshot(0, 0, 0); + SimpleSnapshot b = new SimpleSnapshot(1, 1, 0); + SimpleSnapshot c = new SimpleSnapshot(2, 2, 0); + + // insert A and C + SnapshotInterpolation.InsertIfNewEnough(a, buffer); + SnapshotInterpolation.InsertIfNewEnough(c, buffer); + + // trying to insert B between the first two snapshots should fail + SnapshotInterpolation.InsertIfNewEnough(b, buffer); + Assert.That(buffer.Count, Is.EqualTo(2)); + Assert.That(buffer.Values[0], Is.EqualTo(a)); + Assert.That(buffer.Values[1], Is.EqualTo(c)); + } + + // the 'first is lagging' problem: + // server sends A, B. + // A is lagging behind by 2000ms for whatever reason. + // we get B first. + // B should remain the first snapshot, the lagging A should be dropped + [Test] + public void InsertIfNewEnough_FirstIsLagging_Problem() + { + SimpleSnapshot a = new SimpleSnapshot(0, 0, 0); + SimpleSnapshot b = new SimpleSnapshot(1, 1, 0); + + // insert B. A is still delayed. + SnapshotInterpolation.InsertIfNewEnough(b, buffer); + + // now the delayed A comes in. + // timestamp is before B though. + // but it should still be dropped. + SnapshotInterpolation.InsertIfNewEnough(a, buffer); + Assert.That(buffer.Count, Is.EqualTo(1)); + Assert.That(buffer.Values[0], Is.EqualTo(b)); + } + + [Test] + public void HasAmountOlderThan_NotEnough() + { + // only add two + SimpleSnapshot a = new SimpleSnapshot(0, 0, 0); + SimpleSnapshot b = new SimpleSnapshot(1, 1, 0); + buffer.Add(a.remoteTimestamp, a); + buffer.Add(b.remoteTimestamp, b); + + // shouldn't have more old enough than two + // because we don't have more than two + Assert.That(SnapshotInterpolation.HasAmountOlderThan(buffer, 0, 3), Is.False); + } + + [Test] + public void HasAmountOlderThan_EnoughButNotOldEnough() + { + // add three + SimpleSnapshot a = new SimpleSnapshot(0, 0, 0); + SimpleSnapshot b = new SimpleSnapshot(1, 1, 0); + SimpleSnapshot c = new SimpleSnapshot(2, 2, 0); + buffer.Add(a.remoteTimestamp, a); + buffer.Add(b.remoteTimestamp, b); + buffer.Add(c.remoteTimestamp, c); + + // check at time = 1.9, where third one would not be old enough. + Assert.That(SnapshotInterpolation.HasAmountOlderThan(buffer, 1.9, 3), Is.False); + } + + [Test] + public void HasAmountOlderThan_EnoughAndOldEnough() + { + // add three + SimpleSnapshot a = new SimpleSnapshot(0, 0, 0); + SimpleSnapshot b = new SimpleSnapshot(1, 1, 0); + SimpleSnapshot c = new SimpleSnapshot(2, 2, 0); + buffer.Add(a.remoteTimestamp, a); + buffer.Add(b.remoteTimestamp, b); + buffer.Add(c.remoteTimestamp, c); + + // check at time = 2.1, where third one would be old enough. + Assert.That(SnapshotInterpolation.HasAmountOlderThan(buffer, 2.1, 3), Is.True); + } + + // UDP messages might arrive twice sometimes. + // make sure InsertIfNewEnough can handle it. + [Test] + public void InsertIfNewEnough_Duplicate() + { + SimpleSnapshot a = new SimpleSnapshot(0, 0, 0); + SimpleSnapshot b = new SimpleSnapshot(1, 1, 0); + SimpleSnapshot c = new SimpleSnapshot(2, 2, 0); + + // add two valid snapshots first. + // we can't add 'duplicates' before 3rd and 4th anyway. + SnapshotInterpolation.InsertIfNewEnough(a, buffer); + SnapshotInterpolation.InsertIfNewEnough(b, buffer); + + // insert C which is newer than B. + // then insert it again because it arrive twice. + SnapshotInterpolation.InsertIfNewEnough(c, buffer); + SnapshotInterpolation.InsertIfNewEnough(c, buffer); + + // count should still be 3. + Assert.That(buffer.Count, Is.EqualTo(3)); + } + + [Test] + public void CalculateCatchup_Empty() + { + // make sure nothing happens with buffer size = 0 + Assert.That(SnapshotInterpolation.CalculateCatchup(buffer, 0, 10), Is.EqualTo(0)); + } + + [Test] + public void CalculateCatchup_None() + { + // add one + buffer.Add(0, default); + + // catch-up starts at threshold = 1. so nothing. + Assert.That(SnapshotInterpolation.CalculateCatchup(buffer, 1, 10), Is.EqualTo(0)); + } + + [Test] + public void GetFirstSecondAndDelta() + { + // add three + SimpleSnapshot a = new SimpleSnapshot(0, 1, 0); + SimpleSnapshot b = new SimpleSnapshot(2, 3, 0); + SimpleSnapshot c = new SimpleSnapshot(10, 20, 0); + buffer.Add(a.remoteTimestamp, a); + buffer.Add(b.remoteTimestamp, b); + buffer.Add(c.remoteTimestamp, c); + + SnapshotInterpolation.GetFirstSecondAndDelta(buffer, out SimpleSnapshot first, out SimpleSnapshot second, out double delta); + Assert.That(first, Is.EqualTo(a)); + Assert.That(second, Is.EqualTo(b)); + Assert.That(delta, Is.EqualTo(b.remoteTimestamp - a.remoteTimestamp)); + } + + [Test] + public void CalculateCatchup_Multiple() + { + // add three + buffer.Add(0, default); + buffer.Add(1, default); + buffer.Add(2, default); + + // catch-up starts at threshold = 1. so two are multiplied by 10. + Assert.That(SnapshotInterpolation.CalculateCatchup(buffer, 1, 10), Is.EqualTo(20)); + } + + // first step: with empty buffer and defaults, nothing should happen + [Test] + public void Compute_Step1_DefaultDoesNothing() + { + // compute with defaults + double localTime = 0; + double deltaTime = 0; + double interpolationTime = 0; + float bufferTime = 0; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should not spit out any snapshot to apply + Assert.That(result, Is.False); + // no interpolation should have happened yet + Assert.That(interpolationTime, Is.EqualTo(0)); + // buffer should still be untouched + Assert.That(buffer.Count, Is.EqualTo(0)); + } + + // third step: compute should always wait until the first two snapshots + // are older than the time we buffer ('bufferTime') + // => test for both snapshots not old enough + [Test] + public void Compute_Step3_WaitsUntilBufferTime() + { + // add two snapshots that are barely not old enough + // (localTime - bufferTime) + // IMPORTANT: use a 'definitely old enough' remoteTime to make sure + // that compute() actually checks LOCAL, not REMOTE time! + SimpleSnapshot first = new SimpleSnapshot(0.1, 0.1, 0); + SimpleSnapshot second = new SimpleSnapshot(0.9, 1.1, 0); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + double localTime = 3; + double deltaTime = 0.5; + double interpolationTime = 0; + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should not spit out any snapshot to apply + Assert.That(result, Is.False); + // no interpolation should happen yet (not old enough) + Assert.That(interpolationTime, Is.EqualTo(0)); + // buffer should be untouched + Assert.That(buffer.Count, Is.EqualTo(2)); + } + + // third step: compute should always wait until the first two snapshots + // are older than the time we buffer ('bufferTime') + // => test for only one snapshot which is old enough + [Test] + public void Compute_Step3_WaitsForSecondSnapshot() + { + // add a snapshot at t = 0 + SimpleSnapshot first = new SimpleSnapshot(0, 0, 0); + buffer.Add(first.remoteTimestamp, first); + + // compute at localTime = 2 with bufferTime = 1 + // so the threshold is anything < t=1 + double localTime = 2; + double deltaTime = 0; + double interpolationTime = 0; + float bufferTime = 1; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should not spit out any snapshot to apply + Assert.That(result, Is.False); + // no interpolation should happen yet (not enough snapshots) + Assert.That(interpolationTime, Is.EqualTo(0)); + // buffer should be untouched + Assert.That(buffer.Count, Is.EqualTo(1)); + } + + // fourth step: compute should begin if we have two old enough snapshots + [Test] + public void Compute_Step4_InterpolateWithTwoOldEnoughSnapshots() + { + // add two old enough snapshots + // (localTime - bufferTime) + SimpleSnapshot first = new SimpleSnapshot(0, 0, 1); + // IMPORTANT: second snapshot delta is != 1 so we can be sure that + // interpolationTime result is actual time, not 't' ratio. + // for a delta of 1, absolute and relative values would + // return the same results. + SimpleSnapshot second = new SimpleSnapshot(2, 2, 2); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + double localTime = 4; + double deltaTime = 1.5; + double interpolationTime = 0; + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should spit out the interpolated snapshot + Assert.That(result, Is.True); + // interpolation started just now, from 0. + // and deltaTime is 1.5, so we should be at 1.5 now. + Assert.That(interpolationTime, Is.EqualTo(1.5)); + // buffer should be untouched, we are still interpolating between the two + Assert.That(buffer.Count, Is.EqualTo(2)); + // interpolationTime is at 1.5, so 3/4 between first & second. + // computed snapshot should be interpolated at 3/4ths. + Assert.That(computed.value, Is.EqualTo(1.75).Within(Mathf.Epsilon)); + } + + // fourth step: compute should begin if we have two old enough snapshots + // => test with 3 snapshots to make sure the third one + // isn't touched while t between [0,1] + [Test] + public void Compute_Step4_InterpolateWithThreeOldEnoughSnapshots() + { + // add three old enough snapshots. + // (localTime - bufferTime) + SimpleSnapshot first = new SimpleSnapshot(0, 0, 1); + SimpleSnapshot second = new SimpleSnapshot(1, 1, 2); + SimpleSnapshot third = new SimpleSnapshot(2, 2, 2); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + buffer.Add(third.remoteTimestamp, third); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + double localTime = 4; + double deltaTime = 0.5; + double interpolationTime = 0; + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should spit out the interpolated snapshot + Assert.That(result, Is.True); + // interpolation started just now, from 0. + // and deltaTime is 0.5, so we should be at 0.5 now. + Assert.That(interpolationTime, Is.EqualTo(0.5)); + // buffer should be untouched, we are still interpolating between + // the first two. third should still be there. + Assert.That(buffer.Count, Is.EqualTo(3)); + // computed snapshot should be interpolated in the middle + Assert.That(computed.value, Is.EqualTo(1.5).Within(Mathf.Epsilon)); + } + + // fourth step: simulate interpolation after a long time of no updates. + // for example, a mobile user might put the app in the + // background for a minute. + [Test] + public void Compute_Step4_InterpolateAfterLongPause() + { + // add two immediate, and one that arrives 100s later + // (localTime - bufferTime) + SimpleSnapshot first = new SimpleSnapshot(0, 0, 0); + SimpleSnapshot second = new SimpleSnapshot(1, 1, 1); + SimpleSnapshot third = new SimpleSnapshot(101, 2, 101); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + buffer.Add(third.remoteTimestamp, third); + + // compute where we are half way between first and second, + // and now are updated 1 minute later. + double localTime = 103; // 1011+bufferTime so third snapshot is old enough + double deltaTime = 98.5; // 99s - interpolation time + double interpolationTime = 0.5; // half way between first and second + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should spit out the interpolated snapshot + Assert.That(result, Is.True); + // interpolation started at 0.5, right between first & second. + // we received another snapshot at t=101. + // delta = 98.5 seconds + // => interpolationTime = 99 + // => overshoots second goal, so we move to third goal and subtract 1 + // => so we should be at 98 now + Assert.That(interpolationTime, Is.EqualTo(98)); + // we moved to the next snapshot. so only 2 should be in buffer now. + Assert.That(buffer.Count, Is.EqualTo(2)); + // delta between second and third is 100. + // interpolationTime is at 98 + // interpolationTime is relative to second.time + // => InverseLerp(1, 101, 1 + 98) = 0.98 + // which is at 98% of the value + // => Lerp(1, 101, 0.98): 101-1 is 100. 98% are 98. relative to '1' + // makes it 99. + Assert.That(computed.value, Is.EqualTo(99).Within(Mathf.Epsilon)); + } + + // fourth step: catchup should be considered if buffer gets too large + [Test] + public void Compute_Step4_InterpolateWithCatchup() + { + // add two old enough snapshots + // (localTime - bufferTime) + SimpleSnapshot first = new SimpleSnapshot(0, 0, 1); + SimpleSnapshot second = new SimpleSnapshot(1, 1, 2); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + + // start applying 25% catchup per excess when > 2. + int catchupThreshold = 2; + float catchupMultiplier = 0.25f; + + // two excess snapshots to make sure that multiplier is accumulated + SimpleSnapshot excess1 = new SimpleSnapshot(2, 2, 3); + SimpleSnapshot excess2 = new SimpleSnapshot(3, 3, 4); + buffer.Add(excess1.remoteTimestamp, excess1); + buffer.Add(excess2.remoteTimestamp, excess2); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + double localTime = 3; + double deltaTime = 0.5; + double interpolationTime = 0; + float bufferTime = 2; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should spit out the interpolated snapshot + Assert.That(result, Is.True); + // interpolation started just now, from 0. + // and deltaTime is 0.5 + 50% catchup, so we should be at 0.75 now + Assert.That(interpolationTime, Is.EqualTo(0.75)); + // buffer should be untouched, we are still interpolating between + // the first two. + Assert.That(buffer.Count, Is.EqualTo(4)); + // computed snapshot should be interpolated in 3/4 because + // interpolationTime is at 3/4 + Assert.That(computed.value, Is.EqualTo(1.75).Within(Mathf.Epsilon)); + } + + // fifth step: interpolation time overshoots the end while waiting for + // more snapshots. + // + // IMPORTANT: we should NOT extrapolate & predict while waiting for more + // snapshots as this would introduce a whole range of issues: + // * player might be extrapolated WAY out if we wait for long + // * player might be extrapolated behind walls + // * once we receive a new snapshot, we would interpolate + // not from the last valid position, but from the + // extrapolated position. this could be ANYWHERE. the + // player might get stuck in walls, etc. + // => we are NOT doing client side prediction & rollback here + // => we are simply interpolating with known, valid positions + // + // NOTE: to reproduce the issue in a real example: + // * open mirror benchmark example + // * editor=host 1000+ monsters & deep profiling for LOW FPS + // * build=client + // * move around client + // * see it all over the place in editor because it extrapolates, + // ends up at the wrong start positions and gets worse from + // there. + // + // video: https://gyazo.com/8de68f0a821449d7b9a8424e2c9e3ff8 + // (or see Mirror/Docs/Screenshots/NT Snap. Interp./extrapolation issues) + [Test] + public void Compute_Step5_OvershootWithoutEnoughSnapshots() + { + // add two old enough snapshots + // (localTime - bufferTime) + SimpleSnapshot first = new SimpleSnapshot(0, 0, 1); + SimpleSnapshot second = new SimpleSnapshot(1, 1, 2); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + // -> interpolation time is already at '1' at the end. + // -> compute will add 0.5 deltaTime + // -> so we should NOT overshoot aka extrapolate beyond second snap. + double localTime = 3; + double deltaTime = 0.5; + double interpolationTime = 1; + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should spit out the interpolated snapshot + Assert.That(result, Is.True); + // interpolation started at the end = 1 + // and deltaTime is 0.5, so it's at 1.5 internally. + // + // BUT there's NO reason to overshoot interpolationTime if there's + // no other snapshots to move to. + // interpolationTime overshoot is only for smooth transitions WHILE + // moving. + // for example, if we keep overshooting to 100, then we would + // instantly skip the next 20 snapshots. + // => so it should be capped at second.remoteTime + Assert.That(interpolationTime, Is.EqualTo(1)); + // buffer should be untouched, we are still interpolating between the two + Assert.That(buffer.Count, Is.EqualTo(2)); + // computed snapshot should NOT extrapolate beyond second snap. + Assert.That(computed.value, Is.EqualTo(2).Within(Mathf.Epsilon)); + } + + // fifth step: interpolation time overshoots the end while having more + // snapshots available. + // BUT: the next snapshot isn't old enough yet. + // we shouldn't move there until old enough. + // for the same reason we don't move to first, second + // until they are old enough. + // => always need to be 'bufferTime' old. + [Test] + public void Compute_Step5_OvershootWithEnoughSnapshots_NextIsntOldEnough() + { + // add two old enough snapshots + // (localTime - bufferTime) + // + // IMPORTANT: second.time needs to be != second.time-first.time + // to guarantee that we cap interpolationTime (which is + // RELATIVE from 0..delta) at delta, not at second.time. + // this was a bug before. + SimpleSnapshot first = new SimpleSnapshot(1, 1, 1); + SimpleSnapshot second = new SimpleSnapshot(2, 2, 2); + // IMPORTANT: third snapshot needs to be: + // - a different time delta + // to test if overflow is correct if deltas are different. + // it's not obvious if we ever use t ratio between [0,1] where an + // overflow of 0.1 between A,B could speed up B,C interpolation if + // that's not the same delta, since t is a ratio. + // - a different value delta to check if it really _interpolates_, + // not just extrapolates further after A,B + SimpleSnapshot third = new SimpleSnapshot(4, 4, 4); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + buffer.Add(third.remoteTimestamp, third); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + // -> interpolation time is already at '1' at the end. + // -> compute will add 0.5 deltaTime + // -> so we overshoot beyond the second one and move to the next + // + // localTime is at 4 + // third snapshot localTime is at 4. + // bufferTime is 2, so it is NOT old enough and we should wait! + double localTime = 4; + double deltaTime = 0.5; + double interpolationTime = 1; + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should still spit out a result between first & second. + Assert.That(result, Is.True); + // interpolation started at the end = 1 + // and deltaTime is 0.5, so we were at 1.5 internally. + // + // BUT there's NO reason to overshoot interpolationTime while we + // wait for the next snapshot which isn't old enough. + // we stopped movement anyway. + // interpolationTime overshoot is only for smooth transitions WHILE + // moving. + // for example, if we overshoot to 100 while waiting, then we would + // instantly skip the next 20 snapshots. + // => so it should be capped at max + // => which is always 0..delta, NOT first.time .. second.time!! + Assert.That(interpolationTime, Is.EqualTo(1)); + // buffer should be untouched. shouldn't have moved to third yet. + Assert.That(buffer.Count, Is.EqualTo(3)); + // computed snapshot should be all the way at second snapshot. + Assert.That(computed.value, Is.EqualTo(2).Within(Mathf.Epsilon)); + } + + // fifth step: interpolation time overshoots the end while having more + // snapshots available. + [Test] + public void Compute_Step5_OvershootWithEnoughSnapshots_MovesToNextSnapshotIfOldEnough() + { + // add two old enough snapshots + // (localTime - bufferTime) + SimpleSnapshot first = new SimpleSnapshot(0, 0, 1); + SimpleSnapshot second = new SimpleSnapshot(1, 1, 2); + // IMPORTANT: third snapshot needs to be: + // - a different time delta + // to test if overflow is correct if deltas are different. + // it's not obvious if we ever use t ratio between [0,1] where an + // overflow of 0.1 between A,B could speed up B,C interpolation if + // that's not the same delta, since t is a ratio. + // - a different value delta to check if it really _interpolates_, + // not just extrapolates further after A,B + SimpleSnapshot third = new SimpleSnapshot(3, 3, 4); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + buffer.Add(third.remoteTimestamp, third); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + // -> interpolation time is already at '1' at the end. + // -> compute will add 0.5 deltaTime + // -> so we overshoot beyond the second one and move to the next + // + // localTime is 5. third snapshot localTime is at 3. + // bufferTime is 2. + // so third is exactly old enough and we should move there. + double localTime = 5; + double deltaTime = 0.5; + double interpolationTime = 1; + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should spit out the interpolated snapshot + Assert.That(result, Is.True); + // interpolation started at the end = 1 + // and deltaTime is 0.5, so we were at 1.5 internally. + // we have more snapshots, so we jump to the next and subtract '1' + // 1 + 0.5 = 1.5 => -1 => 0.5 + Assert.That(interpolationTime, Is.EqualTo(0.5)); + // buffer's first entry should have been removed + Assert.That(buffer.Count, Is.EqualTo(2)); + // computed snapshot should be 1/4 way between second and third + // because delta is 2 and interpolationTime is at 0.5 which is 1/4 + Assert.That(computed.value, Is.EqualTo(2.5).Within(Mathf.Epsilon)); + } + + // fifth step: interpolation time overshoots 2x the end while having + // >= 2 more snapshots available. it should correctly jump + // ahead the first pending one to the second one. + [Test] + public void Compute_Step5_OvershootWithEnoughSnapshots_2x_MovesToSecondNextSnapshot() + { + // add two old enough snapshots + // (localTime - bufferTime) + SimpleSnapshot first = new SimpleSnapshot(0, 0, 1); + SimpleSnapshot second = new SimpleSnapshot(1, 1, 2); + // IMPORTANT: third snapshot needs to be: + // - a different time delta + // to test if overflow is correct if deltas are different. + // it's not obvious if we ever use t ratio between [0,1] where an + // overflow of 0.1 between A,B could speed up B,C interpolation if + // that's not the same delta, since t is a ratio. + // - a different value delta to check if it really _interpolates_, + // not just extrapolates further after A,B + SimpleSnapshot third = new SimpleSnapshot(3, 3, 4); + SimpleSnapshot fourth = new SimpleSnapshot(5, 5, 6); + buffer.Add(first.remoteTimestamp, first); + buffer.Add(second.remoteTimestamp, second); + buffer.Add(third.remoteTimestamp, third); + buffer.Add(fourth.remoteTimestamp, fourth); + + // compute with initialized remoteTime and buffer time of 2 seconds + // and a delta time to be sure that we move along it no matter what. + // -> interpolation time is already at '1' at the end. + // -> compute will add 1.5 deltaTime + // -> so we should overshoot beyond second and third even + // + // localTime is 7. fourth snapshot localTime is at 5. + // bufferTime is 2. + // so fourth is exactly old enough and we should move there. + double localTime = 7; + double deltaTime = 2.5; + double interpolationTime = 1; + float bufferTime = 2; + int catchupThreshold = Int32.MaxValue; + float catchupMultiplier = 0; + bool result = SnapshotInterpolation.Compute(localTime, deltaTime, ref interpolationTime, bufferTime, buffer, catchupThreshold, catchupMultiplier, SimpleSnapshot.Interpolate, out SimpleSnapshot computed); + + // should spit out the interpolated snapshot + Assert.That(result, Is.True); + // interpolation started at the end = 1 + // and deltaTime is 2.5, so we were at 4.5 internally. + // we have more snapshots, so we: + // * jump to third, subtract delta of 1-0 = 1 => 2.5 + // * jump to fourth, subtract delta of 3-1 = 2 => 0.5 + // * end up at 0.5 again, between third and fourth + Assert.That(interpolationTime, Is.EqualTo(0.5)); + // buffer's first entry should have been removed + Assert.That(buffer.Count, Is.EqualTo(2)); + // computed snapshot should be 1/4 way between second and third + // because delta is 2 and interpolationTime is at 0.5 which is 1/4 + Assert.That(computed.value, Is.EqualTo(4.5).Within(Mathf.Epsilon)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SnapshotInterpolationTests.cs.meta b/Assets/Mirror/Tests/Editor/SnapshotInterpolationTests.cs.meta new file mode 100644 index 000000000..31edb4df0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SnapshotInterpolationTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d061a81bee3e4558b19b0d4dbedc8f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/StructMessagesTests.cs b/Assets/Mirror/Tests/Editor/StructMessagesTests.cs new file mode 100644 index 000000000..79f03fca1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/StructMessagesTests.cs @@ -0,0 +1,36 @@ +using NUnit.Framework; + +namespace Mirror.Tests.StructMessages +{ + public struct SomeStructMessage : NetworkMessage + { + public int someValue; + } + + [TestFixture] + public class StructMessagesTests + { + [Test] + public void SerializeAreAddedWhenEmptyInStruct() + { + NetworkWriter writer = new NetworkWriter(); + + const int someValue = 3; + writer.Write(new SomeStructMessage + { + someValue = someValue, + }); + + byte[] arr = writer.ToArray(); + + NetworkReader reader = new NetworkReader(arr); + SomeStructMessage received = reader.Read(); + + Assert.AreEqual(someValue, received.someValue); + + 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}"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/StructMessagesTests.cs.meta b/Assets/Mirror/Tests/Editor/StructMessagesTests.cs.meta new file mode 100644 index 000000000..f44bd63aa --- /dev/null +++ b/Assets/Mirror/Tests/Editor/StructMessagesTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8268c9b6cd466ae4c806291bdc88c0e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncDictionaryTest.cs b/Assets/Mirror/Tests/Editor/SyncDictionaryTest.cs new file mode 100644 index 000000000..e44927c4d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncDictionaryTest.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Mirror.Tests +{ + [TestFixture] + public class SyncDictionaryTest + { + SyncDictionary serverSyncDictionary; + SyncDictionary clientSyncDictionary; + int serverSyncDictionaryDirtyCalled; + int clientSyncDictionaryDirtyCalled; + + void SerializeAllTo(T fromList, T toList) where T : SyncObject + { + NetworkWriter writer = new NetworkWriter(); + fromList.OnSerializeAll(writer); + NetworkReader reader = new NetworkReader(writer.ToArray()); + toList.OnDeserializeAll(reader); + } + + void SerializeDeltaTo(T fromList, T toList) where T : SyncObject + { + NetworkWriter writer = new NetworkWriter(); + fromList.OnSerializeDelta(writer); + NetworkReader reader = new NetworkReader(writer.ToArray()); + toList.OnDeserializeDelta(reader); + fromList.ClearChanges(); + } + + [SetUp] + public void SetUp() + { + serverSyncDictionary = new SyncDictionary(); + clientSyncDictionary = new SyncDictionary(); + + // add some data to the list + serverSyncDictionary.Add(0, "Hello"); + serverSyncDictionary.Add(1, "World"); + serverSyncDictionary.Add(2, "!"); + SerializeAllTo(serverSyncDictionary, clientSyncDictionary); + + // set up dirty callbacks for testing. + // AFTER adding the example data. we already know we added that data. + serverSyncDictionaryDirtyCalled = 0; + clientSyncDictionaryDirtyCalled = 0; + serverSyncDictionary.OnDirty = () => ++serverSyncDictionaryDirtyCalled; + clientSyncDictionary.OnDirty = () => ++clientSyncDictionaryDirtyCalled; + } + + [Test] + public void TestInit() + { + Dictionary comparer = new Dictionary + { + [0] = "Hello", + [1] = "World", + [2] = "!" + }; + Assert.That(clientSyncDictionary[0], Is.EqualTo("Hello")); + Assert.That(clientSyncDictionary, Is.EquivalentTo(comparer)); + } + + // test the '= List{1,2,3}' constructor. + // it calls .Add(1); .Add(2); .Add(3) in the constructor. + // (the OnDirty change broke this and we didn't have a test before) + [Test] + public void CurlyBracesConstructor() + { + SyncDictionary dict = new SyncDictionary{{1,"1"}, {2,"2"}, {3,"3"}}; + Assert.That(dict.Count, Is.EqualTo(3)); + } + + [Test] + public void TestAdd() + { + serverSyncDictionary.Add(4, "yay"); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary.ContainsKey(4)); + Assert.That(clientSyncDictionary[4], Is.EqualTo("yay")); + } + + [Test] + public void TestClear() + { + serverSyncDictionary.Clear(); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(serverSyncDictionary, Is.EquivalentTo(new SyncDictionary())); + } + + [Test] + public void TestSet() + { + serverSyncDictionary[1] = "yay"; + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary.ContainsKey(1)); + Assert.That(clientSyncDictionary[1], Is.EqualTo("yay")); + } + + [Test] + public void TestBareSet() + { + serverSyncDictionary[4] = "yay"; + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary.ContainsKey(4)); + Assert.That(clientSyncDictionary[4], Is.EqualTo("yay")); + } + + [Test] + public void TestBareSetNull() + { + serverSyncDictionary[4] = null; + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary[4], Is.Null); + Assert.That(clientSyncDictionary.ContainsKey(4)); + } + + [Test] + public void TestConsecutiveSet() + { + serverSyncDictionary[1] = "yay"; + serverSyncDictionary[1] = "world"; + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary[1], Is.EqualTo("world")); + } + + [Test] + public void TestNullSet() + { + serverSyncDictionary[1] = null; + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary.ContainsKey(1)); + Assert.That(clientSyncDictionary[1], Is.Null); + } + + [Test] + public void TestRemove() + { + serverSyncDictionary.Remove(1); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(!clientSyncDictionary.ContainsKey(1)); + } + + [Test] + public void TestMultSync() + { + serverSyncDictionary.Add(10, "1"); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + // add some delta and see if it applies + serverSyncDictionary.Add(11, "2"); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary.ContainsKey(10)); + Assert.That(clientSyncDictionary[10], Is.EqualTo("1")); + Assert.That(clientSyncDictionary.ContainsKey(11)); + Assert.That(clientSyncDictionary[11], Is.EqualTo("2")); + } + + [Test] + public void TestContains() + { + Assert.That(!clientSyncDictionary.Contains(new KeyValuePair(2, "Hello"))); + serverSyncDictionary[2] = "Hello"; + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(clientSyncDictionary.Contains(new KeyValuePair(2, "Hello"))); + } + + [Test] + public void CallbackTest() + { + bool called = false; + clientSyncDictionary.Callback += (op, index, item) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_ADD)); + Assert.That(index, Is.EqualTo(3)); + Assert.That(item, Is.EqualTo("yay")); + Assert.That(clientSyncDictionary[index], Is.EqualTo("yay")); + + }; + serverSyncDictionary.Add(3, "yay"); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(called, Is.True); + } + + [Test] + public void ServerCallbackTest() + { + bool called = false; + serverSyncDictionary.Callback += (op, index, item) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_ADD)); + Assert.That(index, Is.EqualTo(3)); + Assert.That(item, Is.EqualTo("yay")); + Assert.That(serverSyncDictionary[index], Is.EqualTo("yay")); + }; + serverSyncDictionary[3] = "yay"; + Assert.That(called, Is.True); + } + + [Test] + public void CallbackRemoveTest() + { + bool called = false; + clientSyncDictionary.Callback += (op, key, item) => + { + called = true; + Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_REMOVE)); + Assert.That(item, Is.EqualTo("World")); + }; + serverSyncDictionary.Remove(1); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + Assert.That(called, Is.True); + } + + [Test] + public void CountTest() + { + Assert.That(serverSyncDictionary.Count, Is.EqualTo(3)); + } + + [Test] + public void CopyToTest() + { + KeyValuePair[] data = new KeyValuePair[3]; + + clientSyncDictionary.CopyTo(data, 0); + + Assert.That(data, Is.EquivalentTo(new KeyValuePair[] + { + new KeyValuePair(0, "Hello"), + new KeyValuePair(1, "World"), + new KeyValuePair(2, "!"), + + })); + } + + [Test] + public void CopyToOutOfRangeTest() + { + KeyValuePair[] data = new KeyValuePair[3]; + + Assert.Throws(typeof(ArgumentOutOfRangeException), delegate + { + clientSyncDictionary.CopyTo(data, -1); + }); + } + + [Test] + public void CopyToOutOfBoundsTest() + { + KeyValuePair[] data = new KeyValuePair[3]; + + Assert.Throws(typeof(ArgumentException), delegate + { + clientSyncDictionary.CopyTo(data, 2); + }); + } + + [Test] + public void TestRemovePair() + { + KeyValuePair data = new KeyValuePair(0, "Hello"); + + serverSyncDictionary.Remove(data); + + Assert.That(serverSyncDictionary, Is.EquivalentTo(new KeyValuePair[] + { + new KeyValuePair(1, "World"), + new KeyValuePair(2, "!"), + })); + } + + [Test] + public void ReadOnlyTest() + { + Assert.That(serverSyncDictionary.IsReadOnly, Is.False); + Assert.That(clientSyncDictionary.IsReadOnly, Is.True); + } + + [Test] + public void WritingToReadOnlyThrows() + { + Assert.Throws(() => clientSyncDictionary.Add(50, "fail")); + } + + [Test] + public void DirtyTest() + { + // Sync Delta to clear dirty + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + + // nothing to send + Assert.That(serverSyncDictionaryDirtyCalled, Is.EqualTo(0)); + + // something has changed + serverSyncDictionary.Add(15, "yay"); + Assert.That(serverSyncDictionaryDirtyCalled, Is.EqualTo(1)); + SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); + } + + [Test] + public void ObjectCanBeReusedAfterReset() + { + clientSyncDictionary.Reset(); + + // make old client the host + SyncDictionary hostList = clientSyncDictionary; + SyncDictionary clientList2 = new SyncDictionary(); + + Assert.That(hostList.IsReadOnly, Is.False); + + // Check Add and Sync without errors + hostList.Add(30, "hello"); + hostList.Add(35, "world"); + SerializeDeltaTo(hostList, clientList2); + } + + [Test] + public void ResetShouldSetReadOnlyToFalse() + { + clientSyncDictionary.Reset(); + Assert.That(clientSyncDictionary.IsReadOnly, Is.False); + } + + [Test] + public void ResetShouldClearChanges() + { + serverSyncDictionary.Reset(); + Assert.That(serverSyncDictionary.GetChangeCount(), Is.Zero); + } + + [Test] + public void ResetShouldClearItems() + { + serverSyncDictionary.Reset(); + Assert.That(serverSyncDictionary, Is.Empty); + } + + [Test] + public void IsRecording() + { + // shouldn't record changes if IsRecording() returns false + serverSyncDictionary.ClearChanges(); + serverSyncDictionary.IsRecording = () => false; + serverSyncDictionary[42] = null; + Assert.That(serverSyncDictionary.GetChangeCount(), Is.EqualTo(0)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncDictionaryTest.cs.meta b/Assets/Mirror/Tests/Editor/SyncDictionaryTest.cs.meta new file mode 100644 index 000000000..552e96ded --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncDictionaryTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cadf48c3662efac4181b91f5c9c88774 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncListClassTest.cs b/Assets/Mirror/Tests/Editor/SyncListClassTest.cs new file mode 100644 index 000000000..f6ed2b402 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncListClassTest.cs @@ -0,0 +1,78 @@ +using System.Linq; +using NUnit.Framework; + +namespace Mirror.Tests +{ + class TestObjectBehaviour : NetworkBehaviour + { + // note synclists must be a property of a NetworkBehavior so that + // the weaver generates the reader and writer for the object + public readonly SyncList myList = new SyncList(); + } + + public class SyncListClassTest + { + [Test] + public void RemoveShouldRemoveItem() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + + SyncListTest.SerializeAllTo(serverList, clientList); + + // add some items + TestObject item1 = new TestObject { id = 1, text = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nostrum ullam aliquid perferendis, aut nihil sunt quod ipsum corporis a. Cupiditate, alias. Commodi, molestiae distinctio repellendus dolor similique delectus inventore eum." }; + serverList.Add(item1); + TestObject item2 = new TestObject { id = 2, text = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nostrum ullam aliquid perferendis, aut nihil sunt quod ipsum corporis a. Cupiditate, alias. Commodi, molestiae distinctio repellendus dolor similique delectus inventore eum." }; + serverList.Add(item2); + + // sync + SyncListTest.SerializeDeltaTo(serverList, clientList); + + // clear all items + serverList.Remove(item1); + + // sync + SyncListTest.SerializeDeltaTo(serverList, clientList); + + Assert.IsFalse(clientList.Any(x => x.id == item1.id)); + Assert.IsTrue(clientList.Any(x => x.id == item2.id)); + } + + [Test] + public void ClearShouldClearAll() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + + SyncListTest.SerializeAllTo(serverList, clientList); + + // add some items + TestObject item1 = new TestObject { id = 1, text = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nostrum ullam aliquid perferendis, aut nihil sunt quod ipsum corporis a. Cupiditate, alias. Commodi, molestiae distinctio repellendus dolor similique delectus inventore eum." }; + serverList.Add(item1); + TestObject item2 = new TestObject { id = 2, text = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nostrum ullam aliquid perferendis, aut nihil sunt quod ipsum corporis a. Cupiditate, alias. Commodi, molestiae distinctio repellendus dolor similique delectus inventore eum." }; + serverList.Add(item2); + + // sync + SyncListTest.SerializeDeltaTo(serverList, clientList); + + // clear all items + serverList.Clear(); + + // sync + SyncListTest.SerializeDeltaTo(serverList, clientList); + + Assert.That(clientList.Count, Is.Zero); + + Assert.IsFalse(clientList.Any(x => x.id == item1.id)); + Assert.IsFalse(clientList.Any(x => x.id == item2.id)); + } + } + + [System.Serializable] + public class TestObject + { + public int id; + public string text; + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncListClassTest.cs.meta b/Assets/Mirror/Tests/Editor/SyncListClassTest.cs.meta new file mode 100644 index 000000000..d26a39d04 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncListClassTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c4bb793f509fcb47bdf830656826f2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncListStructTest.cs b/Assets/Mirror/Tests/Editor/SyncListStructTest.cs new file mode 100644 index 000000000..6c3a6e565 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncListStructTest.cs @@ -0,0 +1,73 @@ +using NUnit.Framework; + +namespace Mirror.Tests +{ + class TestPlayerBehaviour : NetworkBehaviour + { + // note synclists must be a property of a NetworkBehavior so that + // the weaver generates the reader and writer for the object + public readonly SyncList myList = new SyncList(); + } + + public class SyncListStructTest + { + [Test] + public void ListIsDirtyWhenModifingAndSettingStruct() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + + // set up dirty callback + int serverListDirtyCalled = 0; + serverList.OnDirty = () => ++serverListDirtyCalled; + + SyncListTest.SerializeAllTo(serverList, clientList); + serverList.Add(new TestPlayer { item = new TestItem { price = 10 } }); + Assert.That(serverListDirtyCalled, Is.EqualTo(1)); + SyncListTest.SerializeDeltaTo(serverList, clientList); + serverListDirtyCalled = 0; + + TestPlayer player = serverList[0]; + player.item.price = 15; + serverList[0] = player; + + Assert.That(serverListDirtyCalled, Is.EqualTo(1)); + } + + [Test] + public void OldValueShouldNotBeNewValue() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + SyncListTest.SerializeAllTo(serverList, clientList); + serverList.Add(new TestPlayer { item = new TestItem { price = 10 } }); + SyncListTest.SerializeDeltaTo(serverList, clientList); + + TestPlayer player = serverList[0]; + player.item.price = 15; + serverList[0] = player; + + bool callbackCalled = false; + clientList.Callback += (SyncList.Operation op, int itemIndex, TestPlayer oldItem, TestPlayer newItem) => + { + Assert.That(op == SyncList.Operation.OP_SET, Is.True); + Assert.That(itemIndex, Is.EqualTo(0)); + Assert.That(oldItem.item.price, Is.EqualTo(10)); + Assert.That(newItem.item.price, Is.EqualTo(15)); + callbackCalled = true; + }; + + SyncListTest.SerializeDeltaTo(serverList, clientList); + Assert.IsTrue(callbackCalled); + } + } + + public struct TestPlayer + { + public TestItem item; + } + public struct TestItem + { + public float price; + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncListStructTest.cs.meta b/Assets/Mirror/Tests/Editor/SyncListStructTest.cs.meta new file mode 100644 index 000000000..b3a20facc --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncListStructTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1cad81f2aee838a4ababa9c8ee23a700 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncListTest.cs b/Assets/Mirror/Tests/Editor/SyncListTest.cs new file mode 100644 index 000000000..9e33ccec7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncListTest.cs @@ -0,0 +1,434 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Mirror.Tests +{ + [TestFixture] + public class SyncListTest + { + SyncList serverSyncList; + SyncList clientSyncList; + int serverSyncListDirtyCalled; + int clientSyncListDirtyCalled; + + public static void SerializeAllTo(T fromList, T toList) where T : SyncObject + { + NetworkWriter writer = new NetworkWriter(); + fromList.OnSerializeAll(writer); + NetworkReader reader = new NetworkReader(writer.ToArray()); + toList.OnDeserializeAll(reader); + + 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}"); + + } + + public static void SerializeDeltaTo(T fromList, T toList) where T : SyncObject + { + NetworkWriter writer = new NetworkWriter(); + fromList.OnSerializeDelta(writer); + NetworkReader reader = new NetworkReader(writer.ToArray()); + toList.OnDeserializeDelta(reader); + fromList.ClearChanges(); + + int writeLength = writer.Position; + int readLength = reader.Position; + Assert.That(writeLength == readLength, $"OnSerializeDelta and OnDeserializeDelta calls write the same amount of data\n writeLength={writeLength}\n readLength={readLength}"); + } + + [SetUp] + public void SetUp() + { + serverSyncList = new SyncList(); + clientSyncList = new SyncList(); + + // add some data to the list + serverSyncList.Add("Hello"); + serverSyncList.Add("World"); + serverSyncList.Add("!"); + SerializeAllTo(serverSyncList, clientSyncList); + + // set up dirty callbacks for testing + // AFTER adding the example data. we already know we added that data. + serverSyncList.OnDirty = () => ++serverSyncListDirtyCalled; + clientSyncList.OnDirty = () => ++clientSyncListDirtyCalled; + serverSyncListDirtyCalled = 0; + clientSyncListDirtyCalled = 0; + } + + [Test] + public void TestInit() + { + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "World", "!" })); + } + + // test the '= List{1,2,3}' constructor. + // it calls .Add(1); .Add(2); .Add(3) in the constructor. + // (the OnDirty change broke this and we didn't have a test before) + [Test] + public void CurlyBracesConstructor() + { + SyncList list = new SyncList{1,2,3}; + Assert.That(list.Count, Is.EqualTo(3)); + } + + [Test] + public void TestAdd() + { + serverSyncList.Add("yay"); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "World", "!", "yay" })); + } + + [Test] + public void TestAddRange() + { + serverSyncList.AddRange(new[] { "One", "Two", "Three" }); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EqualTo(new[] { "Hello", "World", "!", "One", "Two", "Three" })); + } + + [Test] + public void TestClear() + { + serverSyncList.Clear(); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new string[] {})); + } + + [Test] + public void TestInsert() + { + serverSyncList.Insert(0, "yay"); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "yay", "Hello", "World", "!" })); + } + + [Test] + public void TestInsertRange() + { + serverSyncList.InsertRange(1, new[] { "One", "Two", "Three" }); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EqualTo(new[] { "Hello", "One", "Two", "Three", "World", "!" })); + } + + [Test] + public void TestSet() + { + serverSyncList[1] = "yay"; + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList[1], Is.EqualTo("yay")); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "yay", "!" })); + } + + [Test] + public void TestSetNull() + { + serverSyncList[1] = null; + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList[1], Is.EqualTo(null)); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", null, "!" })); + serverSyncList[1] = "yay"; + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "yay", "!" })); + } + + [Test] + public void TestRemoveAll() + { + serverSyncList.RemoveAll(entry => entry.Contains("l")); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "!" })); + } + + [Test] + public void TestRemoveAllNone() + { + serverSyncList.RemoveAll(entry => entry == "yay"); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "World", "!" })); + } + + [Test] + public void TestRemoveAt() + { + serverSyncList.RemoveAt(1); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "!" })); + } + + [Test] + public void TestRemove() + { + serverSyncList.Remove("World"); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "!" })); + } + + [Test] + public void TestFindIndex() + { + int index = serverSyncList.FindIndex(entry => entry == "World"); + Assert.That(index, Is.EqualTo(1)); + } + + [Test] + public void TestFind() + { + string element = serverSyncList.Find(entry => entry == "World"); + Assert.That(element, Is.EqualTo("World")); + } + + [Test] + public void TestNoFind() + { + string nonexistent = serverSyncList.Find(entry => entry == "yay"); + Assert.That(nonexistent, Is.Null); + } + + [Test] + public void TestFindAll() + { + List results = serverSyncList.FindAll(entry => entry.Contains("l")); + Assert.That(results, Is.EquivalentTo(new[] { "Hello", "World" })); + } + + [Test] + public void TestFindAllNonExistent() + { + List nonexistent = serverSyncList.FindAll(entry => entry == "yay"); + Assert.That(nonexistent, Is.Empty); + } + + [Test] + public void TestMultSync() + { + serverSyncList.Add("1"); + SerializeDeltaTo(serverSyncList, clientSyncList); + // add some delta and see if it applies + serverSyncList.Add("2"); + SerializeDeltaTo(serverSyncList, clientSyncList); + Assert.That(clientSyncList, Is.EquivalentTo(new[] { "Hello", "World", "!", "1", "2" })); + } + + [Test] + public void SyncListIntTest() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + + serverList.Add(1); + serverList.Add(2); + serverList.Add(3); + SerializeDeltaTo(serverList, clientList); + + Assert.That(clientList, Is.EquivalentTo(new[] { 1, 2, 3 })); + } + + [Test] + public void SyncListBoolTest() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + + serverList.Add(true); + serverList.Add(false); + serverList.Add(true); + SerializeDeltaTo(serverList, clientList); + + Assert.That(clientList, Is.EquivalentTo(new[] { true, false, true })); + } + + [Test] + public void SyncListUIntTest() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + + serverList.Add(1U); + serverList.Add(2U); + serverList.Add(3U); + SerializeDeltaTo(serverList, clientList); + + Assert.That(clientList, Is.EquivalentTo(new[] { 1U, 2U, 3U })); + } + + [Test] + public void SyncListFloatTest() + { + SyncList serverList = new SyncList(); + SyncList clientList = new SyncList(); + + serverList.Add(1.0F); + serverList.Add(2.0F); + serverList.Add(3.0F); + SerializeDeltaTo(serverList, clientList); + + Assert.That(clientList, Is.EquivalentTo(new[] { 1.0F, 2.0F, 3.0F })); + } + + [Test] + public void CallbackTest() + { + bool called = false; + + clientSyncList.Callback += (op, index, oldItem, newItem) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncList.Operation.OP_ADD)); + Assert.That(index, Is.EqualTo(3)); + Assert.That(oldItem, Is.EqualTo(default(string))); + Assert.That(newItem, Is.EqualTo("yay")); + }; + + serverSyncList.Add("yay"); + SerializeDeltaTo(serverSyncList, clientSyncList); + + Assert.That(called, Is.True); + } + + [Test] + public void CallbackRemoveTest() + { + bool called = false; + + clientSyncList.Callback += (op, index, oldItem, newItem) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncList.Operation.OP_REMOVEAT)); + Assert.That(oldItem, Is.EqualTo("World")); + Assert.That(newItem, Is.EqualTo(default(string))); + }; + serverSyncList.Remove("World"); + SerializeDeltaTo(serverSyncList, clientSyncList); + + Assert.That(called, Is.True); + } + + [Test] + public void CallbackRemoveAtTest() + { + bool called = false; + + clientSyncList.Callback += (op, index, oldItem, newItem) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncList.Operation.OP_REMOVEAT)); + Assert.That(index, Is.EqualTo(1)); + Assert.That(oldItem, Is.EqualTo("World")); + Assert.That(newItem, Is.EqualTo(default(string))); + }; + + serverSyncList.RemoveAt(1); + SerializeDeltaTo(serverSyncList, clientSyncList); + + Assert.That(called, Is.True); + } + + [Test] + public void CountTest() + { + Assert.That(serverSyncList.Count, Is.EqualTo(3)); + } + + [Test] + public void ReadOnlyTest() + { + Assert.That(serverSyncList.IsReadOnly, Is.False); + Assert.That(clientSyncList.IsReadOnly, Is.True); + } + [Test] + public void WritingToReadOnlyThrows() + { + Assert.Throws(() => { clientSyncList.Add("fail"); }); + } + + [Test] + public void DirtyTest() + { + // Sync Delta to clear dirty + Assert.That(serverSyncListDirtyCalled, Is.EqualTo(0)); + SerializeDeltaTo(serverSyncList, clientSyncList); + + // nothing to send + Assert.That(serverSyncListDirtyCalled, Is.EqualTo(0)); + + // something has changed + serverSyncList.Add("1"); + Assert.That(serverSyncListDirtyCalled, Is.EqualTo(1)); + SerializeDeltaTo(serverSyncList, clientSyncList); + } + + [Test] + public void ObjectCanBeReusedAfterReset() + { + clientSyncList.Reset(); + + // make old client the host + SyncList hostList = clientSyncList; + SyncList clientList2 = new SyncList(); + + Assert.That(hostList.IsReadOnly, Is.False); + + // Check Add and Sync without errors + hostList.Add("hello"); + hostList.Add("world"); + SerializeDeltaTo(hostList, clientList2); + } + + [Test] + public void ResetShouldSetReadOnlyToFalse() + { + clientSyncList.Reset(); + + Assert.That(clientSyncList.IsReadOnly, Is.False); + } + + [Test] + public void ResetShouldClearChanges() + { + serverSyncList.Reset(); + + Assert.That(serverSyncList.GetChangeCount(), Is.Zero); + } + + [Test] + public void ResetShouldClearItems() + { + serverSyncList.Reset(); + + Assert.That(serverSyncList, Is.Empty); + } + + [Test] + public void IsRecording() + { + // shouldn't record changes if IsRecording() returns false + serverSyncList.ClearChanges(); + serverSyncList.IsRecording = () => false; + serverSyncList.Add("42"); + Assert.That(serverSyncList.GetChangeCount(), Is.EqualTo(0)); + } + } + + public static class SyncObjectTestMethods + { + public static uint GetChangeCount(this SyncObject syncObject) + { + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + syncObject.OnSerializeDelta(writer); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment())) + { + return reader.ReadUInt(); + } + } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncListTest.cs.meta b/Assets/Mirror/Tests/Editor/SyncListTest.cs.meta new file mode 100644 index 000000000..9b55701a4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncListTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a937d4274ff484d769209f2e0b0c1d8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncSetTest.cs b/Assets/Mirror/Tests/Editor/SyncSetTest.cs new file mode 100644 index 000000000..bb106c0ce --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncSetTest.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Mirror.Tests +{ + [TestFixture] + public class SyncSetTest + { + SyncHashSet serverSyncSet; + SyncHashSet clientSyncSet; + int serverSyncSetDirtyCalled; + int clientSyncSetDirtyCalled; + + void SerializeAllTo(T fromList, T toList) where T : SyncObject + { + NetworkWriter writer = new NetworkWriter(); + fromList.OnSerializeAll(writer); + NetworkReader reader = new NetworkReader(writer.ToArray()); + toList.OnDeserializeAll(reader); + } + + void SerializeDeltaTo(T fromList, T toList) where T : SyncObject + { + NetworkWriter writer = new NetworkWriter(); + fromList.OnSerializeDelta(writer); + NetworkReader reader = new NetworkReader(writer.ToArray()); + toList.OnDeserializeDelta(reader); + fromList.ClearChanges(); + } + + [SetUp] + public void SetUp() + { + serverSyncSet = new SyncHashSet(); + clientSyncSet = new SyncHashSet(); + + // add some data to the list + serverSyncSet.Add("Hello"); + serverSyncSet.Add("World"); + serverSyncSet.Add("!"); + SerializeAllTo(serverSyncSet, clientSyncSet); + + // set up dirty callbacks for testing + // AFTER adding the example data. we already know we added that data. + serverSyncSet.OnDirty = () => ++serverSyncSetDirtyCalled; + clientSyncSet.OnDirty = () => ++clientSyncSetDirtyCalled; + serverSyncSetDirtyCalled = 0; + clientSyncSetDirtyCalled = 0; + } + + [Test] + public void TestInit() + { + Assert.That(serverSyncSet, Is.EquivalentTo(new[] { "Hello", "World", "!" })); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "Hello", "World", "!" })); + } + + // test the '= List{1,2,3}' constructor. + // it calls .Add(1); .Add(2); .Add(3) in the constructor. + // (the OnDirty change broke this and we didn't have a test before) + [Test] + public void CurlyBracesConstructor() + { + SyncHashSet set = new SyncHashSet{1,2,3}; + Assert.That(set.Count, Is.EqualTo(3)); + } + + [Test] + public void TestAdd() + { + serverSyncSet.Add("yay"); + Assert.That(serverSyncSetDirtyCalled, Is.EqualTo(1)); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "Hello", "World", "!", "yay" })); + } + + [Test] + public void TestClear() + { + serverSyncSet.Clear(); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new string[] {})); + } + + [Test] + public void TestRemove() + { + serverSyncSet.Remove("World"); + Assert.That(serverSyncSetDirtyCalled, Is.EqualTo(1)); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "Hello", "!" })); + } + + [Test] + public void TestMultSync() + { + serverSyncSet.Add("1"); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + // add some delta and see if it applies + serverSyncSet.Add("2"); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "Hello", "World", "!", "1", "2" })); + } + + [Test] + public void CallbackTest() + { + bool called = false; + + clientSyncSet.Callback += (op, item) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncHashSet.Operation.OP_ADD)); + Assert.That(item, Is.EqualTo("yay")); + }; + + serverSyncSet.Add("yay"); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + + Assert.That(called, Is.True); + } + + [Test] + public void CallbackRemoveTest() + { + bool called = false; + + clientSyncSet.Callback += (op, item) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncHashSet.Operation.OP_REMOVE)); + Assert.That(item, Is.EqualTo("World")); + }; + serverSyncSet.Remove("World"); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + + Assert.That(called, Is.True); + } + + [Test] + public void CountTest() + { + Assert.That(serverSyncSet.Count, Is.EqualTo(3)); + } + [Test] + public void TestExceptWith() + { + serverSyncSet.ExceptWith(new[] { "World", "Hello" }); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "!" })); + } + + [Test] + public void TestExceptWithSelf() + { + serverSyncSet.ExceptWith(serverSyncSet); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new String[] {})); + } + + [Test] + public void TestIntersectWith() + { + serverSyncSet.IntersectWith(new[] { "World", "Hello" }); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "World", "Hello" })); + } + + [Test] + public void TestIntersectWithSet() + { + serverSyncSet.IntersectWith(new HashSet { "World", "Hello" }); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "World", "Hello" })); + } + + [Test] + public void TestIsProperSubsetOf() + { + Assert.That(clientSyncSet.IsProperSubsetOf(new[] { "World", "Hello", "!", "pepe" })); + } + + [Test] + public void TestIsProperSubsetOfSet() + { + Assert.That(clientSyncSet.IsProperSubsetOf(new HashSet { "World", "Hello", "!", "pepe" })); + } + + [Test] + public void TestIsNotProperSubsetOf() + { + Assert.That(clientSyncSet.IsProperSubsetOf(new[] { "World", "!", "pepe" }), Is.False); + } + + [Test] + public void TestIsProperSuperSetOf() + { + Assert.That(clientSyncSet.IsProperSupersetOf(new[] { "World", "Hello" })); + } + + [Test] + public void TestIsSubsetOf() + { + Assert.That(clientSyncSet.IsSubsetOf(new[] { "World", "Hello", "!" })); + } + + [Test] + public void TestIsSupersetOf() + { + Assert.That(clientSyncSet.IsSupersetOf(new[] { "World", "Hello" })); + } + + [Test] + public void TestOverlaps() + { + Assert.That(clientSyncSet.Overlaps(new[] { "World", "my", "baby" })); + } + + [Test] + public void TestSetEquals() + { + Assert.That(clientSyncSet.SetEquals(new[] { "World", "Hello", "!" })); + } + + [Test] + public void TestSymmetricExceptWith() + { + serverSyncSet.SymmetricExceptWith(new HashSet { "Hello", "is" }); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "World", "is", "!" })); + } + + [Test] + public void TestSymmetricExceptWithSelf() + { + serverSyncSet.SymmetricExceptWith(serverSyncSet); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new String[] {})); + } + + [Test] + public void TestUnionWith() + { + serverSyncSet.UnionWith(new HashSet { "Hello", "is" }); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "World", "Hello", "is", "!" })); + } + + [Test] + public void TestUnionWithSelf() + { + serverSyncSet.UnionWith(serverSyncSet); + SerializeDeltaTo(serverSyncSet, clientSyncSet); + Assert.That(clientSyncSet, Is.EquivalentTo(new[] { "World", "Hello", "!" })); + } + + [Test] + public void ReadOnlyTest() + { + Assert.That(serverSyncSet.IsReadOnly, Is.False); + Assert.That(clientSyncSet.IsReadOnly, Is.True); + } + + [Test] + public void WritingToReadOnlyThrows() + { + Assert.Throws(() => { clientSyncSet.Add("5"); }); + } + + [Test] + public void ObjectCanBeReusedAfterReset() + { + clientSyncSet.Reset(); + + // make old client the host + SyncHashSet hostList = clientSyncSet; + SyncHashSet clientList2 = new SyncHashSet(); + + Assert.That(hostList.IsReadOnly, Is.False); + + // Check Add and Sync without errors + hostList.Add("1"); + hostList.Add("2"); + hostList.Add("3"); + SerializeDeltaTo(hostList, clientList2); + } + + [Test] + public void ResetShouldSetReadOnlyToFalse() + { + clientSyncSet.Reset(); + Assert.That(clientSyncSet.IsReadOnly, Is.False); + } + + [Test] + public void ResetShouldClearChanges() + { + serverSyncSet.Reset(); + Assert.That(serverSyncSet.GetChangeCount(), Is.Zero); + } + + [Test] + public void ResetShouldClearItems() + { + serverSyncSet.Reset(); + Assert.That(serverSyncSet, Is.Empty); + } + + [Test] + public void IsRecording() + { + // shouldn't record changes if IsRecording() returns false + serverSyncSet.ClearChanges(); + serverSyncSet.IsRecording = () => false; + serverSyncSet.Add("42"); + Assert.That(serverSyncSet.GetChangeCount(), Is.EqualTo(0)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncSetTest.cs.meta b/Assets/Mirror/Tests/Editor/SyncSetTest.cs.meta new file mode 100644 index 000000000..7411ffedd --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncSetTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 36dbb64593fa546edb477df3d88b6e1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncVarAttributeHookTest.cs b/Assets/Mirror/Tests/Editor/SyncVarAttributeHookTest.cs new file mode 100644 index 000000000..5bf65e0f7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarAttributeHookTest.cs @@ -0,0 +1,521 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.SyncVarAttributeTests +{ + class HookBehaviour : NetworkBehaviour + { + [SyncVar(hook = nameof(OnValueChanged))] + public int value = 0; + + public event Action HookCalled; + + void OnValueChanged(int oldValue, int newValue) + { + HookCalled.Invoke(oldValue, newValue); + } + } + + class GameObjectHookBehaviour : NetworkBehaviour + { + [SyncVar(hook = nameof(OnValueChanged))] + public GameObject value = null; + + public event Action HookCalled; + + void OnValueChanged(GameObject oldValue, GameObject newValue) + { + HookCalled.Invoke(oldValue, newValue); + } + } + + class NetworkIdentityHookBehaviour : NetworkBehaviour + { + [SyncVar(hook = nameof(OnValueChanged))] + public NetworkIdentity value = null; + + public event Action HookCalled; + + void OnValueChanged(NetworkIdentity oldValue, NetworkIdentity newValue) + { + HookCalled.Invoke(oldValue, newValue); + } + } + + class NetworkBehaviourHookBehaviour : NetworkBehaviour + { + [SyncVar(hook = nameof(OnValueChanged))] + public NetworkBehaviourHookBehaviour value = null; + + public event Action HookCalled; + + void OnValueChanged(NetworkBehaviourHookBehaviour oldValue, NetworkBehaviourHookBehaviour newValue) + { + HookCalled.Invoke(oldValue, newValue); + } + } + + class StaticHookBehaviour : NetworkBehaviour + { + [SyncVar(hook = nameof(OnValueChanged))] + public int value = 0; + + public static event Action HookCalled; + + static void OnValueChanged(int oldValue, int newValue) + { + HookCalled.Invoke(oldValue, newValue); + } + } + + class VirtualHookBase : NetworkBehaviour + { + [SyncVar(hook = nameof(OnValueChanged))] + public int value = 0; + + public event Action BaseHookCalled; + + protected virtual void OnValueChanged(int oldValue, int newValue) + { + BaseHookCalled.Invoke(oldValue, newValue); + } + } + + class VirtualOverrideHook : VirtualHookBase + { + public event Action OverrideHookCalled; + + protected override void OnValueChanged(int oldValue, int newValue) + { + OverrideHookCalled.Invoke(oldValue, newValue); + } + } + + abstract class AbstractHookBase : NetworkBehaviour + { + [SyncVar(hook = nameof(OnValueChanged))] + public int value = 0; + + protected abstract void OnValueChanged(int oldValue, int newValue); + } + + class AbstractHook : AbstractHookBase + { + public event Action HookCalled; + + protected override void OnValueChanged(int oldValue, int newValue) + { + HookCalled.Invoke(oldValue, newValue); + } + } + + public struct Proportions + { + public byte[] Array; + } + class ImerHook_Ldflda : NetworkBehaviour + { + // to check + public byte[] ldflda_Array; + + [SyncVar(hook = nameof(OnUpdateProportions))] + public Proportions _syncProportions; + + protected void OnUpdateProportions(Proportions old, Proportions new_) + { + // _new is fine with the new values. + // assigning to _syncProportions is fine too. + _syncProportions = new_; + + // loading _syncProportions.Array would still load the original SyncVar, + // not the replacement. so .Array was still null. + // we needed to replace ldflda here. + // + // this throws if it still loads the old _syncProportions after weaving + // because the .Array was still null there. + ldflda_Array = _syncProportions.Array; + Debug.Log("Array= " + ldflda_Array); + } + } + + + // repro for the bug found by David_548219 in discord where setting + // MyStruct.value would throw invalid IL + public struct DavidStruct + { + public int Value; + } + class DavidHookComponent : NetworkBehaviour + { + [SyncVar] public DavidStruct syncvar; + + public override void OnStartServer() + { + syncvar.Value = 42; + } + } + + public class SyncVarAttributeHookTest : SyncVarAttributeTestBase + { + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // start server & connect client because we need spawn functions + NetworkServer.Listen(1); + ConnectClientBlockingAuthenticatedAndReady(out _); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Hook_CalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out HookBehaviour serverObject, + out _, out _, out HookBehaviour clientObject); + + const int serverValue = 24; + + // change it on server + serverObject.value = serverValue; + + // hook should change it on client + int callCount = 0; + clientObject.HookCalled += (oldValue, newValue) => + { + callCount++; + Assert.That(oldValue, Is.EqualTo(0)); + Assert.That(newValue, Is.EqualTo(serverValue)); + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Hook_NotCalledWhenSyncingSameValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out HookBehaviour serverObject, + out _, out _, out HookBehaviour clientObject); + + const int clientValue = 16; + const int serverValue = 16; + + // set both to same values once + serverObject.value = serverValue; + clientObject.value = clientValue; + + // client hook + int callCount = 0; + clientObject.HookCalled += (oldValue, newValue) => + { + callCount++; + }; + + // hook shouldn't be called because both already have same value + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(callCount, Is.EqualTo(0)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Hook_OnlyCalledOnClient(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out HookBehaviour serverObject, + out _, out _, out HookBehaviour clientObject); + + // set up hooks on server and client + int clientCalled = 0; + int serverCalled = 0; + clientObject.HookCalled += (oldValue, newValue) => ++clientCalled; + serverObject.HookCalled += (oldValue, newValue) => ++serverCalled; + + // change on server + ++serverObject.value; + //++clientObject.value; + + // sync. hook should've only been called on client. + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(clientCalled, Is.EqualTo(1)); + Assert.That(serverCalled, Is.EqualTo(0)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void StaticMethod_HookCalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out StaticHookBehaviour serverObject, + out _, out _, out StaticHookBehaviour clientObject); + + const int serverValue = 24; + + // change it on server + serverObject.value = serverValue; + + // hook should change it on client + int hookcallCount = 0; + StaticHookBehaviour.HookCalled += (oldValue, newValue) => + { + hookcallCount++; + Assert.That(oldValue, Is.EqualTo(0)); + Assert.That(newValue, Is.EqualTo(serverValue)); + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(hookcallCount, Is.EqualTo(1)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void GameObjectHook_HookCalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out GameObjectHookBehaviour serverObject, + out _, out _, out GameObjectHookBehaviour clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out GameObject serverValue, out _, + out GameObject clientValue, out _); + + // change it on server + clientObject.value = null; + serverObject.value = serverValue; + + // hook should change it on client + int callCount = 0; + clientObject.HookCalled += (oldValue, newValue) => + { + callCount++; + Assert.That(oldValue, Is.EqualTo(null)); + Assert.That(newValue, Is.EqualTo(clientValue)); + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void NetworkIdentityHook_HookCalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out NetworkIdentityHookBehaviour serverObject, + out _, out _, out NetworkIdentityHookBehaviour clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out NetworkIdentity serverValue, + out _, out NetworkIdentity clientValue); + + // change it on server + serverObject.value = serverValue; + clientObject.value = null; + + // hook should change it on client + int callCount = 0; + clientObject.HookCalled += (oldValue, newValue) => + { + callCount++; + Assert.That(oldValue, Is.EqualTo(null)); + Assert.That(newValue, Is.EqualTo(clientValue)); + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void NetworkBehaviourHook_HookCalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out NetworkBehaviourHookBehaviour serverObject, + out _, out _, out NetworkBehaviourHookBehaviour clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out _, out NetworkBehaviourHookBehaviour serverValue, + out _, out _, out NetworkBehaviourHookBehaviour clientValue); + + // change it on server + serverObject.value = serverValue; + clientObject.value = null; + + // hook should change it on client + int callCount = 0; + clientObject.HookCalled += (oldValue, newValue) => + { + callCount++; + Assert.That(oldValue, Is.EqualTo(null)); + Assert.That(newValue, Is.EqualTo(clientValue)); + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void VirtualHook_HookCalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out VirtualHookBase serverObject, + out _, out _, out VirtualHookBase clientObject); + + const int clientValue = 10; + const int serverValue = 24; + + // change it on server + serverObject.value = serverValue; + clientObject.value = clientValue; + + // hook should change it on client + int baseCallCount = 0; + clientObject.BaseHookCalled += (oldValue, newValue) => + { + baseCallCount++; + Assert.That(oldValue, Is.EqualTo(clientValue)); + Assert.That(newValue, Is.EqualTo(serverValue)); + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(baseCallCount, Is.EqualTo(1)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void VirtualOverrideHook_HookCalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out VirtualOverrideHook serverObject, + out _, out _, out VirtualOverrideHook clientObject); + + const int serverValue = 24; + + // change it on server + serverObject.value = serverValue; + + // hook should change it on client + int overrideCallCount = 0; + int baseCallCount = 0; + clientObject.OverrideHookCalled += (oldValue, newValue) => + { + overrideCallCount++; + Assert.That(oldValue, Is.EqualTo(0)); + Assert.That(newValue, Is.EqualTo(serverValue)); + }; + clientObject.BaseHookCalled += (oldValue, newValue) => + { + baseCallCount++; + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(overrideCallCount, Is.EqualTo(1)); + Assert.That(baseCallCount, Is.EqualTo(0)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void AbstractHook_HookCalledWhenSyncingChangedValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out AbstractHook serverObject, + out _, out _, out AbstractHook clientObject); + + const int clientValue = 10; + const int serverValue = 24; + + // change it on server + serverObject.value = serverValue; + clientObject.value = clientValue; + + // hook should change it on client + int callCount = 0; + clientObject.HookCalled += (oldValue, newValue) => + { + callCount++; + Assert.That(oldValue, Is.EqualTo(clientValue)); + Assert.That(newValue, Is.EqualTo(serverValue)); + }; + + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + Assert.That(callCount, Is.EqualTo(1)); + } + + // test to prevent the SyncVar Weaver bug that imer found. + // https://github.com/vis2k/Mirror/pull/2957#issuecomment-1019692366 + // when loading "n = MySyncVar.n", 'ldfdla' loads 'MySyncVar'. + // if we replace MySyncVar with a weaved version like for SyncVar, + // then ldflda for cases like "n = MySyncVar.n" needs to be replaced too. + // + // this wasn't necessary for the original SyncVars, which is why the bug + // wasn't caught by a unit test to begin with. + [Test] + [TestCase(true)] + [TestCase(false)] + public void ImerHook_Ldflda_Uses_Correct_SyncVar(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out ImerHook_Ldflda serverObject, + out _, out _, out ImerHook_Ldflda clientObject); + + // change it on server + serverObject._syncProportions = new Proportions{Array = new byte[]{3, 4}}; + + // sync to client + bool written = SyncToClient(serverObject, clientObject, intialState); + Assert.IsTrue(written); + + // client uses ldflda to get replacement.Array. + // we synced an array with two values, so if ldflda uses the correct + // SyncVar then it shouldn't be null anymore now. + Assert.That(clientObject.ldflda_Array, !Is.Null); + } + + // repro for the bug found by David_548219 in discord where setting + // MyStruct.value would throw invalid IL. + // could happen if we change Weaver [SyncVar] logic / replacements. + // testing syncVar = X isn't enough. + // we should have a test for syncVar.value = X too. + [Test] + [TestCase(true)] + [TestCase(false)] + public void DavidHook_SetSyncVarStructsValue(bool intialState) + { + CreateNetworkedAndSpawn( + out _, out _, out DavidHookComponent serverObject, + out _, out _, out DavidHookComponent clientObject); + + // change it on server. + // should not throw. + serverObject.syncvar.Value = 1337; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarAttributeHookTest.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarAttributeHookTest.cs.meta new file mode 100644 index 000000000..fbbf021cf --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarAttributeHookTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e0f51ab2719c99408ae9c0466fdedc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncVarAttributeTest.cs b/Assets/Mirror/Tests/Editor/SyncVarAttributeTest.cs new file mode 100644 index 000000000..bbaa3b861 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarAttributeTest.cs @@ -0,0 +1,438 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.SyncVarAttributeTests +{ + class MockPlayer : NetworkBehaviour + { + public struct Guild + { + public string name; + } + + [SyncVar] + public Guild guild; + } + + class SyncVarGameObject : NetworkBehaviour + { + [SyncVar] + public GameObject value; + } + class SyncVarNetworkIdentity : NetworkBehaviour + { + [SyncVar] + public NetworkIdentity value; + } + class SyncVarTransform : NetworkBehaviour + { + [SyncVar] + public Transform value; + } + class SyncVarNetworkBehaviour : NetworkBehaviour + { + [SyncVar] + public SyncVarNetworkBehaviour value; + } + class SyncVarAbstractNetworkBehaviour : NetworkBehaviour + { + public abstract class MockMonsterBase : NetworkBehaviour + { + public abstract string GetName(); + } + + public class MockZombie : MockMonsterBase + { + public override string GetName() => "Zombie"; + } + + public class MockWolf : MockMonsterBase + { + public override string GetName() => "Wolf"; + } + + [SyncVar] + public MockMonsterBase monster1; + + [SyncVar] + public MockMonsterBase monster2; + } + + public class SyncVarAttributeTest : SyncVarAttributeTestBase + { + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // start server & connect client because we need spawn functions + NetworkServer.Listen(1); + + // we are testing server->client syncs. + // so we need truly separted server & client, not host. + ConnectClientBlockingAuthenticatedAndReady(out _); + } + + [Test] + public void TestSettingStruct() + { + CreateNetworked(out _, out _, out MockPlayer player); + + // synchronize immediately + player.syncInterval = 0f; + + Assert.That(player.IsDirty(), Is.False, "First time object should not be dirty"); + + MockPlayer.Guild myGuild = new MockPlayer.Guild + { + name = "Back street boys" + }; + + player.guild = myGuild; + + Assert.That(player.IsDirty(), "Setting struct should mark object as dirty"); + player.ClearAllDirtyBits(); + Assert.That(player.IsDirty(), Is.False, "ClearAllDirtyBits() should clear dirty flag"); + + // clearing the guild should set dirty bit too + player.guild = default; + Assert.That(player.IsDirty(), "Clearing struct should mark object as dirty"); + } + + [Test] + public void TestSyncIntervalAndClearDirtyComponents() + { + CreateNetworked(out _, out _, out MockPlayer player); + player.lastSyncTime = NetworkTime.localTime; + // synchronize immediately + player.syncInterval = 1f; + + player.guild = new MockPlayer.Guild + { + name = "Back street boys" + }; + + Assert.That(player.IsDirty(), Is.False, "Sync interval not met, so not dirty yet"); + + // ClearDirtyComponents should do nothing since syncInterval is not + // elapsed yet + player.netIdentity.ClearDirtyComponentsDirtyBits(); + + // set lastSyncTime far enough back to be ready for syncing + player.lastSyncTime = NetworkTime.localTime - player.syncInterval; + + // should be dirty now + Assert.That(player.IsDirty(), Is.True, "Sync interval met, should be dirty"); + } + + [Test] + public void TestSyncIntervalAndClearAllComponents() + { + CreateNetworked(out _, out _, out MockPlayer player); + player.lastSyncTime = NetworkTime.localTime; + // synchronize immediately + player.syncInterval = 1f; + + player.guild = new MockPlayer.Guild + { + name = "Back street boys" + }; + + Assert.That(player.IsDirty(), Is.False, "Sync interval not met, so not dirty yet"); + + // ClearAllComponents should clear dirty even if syncInterval not + // elapsed yet + player.netIdentity.ClearAllComponentsDirtyBits(); + + // set lastSyncTime far enough back to be ready for syncing + player.lastSyncTime = NetworkTime.localTime - player.syncInterval; + + // should be dirty now + Assert.That(player.IsDirty(), Is.False, "Sync interval met, should still not be dirty"); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncsGameobject(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarGameObject serverObject, + out _, out _, out SyncVarGameObject clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out GameObject serverValue, out _, + out GameObject clientValue, out _); + + serverObject.value = serverValue; + clientObject.value = null; + + bool written = SyncToClient(serverObject, clientObject, initialState); + Assert.IsTrue(written); + Assert.That(clientObject.value, Is.EqualTo(clientValue)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncIdentity(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarNetworkIdentity serverObject, + out _, out _, out SyncVarNetworkIdentity clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out NetworkIdentity serverValue, + out _, out NetworkIdentity clientValue); + + serverObject.value = serverValue; + clientObject.value = null; + + bool written = SyncToClient(serverObject, clientObject, initialState); + Assert.IsTrue(written); + Assert.That(clientObject.value, Is.EqualTo(clientValue)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncTransform(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarTransform serverObject, + out _, out _, out SyncVarTransform clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out NetworkIdentity serverIdentity, + out _, out NetworkIdentity clientIdentity); + + Transform serverValue = serverIdentity.transform; + Transform clientValue = clientIdentity.transform; + + serverObject.value = serverValue; + clientObject.value = null; + + bool written = SyncToClient(serverObject, clientObject, initialState); + Assert.IsTrue(written); + Assert.That(clientObject.value, Is.EqualTo(clientValue)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncsBehaviour(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarNetworkBehaviour serverObject, + out _, out _, out SyncVarNetworkBehaviour clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out _, out SyncVarNetworkBehaviour serverValue, + out _, out _, out SyncVarNetworkBehaviour clientValue); + + serverObject.value = serverValue; + clientObject.value = null; + + bool written = SyncToClient(serverObject, clientObject, initialState); + Assert.IsTrue(written); + Assert.That(clientObject.value, Is.EqualTo(clientValue)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncsMultipleBehaviour(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarNetworkBehaviour serverObject, + out _, out _, out SyncVarNetworkBehaviour clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out NetworkIdentity serverIdentity, out SyncVarNetworkBehaviour serverBehaviour1, out SyncVarNetworkBehaviour serverBehaviour2, + out _, out NetworkIdentity clientIdentity, out SyncVarNetworkBehaviour clientBehaviour1, out SyncVarNetworkBehaviour clientBehaviour2); + + // create array/set indices + _ = serverIdentity.NetworkBehaviours; + + int index1 = serverBehaviour1.ComponentIndex; + int index2 = serverBehaviour2.ComponentIndex; + + // check components of same type have different indexes + Assert.That(index1, Is.Not.EqualTo(index2)); + + // check behaviour 1 can be synced + serverObject.value = serverBehaviour1; + clientObject.value = null; + + bool written1 = SyncToClient(serverObject, clientObject, initialState); + Assert.IsTrue(written1); + Assert.That(clientObject.value, Is.EqualTo(clientBehaviour1)); + + // check that behaviour 2 can be synced + serverObject.value = serverBehaviour2; + clientObject.value = null; + + bool written2 = SyncToClient(serverObject, clientObject, initialState); + Assert.IsTrue(written2); + Assert.That(clientObject.value, Is.EqualTo(clientBehaviour2)); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncVarCacheNetidForGameObject(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarGameObject serverObject, + out _, out _, out SyncVarGameObject clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out GameObject serverValue, out NetworkIdentity serverIdentity, + out GameObject clientValue, out NetworkIdentity clientIdentity); + + Assert.That(serverValue, Is.Not.Null, "getCreatedValue should not return null"); + + serverObject.value = serverValue; + clientObject.value = null; + + // write server data + bool written = ServerWrite(serverObject, initialState, out ArraySegment data, out int writeLength); + Assert.IsTrue(written, "did not write"); + + // remove identity from client, as if it walked out of range + NetworkClient.spawned.Remove(clientIdentity.netId); + + // read client data, this should be cached in field + ClientRead(clientObject, initialState, data, writeLength); + + // check field shows as null + Assert.That(clientObject.value, Is.EqualTo(null), "field should return null"); + + // add identity back to collection, as if it walked back into range + NetworkClient.spawned.Add(clientIdentity.netId, clientIdentity); + + // check field finds value + Assert.That(clientObject.value, Is.EqualTo(clientValue), "fields should return clientValue"); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncVarCacheNetidForIdentity(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarNetworkIdentity serverObject, + out _, out _, out SyncVarNetworkIdentity clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out NetworkIdentity serverValue, + out _, out NetworkIdentity clientValue); + + Assert.That(serverValue, Is.Not.Null, "getCreatedValue should not return null"); + + serverObject.value = serverValue; + clientObject.value = null; + + // write server data + bool written = ServerWrite(serverObject, initialState, out ArraySegment data, out int writeLength); + Assert.IsTrue(written, "did not write"); + + // remove identity from client, as if it walked out of range + NetworkClient.spawned.Remove(clientValue.netId); + + // read client data, this should be cached in field + ClientRead(clientObject, initialState, data, writeLength); + + // check field shows as null + Assert.That(clientObject.value, Is.EqualTo(null), "field should return null"); + + // add identity back to collection, as if it walked back into range + NetworkClient.spawned.Add(clientValue.netId, clientValue); + + // check field finds value + Assert.That(clientObject.value, Is.EqualTo(clientValue), "fields should return clientValue"); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SyncVarCacheNetidForBehaviour(bool initialState) + { + CreateNetworkedAndSpawn( + out _, out _, out SyncVarNetworkBehaviour serverObject, + out _, out _, out SyncVarNetworkBehaviour clientObject); + + // create spawned because we will look up netId in .spawned + CreateNetworkedAndSpawn( + out _, out NetworkIdentity serverIdentity, out SyncVarNetworkBehaviour serverValue, + out _, out NetworkIdentity clientIdentity, out SyncVarNetworkBehaviour clientValue); + + Assert.That(serverValue, Is.Not.Null, "getCreatedValue should not return null"); + + // set on server + serverObject.value = serverValue; + clientObject.value = null; + + // write server data + bool written = ServerWrite(serverObject, initialState, out ArraySegment data, out int writeLength); + Assert.IsTrue(written, "did not write"); + + // remove identity from client, as if it walked out of range + NetworkClient.spawned.Remove(clientIdentity.netId); + + // read client data, this should be cached in field + ClientRead(clientObject, initialState, data, writeLength); + + // check field shows as null + Assert.That(clientObject.value, Is.EqualTo(null), "field should return null"); + + // add identity back to collection, as if it walked back into range + NetworkClient.spawned.Add(clientIdentity.netId, clientIdentity); + + // check field finds value + Assert.That(clientObject.value, Is.EqualTo(clientValue), "fields should return clientValue"); + } + + [Test] + public void TestSyncingAbstractNetworkBehaviour() + { + // set up a "server" object + CreateNetworked(out _, out NetworkIdentity serverIdentity, out SyncVarAbstractNetworkBehaviour serverBehaviour); + + // spawn syncvar targets + CreateNetworked(out _, out NetworkIdentity wolfIdentity, out SyncVarAbstractNetworkBehaviour.MockWolf wolf); + CreateNetworked(out _, out NetworkIdentity zombieIdentity, out SyncVarAbstractNetworkBehaviour.MockZombie zombie); + + wolfIdentity.netId = 135; + zombieIdentity.netId = 246; + + serverBehaviour.monster1 = wolf; + serverBehaviour.monster2 = zombie; + + // serialize all the data as we would for the network + NetworkWriter ownerWriter = new NetworkWriter(); + // not really used in this Test + NetworkWriter observersWriter = new NetworkWriter(); + serverIdentity.OnSerializeAllSafely(true, ownerWriter, observersWriter); + + // set up a "client" object + CreateNetworked(out _, out NetworkIdentity clientIdentity, out SyncVarAbstractNetworkBehaviour clientBehaviour); + + // apply all the data from the server object + NetworkReader reader = new NetworkReader(ownerWriter.ToArray()); + clientIdentity.OnDeserializeAllSafely(reader, true); + + // check that the syncvars got updated + Assert.That(clientBehaviour.monster1, Is.EqualTo(serverBehaviour.monster1), "Data should be synchronized"); + Assert.That(clientBehaviour.monster2, Is.EqualTo(serverBehaviour.monster2), "Data should be synchronized"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarAttributeTest.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarAttributeTest.cs.meta new file mode 100644 index 000000000..3bfd55f15 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarAttributeTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a1a87ad2c7e74dc69138ba36f583640 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncVarAttributeTestBase.cs b/Assets/Mirror/Tests/Editor/SyncVarAttributeTestBase.cs new file mode 100644 index 000000000..033b2e25f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarAttributeTestBase.cs @@ -0,0 +1,37 @@ +using System; +using NUnit.Framework; + +namespace Mirror.Tests +{ + public class SyncVarAttributeTestBase : MirrorEditModeTest + { + // returns If data was written by OnSerialize + public static bool SyncToClient(T serverObject, T clientObject, bool initialState) where T : NetworkBehaviour + { + bool written = ServerWrite(serverObject, initialState, out ArraySegment data, out int writeLength); + ClientRead(clientObject, initialState, data, writeLength); + return written; + } + + public static bool ServerWrite(T serverObject, bool initialState, out ArraySegment data, out int writeLength) where T : NetworkBehaviour + { + NetworkWriter writer = new NetworkWriter(); + bool written = serverObject.OnSerialize(writer, initialState); + writeLength = writer.Position; + data = writer.ToArraySegment(); + return written; + } + + public static void ClientRead(T clientObject, bool initialState, ArraySegment data, int writeLength) where T : NetworkBehaviour + { + NetworkReader reader = new NetworkReader(data); + clientObject.OnDeserialize(reader, initialState); + + int readLength = reader.Position; + Assert.That(writeLength == readLength, + $"OnSerializeAll and OnDeserializeAll calls write the same amount of data\n" + + $" writeLength={writeLength}\n" + + $" readLength={readLength}"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarAttributeTestBase.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarAttributeTestBase.cs.meta new file mode 100644 index 000000000..63af154ed --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarAttributeTestBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6fbd8fff4a0795d49a0b122554ed6b13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/SyncVarGameObjectTests.cs b/Assets/Mirror/Tests/Editor/SyncVarGameObjectTests.cs new file mode 100644 index 000000000..9224fbb7e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarGameObjectTests.cs @@ -0,0 +1,169 @@ +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + public class SyncVarGameObjectTests : MirrorTest + { + GameObject go; + NetworkIdentity identity; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // need a connected client & server so we can have spawned identities + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // need a spawned GameObject with a netId (we store by netId) + CreateNetworkedAndSpawn(out go, out identity); + Assert.That(identity.netId, !Is.EqualTo(0)); + } + + [TearDown] + public override void TearDown() => base.TearDown(); + + // make sure the GameObject ctor works, even though base is uint + [Test] + public void Constructor_GameObject() + { + SyncVarGameObject field = new SyncVarGameObject(go); + Assert.That(field.Value, Is.EqualTo(go)); + } + + // make sure the GameObject .Value works, even though base is uint + [Test] + public void Value_GameObject() + { + SyncVarGameObject field = new SyncVarGameObject(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = go; + Assert.That(field.Value, Is.EqualTo(go)); + } + + [Test] + public void ImplicitTo() + { + SyncVarGameObject field = new SyncVarGameObject(go); + // T = field implicit conversion should get .Value + GameObject value = field; + Assert.That(value, Is.EqualTo(go)); + } + + [Test] + public void ImplicitFrom_SetsValue() + { + // field = T implicit conversion should set .Value + SyncVarGameObject field = go; + Assert.That(field.Value, Is.EqualTo(go)); + } + + // make sure the GameObject hook works, even though base is uint. + [Test] + public void Hook() + { + int called = 0; + void OnChanged(GameObject oldValue, GameObject newValue) + { + ++called; + Assert.That(oldValue, Is.Null); + Assert.That(newValue, Is.EqualTo(go)); + } + + SyncVarGameObject field = new SyncVarGameObject(null, OnChanged); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = go; + Assert.That(called, Is.EqualTo(1)); + } + + // SyncField should check .Value for equality. + // two syncfields with same GameObject should be equal. + [Test] + public void EqualsTest() + { + SyncVarGameObject fieldA = new SyncVarGameObject(go); + SyncVarGameObject fieldB = new SyncVarGameObject(go); + SyncVarGameObject fieldC = new SyncVarGameObject(null); + Assert.That(fieldA.Equals(fieldB), Is.True); + Assert.That(fieldA.Equals(fieldC), Is.False); + } + + [Test] + public void PersistenceThroughDisappearance() + { + // field with identity + SyncVarGameObject field = new SyncVarGameObject(go); + + // remove from spawned, shouldn't be found anymore + NetworkServer.spawned.Remove(identity.netId); + Assert.That(field.Value, Is.EqualTo(null)); + + // add to spawned again + // add to spawned again, should be found again + NetworkServer.spawned[identity.netId] = identity; + Assert.That(field.Value, Is.EqualTo(go)); + } + + [Test] + public void SerializeAllWritesNetId() + { + SyncVarGameObject field = new SyncVarGameObject(go); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeAll(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(identity.netId)); + } + + [Test] + public void SerializeDeltaWritesNetId() + { + SyncVarGameObject field = new SyncVarGameObject(go); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeDelta(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(identity.netId)); + } + + [Test] + public void DeserializeAllReadsNetId() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(identity.netId); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarGameObject field = new SyncVarGameObject(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeAll(reader); + Assert.That(field.Value, Is.EqualTo(go)); + } + + [Test] + public void DeserializeDeltaReadsNetId() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(identity.netId); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarGameObject field = new SyncVarGameObject(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeDelta(reader); + Assert.That(field.Value, Is.EqualTo(go)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarGameObjectTests.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarGameObjectTests.cs.meta new file mode 100644 index 000000000..67956ffa4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarGameObjectTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e09fed54f2b64402928a287b2fd8313f +timeCreated: 1632232605 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourAbstractTests.cs b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourAbstractTests.cs new file mode 100644 index 000000000..3698796da --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourAbstractTests.cs @@ -0,0 +1,190 @@ +using NUnit.Framework; + +namespace Mirror.Tests +{ + // SyncVarNetworkBehaviour for a abstract NetworkBehaviour + public class SyncVarNetworkBehaviourAbstractTests : MirrorTest + { + NetworkIdentity identity; + NetworkBehaviour component; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // need a connected client & server so we can have spawned identities + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // need a spawned NetworkIdentity with a netId (we store by netId) + // we need a valid NetworkBehaviour component. + // the class is abstract so we need to use EmptyBehaviour and cast back + CreateNetworkedAndSpawn(out _, out identity, out EmptyBehaviour inheritedComponent); + component = inheritedComponent; + Assert.That(identity.netId, !Is.EqualTo(0)); + } + + [TearDown] + public override void TearDown() => base.TearDown(); + + [Test] + public void Pack() + { + ulong packed = SyncVarNetworkBehaviour.Pack(0xAABBCCDD, 0x12); + Assert.That(packed, Is.EqualTo(0xAABBCCDD00000012)); + } + + [Test] + public void Unpack() + { + SyncVarNetworkBehaviour.Unpack(0xAABBCCDD00000012, out uint netId, out byte componentIndex); + Assert.That(netId, Is.EqualTo(0xAABBCCDD)); + Assert.That(componentIndex, Is.EqualTo(0x12)); + } + + // make sure the NetworkBehaviour ctor works, even though base is uint + [Test] + public void Constructor_NetworkBehaviour() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + Assert.That(field.Value, Is.EqualTo(component)); + } + + // make sure the NetworkBehaviour .Value works, even though base is uint + [Test] + public void Value_NetworkBehaviour() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = component; + Assert.That(field.Value, Is.EqualTo(component)); + } + + [Test] + public void PersistenceThroughDisappearance() + { + // field with NetworkBehaviour + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + + // remove from spawned, shouldn't be found anymore + NetworkServer.spawned.Remove(identity.netId); + Assert.That(field.Value, Is.EqualTo(null)); + + // add to spawned again, should be found again + NetworkServer.spawned[identity.netId] = identity; + Assert.That(field.Value, Is.EqualTo(component)); + } + + [Test] + public void ImplicitTo() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + // T = field implicit conversion should get .Value + NetworkBehaviour value = field; + Assert.That(value, Is.EqualTo(component)); + } + + [Test] + public void ImplicitFrom_SetsValue() + { + // field = T implicit conversion should set .Value + SyncVarNetworkBehaviour field = component; + Assert.That(field.Value, Is.EqualTo(component)); + } + + // make sure the NetworkBehaviour hook works, even though base is uint. + [Test] + public void Hook() + { + int called = 0; + void OnChanged(NetworkBehaviour oldValue, NetworkBehaviour newValue) + { + ++called; + Assert.That(oldValue, Is.Null); + Assert.That(newValue, Is.EqualTo(component)); + } + + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null, OnChanged); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = component; + Assert.That(called, Is.EqualTo(1)); + } + + // SyncField should check .Value for equality. + // two syncfields with same NetworkBehaviour should be equal. + [Test] + public void EqualsTest() + { + SyncVarNetworkBehaviour fieldA = new SyncVarNetworkBehaviour(component); + SyncVarNetworkBehaviour fieldB = new SyncVarNetworkBehaviour(component); + SyncVarNetworkBehaviour fieldC = new SyncVarNetworkBehaviour(null); + Assert.That(fieldA.Equals(fieldB), Is.True); + Assert.That(fieldA.Equals(fieldC), Is.False); + } + + [Test] + public void SerializeAllWritesNetIdAndComponentIndex() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeAll(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(component.netId)); + Assert.That(reader.ReadByte(), Is.EqualTo(component.ComponentIndex)); + } + + [Test] + public void SerializeDeltaWritesNetIdAndComponentIndex() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeDelta(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(component.netId)); + Assert.That(reader.ReadByte(), Is.EqualTo(component.ComponentIndex)); + } + + [Test] + public void DeserializeAllReadsNetIdAndComponentIndex() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(component.netId); + writer.WriteByte((byte)component.ComponentIndex); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeAll(reader); + Assert.That(field.Value, Is.EqualTo(component)); + } + + [Test] + public void DeserializeDeltaReadsNetIdAndComponentIndex() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(component.netId); + writer.WriteByte((byte)component.ComponentIndex); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeDelta(reader); + Assert.That(field.Value, Is.EqualTo(component)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourAbstractTests.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourAbstractTests.cs.meta new file mode 100644 index 000000000..6f23f9237 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourAbstractTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3149b1d08c904a0092a504b4345afbd1 +timeCreated: 1633753766 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourInheritedTests.cs b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourInheritedTests.cs new file mode 100644 index 000000000..bf04a90ba --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourInheritedTests.cs @@ -0,0 +1,187 @@ +using NUnit.Framework; + +namespace Mirror.Tests +{ + // SyncVarNetworkBehaviour for a class that inherits from NetworkBehaviour + public class SyncVarNetworkBehaviourInheritedTests : MirrorTest + { + NetworkIdentity identity; + EmptyBehaviour component; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // need a connected client & server so we can have spawned identities + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // need a spawned NetworkIdentity with a netId (we store by netId) + CreateNetworkedAndSpawn(out _, out identity, out component); + Assert.That(identity.netId, !Is.EqualTo(0)); + } + + [TearDown] + public override void TearDown() => base.TearDown(); + + [Test] + public void Pack() + { + ulong packed = SyncVarNetworkBehaviour.Pack(0xAABBCCDD, 0x12); + Assert.That(packed, Is.EqualTo(0xAABBCCDD00000012)); + } + + [Test] + public void Unpack() + { + SyncVarNetworkBehaviour.Unpack(0xAABBCCDD00000012, out uint netId, out byte componentIndex); + Assert.That(netId, Is.EqualTo(0xAABBCCDD)); + Assert.That(componentIndex, Is.EqualTo(0x12)); + } + + // make sure the NetworkBehaviour ctor works, even though base is uint + [Test] + public void Constructor_NetworkBehaviour() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + Assert.That(field.Value, Is.EqualTo(component)); + } + + // make sure the NetworkBehaviour .Value works, even though base is uint + [Test] + public void Value_NetworkBehaviour() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = component; + Assert.That(field.Value, Is.EqualTo(component)); + } + + [Test] + public void PersistenceThroughDisappearance() + { + // field with NetworkBehaviour + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + + // remove from spawned, shouldn't be found anymore + NetworkServer.spawned.Remove(identity.netId); + Assert.That(field.Value, Is.EqualTo(null)); + + // add to spawned again, should be found again + NetworkServer.spawned[identity.netId] = identity; + Assert.That(field.Value, Is.EqualTo(component)); + } + + [Test] + public void ImplicitTo() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + // T = field implicit conversion should get .Value + EmptyBehaviour value = field; + Assert.That(value, Is.EqualTo(component)); + } + + [Test] + public void ImplicitFrom_SetsValue() + { + // field = T implicit conversion should set .Value + SyncVarNetworkBehaviour field = component; + Assert.That(field.Value, Is.EqualTo(component)); + } + + // make sure the NetworkBehaviour hook works, even though base is uint. + [Test] + public void Hook() + { + int called = 0; + void OnChanged(EmptyBehaviour oldValue, EmptyBehaviour newValue) + { + ++called; + Assert.That(oldValue, Is.Null); + Assert.That(newValue, Is.EqualTo(component)); + } + + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null, OnChanged); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = component; + Assert.That(called, Is.EqualTo(1)); + } + + // SyncField should check .Value for equality. + // two syncfields with same NetworkBehaviour should be equal. + [Test] + public void EqualsTest() + { + SyncVarNetworkBehaviour fieldA = new SyncVarNetworkBehaviour(component); + SyncVarNetworkBehaviour fieldB = new SyncVarNetworkBehaviour(component); + SyncVarNetworkBehaviour fieldC = new SyncVarNetworkBehaviour(null); + Assert.That(fieldA.Equals(fieldB), Is.True); + Assert.That(fieldA.Equals(fieldC), Is.False); + } + + [Test] + public void SerializeAllWritesNetIdAndComponentIndex() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeAll(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(component.netId)); + Assert.That(reader.ReadByte(), Is.EqualTo(component.ComponentIndex)); + } + + [Test] + public void SerializeDeltaWritesNetIdAndComponentIndex() + { + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(component); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeDelta(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(component.netId)); + Assert.That(reader.ReadByte(), Is.EqualTo(component.ComponentIndex)); + } + + [Test] + public void DeserializeAllReadsNetIdAndComponentIndex() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(component.netId); + writer.WriteByte((byte)component.ComponentIndex); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeAll(reader); + Assert.That(field.Value, Is.EqualTo(component)); + } + + [Test] + public void DeserializeDeltaReadsNetIdAndComponentIndex() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(component.netId); + writer.WriteByte((byte)component.ComponentIndex); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarNetworkBehaviour field = new SyncVarNetworkBehaviour(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeDelta(reader); + Assert.That(field.Value, Is.EqualTo(component)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourInheritedTests.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourInheritedTests.cs.meta new file mode 100644 index 000000000..67bbf683c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarNetworkBehaviourInheritedTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 14b1e10f8534435aa611ea329253081b +timeCreated: 1632367114 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/SyncVarNetworkIdentityTests.cs b/Assets/Mirror/Tests/Editor/SyncVarNetworkIdentityTests.cs new file mode 100644 index 000000000..4e9304083 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarNetworkIdentityTests.cs @@ -0,0 +1,166 @@ +using NUnit.Framework; + +namespace Mirror.Tests +{ + public class SyncVarNetworkIdentityTests : MirrorTest + { + NetworkIdentity identity; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // need a connected client & server so we can have spawned identities + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + + // need a spawned NetworkIdentity with a netId (we store by netId) + CreateNetworkedAndSpawn(out _, out identity); + Assert.That(identity.netId, !Is.EqualTo(0)); + } + + [TearDown] + public override void TearDown() => base.TearDown(); + + // make sure the NetworkIdentity ctor works, even though base is uint + [Test] + public void Constructor_NetworkIdentity() + { + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(identity); + Assert.That(field.Value, Is.EqualTo(identity)); + } + + // make sure the NetworkIdentity .Value works, even though base is uint + [Test] + public void Value_NetworkIdentity() + { + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = identity; + Assert.That(field.Value, Is.EqualTo(identity)); + } + + [Test] + public void PersistenceThroughDisappearance() + { + // field with identity + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(identity); + + // remove from spawned, shouldn't be found anymore + NetworkServer.spawned.Remove(identity.netId); + Assert.That(field.Value, Is.EqualTo(null)); + + // add to spawned again, should be found again + NetworkServer.spawned[identity.netId] = identity; + Assert.That(field.Value, Is.EqualTo(identity)); + } + + [Test] + public void ImplicitTo() + { + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(identity); + // T = field implicit conversion should get .Value + NetworkIdentity value = field; + Assert.That(value, Is.EqualTo(identity)); + } + + [Test] + public void ImplicitFrom_SetsValue() + { + // field = T implicit conversion should set .Value + SyncVarNetworkIdentity field = identity; + Assert.That(field.Value, Is.EqualTo(identity)); + } + + // make sure the NetworkIdentity hook works, even though base is uint. + [Test] + public void Hook() + { + int called = 0; + void OnChanged(NetworkIdentity oldValue, NetworkIdentity newValue) + { + ++called; + Assert.That(oldValue, Is.Null); + Assert.That(newValue, Is.EqualTo(identity)); + } + + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(null, OnChanged); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = identity; + Assert.That(called, Is.EqualTo(1)); + } + + // SyncField should check .Value for equality. + // two syncfields with same NetworkIdentity should be equal. + [Test] + public void EqualsTest() + { + SyncVarNetworkIdentity fieldA = new SyncVarNetworkIdentity(identity); + SyncVarNetworkIdentity fieldB = new SyncVarNetworkIdentity(identity); + SyncVarNetworkIdentity fieldC = new SyncVarNetworkIdentity(null); + Assert.That(fieldA.Equals(fieldB), Is.True); + Assert.That(fieldA.Equals(fieldC), Is.False); + } + + [Test] + public void SerializeAllWritesNetId() + { + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(identity); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeAll(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(identity.netId)); + } + + [Test] + public void SerializeDeltaWritesNetId() + { + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(identity); + NetworkWriter writer = new NetworkWriter(); + field.OnSerializeDelta(writer); + + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + Assert.That(reader.ReadUInt(), Is.EqualTo(identity.netId)); + } + + [Test] + public void DeserializeAllReadsNetId() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(identity.netId); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeAll(reader); + Assert.That(field.Value, Is.EqualTo(identity)); + } + + [Test] + public void DeserializeDeltaReadsNetId() + { + NetworkWriter writer = new NetworkWriter(); + writer.WriteUInt(identity.netId); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + SyncVarNetworkIdentity field = new SyncVarNetworkIdentity(null); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.OnDeserializeDelta(reader); + Assert.That(field.Value, Is.EqualTo(identity)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarNetworkIdentityTests.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarNetworkIdentityTests.cs.meta new file mode 100644 index 000000000..7afbf3314 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarNetworkIdentityTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 064d7f97eae04da095f6b1ec23e66c2b +timeCreated: 1632209446 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/SyncVarTests.cs b/Assets/Mirror/Tests/Editor/SyncVarTests.cs new file mode 100644 index 000000000..f8821579d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarTests.cs @@ -0,0 +1,254 @@ +using System.Text.RegularExpressions; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests +{ + public class SyncVarTests : MirrorTest + { + [SetUp] + public override void SetUp() + { + base.SetUp(); + + // SyncVar hooks are only called while client is active for now. + // so we need an active client. + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + // SyncField should recommend SyncFielGameObject instead + [Test] + public void SyncFieldGameObject_Recommendation() + { + // should show even if value is null since T is + LogAssert.Expect(LogType.Warning, new Regex($"Use explicit {nameof(SyncVarGameObject)}.*")); + SyncVar _ = new SyncVar(null); + } + + // SyncField should recommend SyncFielNetworkIdentity instead + [Test] + public void SyncFieldNetworkIdentity_Recommendation() + { + // should show even if value is null since T is + LogAssert.Expect(LogType.Warning, new Regex($"Use explicit {nameof(SyncVarNetworkIdentity)}.*")); + SyncVar _ = new SyncVar(null); + } + + // SyncField should recommend SyncFielNetworkBehaviour instead + [Test] + public void SyncFieldNetworkBehaviour_Recommendation() + { + // should show even if value is null since T is + LogAssert.Expect(LogType.Warning, new Regex($"Use explicit SyncVarNetworkBehaviour.*")); + SyncVar _ = new SyncVar(null); + } + + [Test] + public void SetValue_SetsValue() + { + // .Value is a property which does several things. + // make sure it .set actually sets the value + SyncVar field = 42; + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = 1337; + Assert.That(field.Value, Is.EqualTo(1337)); + } + + [Test] + public void SetValue_CallsOnDirty() + { + SyncVar field = 42; + int dirtyCalled = 0; + field.OnDirty = () => ++dirtyCalled; + + // setting SyncField.Value should call dirty + field.Value = 1337; + Assert.That(dirtyCalled, Is.EqualTo(1)); + } + + [Test] + public void SetValue_CallsOnDirty_OnlyIfValueChanged() + { + SyncVar field = 42; + int dirtyCalled = 0; + field.OnDirty = () => ++dirtyCalled; + + // setting same value should not call OnDirty again + field.Value = 42; + Assert.That(dirtyCalled, Is.EqualTo(0)); + } + + [Test] + public void ImplicitTo() + { + SyncVar field = new SyncVar(42); + // T = field implicit conversion should get .Value + int value = field; + Assert.That(value, Is.EqualTo(42)); + } + + [Test] + public void ImplicitFrom_SetsValue() + { + // field = T implicit conversion should set .Value + SyncVar field = 42; + Assert.That(field.Value, Is.EqualTo(42)); + } + + [Test] + public void Hook_IsCalled() + { + int called = 0; + void OnChanged(int oldValue, int newValue) + { + ++called; + Assert.That(oldValue, Is.EqualTo(42)); + Assert.That(newValue, Is.EqualTo(1337)); + } + + SyncVar field = new SyncVar(42, OnChanged); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + field.Value = 1337; + Assert.That(called, Is.EqualTo(1)); + } + + [Test] + public void Hook_OnlyCalledIfValueChanged() + { + int called = 0; + void OnChanged(int oldValue, int newValue) + { + ++called; + Assert.That(oldValue, Is.EqualTo(42)); + Assert.That(newValue, Is.EqualTo(1337)); + } + + SyncVar field = new SyncVar(42, OnChanged); + // assign same value again. hook shouldn't be called again. + field.Value = 42; + Assert.That(called, Is.EqualTo(0)); + } + + [Test] + public void Hook_Set_DoesntDeadlock() + { + // Value.set calls the hook. + // calling Value.set inside the hook would deadlock. + // this needs to be prevented. + SyncVar field = null; + int called = 0; + void OnChanged(int oldValue, int newValue) + { + // setting a different value calls setter -> hook again + field.Value = 0; + ++called; + } + field = new SyncVar(42, OnChanged); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + // setting a different value will call the hook + field.Value = 1337; + // in the end, hook should've been called exactly once + Assert.That(called, Is.EqualTo(1)); + } + + [Test] + public void DeserializeAll_CallsHook() + { + // create field with hook + int called = 0; + void OnChanged(int oldValue, int newValue) + { + ++called; + Assert.That(oldValue, Is.EqualTo(42)); + Assert.That(newValue, Is.EqualTo(1337)); + } + SyncVar field = new SyncVar(42, OnChanged); + + // avoid 'not initialized' exception + field.OnDirty = () => {}; + + // create reader with data + NetworkWriter writer = new NetworkWriter(); + writer.WriteInt(1337); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + // deserialize + field.OnDeserializeAll(reader); + Assert.That(called, Is.EqualTo(1)); + } + + [Test] + public void DeserializeDelta_CallsHook() + { + // create field with hook + int called = 0; + void OnChanged(int oldValue, int newValue) + { + ++called; + Assert.That(oldValue, Is.EqualTo(42)); + Assert.That(newValue, Is.EqualTo(1337)); + } + SyncVar fieldWithHook = new SyncVar(42, OnChanged); + + // avoid 'not initialized' exception + fieldWithHook.OnDirty = () => {}; + + // create reader with data + NetworkWriter writer = new NetworkWriter(); + writer.WriteInt(1337); + NetworkReader reader = new NetworkReader(writer.ToArraySegment()); + + // deserialize + fieldWithHook.OnDeserializeDelta(reader); + Assert.That(called, Is.EqualTo(1)); + } + + [Test] + public void EqualsT() + { + // .Equals should compare .Value + SyncVar field = 42; + Assert.That(field.Equals(42), Is.True); + } + + [Test] + public void EqualsNull() + { + // .Equals(null) should always be false. so that == null works. + SyncVar field = 42; + Assert.That(field.Equals(null), Is.False); + } + + [Test] + public void EqualsEqualsT() + { + // == should compare .Value + SyncVar field = 42; + Assert.That(field == 42, Is.True); + } + + [Test] + public void ToString_CallsValueToString() + { + SyncVar field = 42; + Assert.That(field.ToString(), Is.EqualTo("42")); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/SyncVarTests.cs.meta b/Assets/Mirror/Tests/Editor/SyncVarTests.cs.meta new file mode 100644 index 000000000..078a619e5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/SyncVarTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e3e4fde08fe240f79ad665b57d2e71c2 +timeCreated: 1632056463 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/TargetRpcOverrideTest.cs b/Assets/Mirror/Tests/Editor/TargetRpcOverrideTest.cs new file mode 100644 index 000000000..f4e565acf --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TargetRpcOverrideTest.cs @@ -0,0 +1,138 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests.RemoteAttrributeTest +{ + class VirtualTargetRpc : NetworkBehaviour + { + public event Action onVirtualSendInt; + + [TargetRpc] + public virtual void TargetSendInt(int someInt) + { + onVirtualSendInt?.Invoke(someInt); + } + } + + class VirtualNoOverrideTargetRpc : VirtualTargetRpc {} + + class VirtualOverrideTargetRpc : VirtualTargetRpc + { + public event Action onOverrideSendInt; + + [TargetRpc] + public override void TargetSendInt(int someInt) + { + onOverrideSendInt?.Invoke(someInt); + } + } + + class VirtualOverrideTargetRpcWithBase : VirtualTargetRpc + { + public event Action onOverrideSendInt; + + [TargetRpc] + public override void TargetSendInt(int someInt) + { + base.TargetSendInt(someInt); + onOverrideSendInt?.Invoke(someInt); + } + } + + public class TargetRpcOverrideTest : RemoteTestBase + { + [Test] + public void VirtualRpcIsCalled() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualTargetRpc hostBehaviour, NetworkServer.localConnection); + + const int someInt = 20; + + int virtualCallCount = 0; + hostBehaviour.onVirtualSendInt += incomingInt => + { + virtualCallCount++; + Assert.That(incomingInt, Is.EqualTo(someInt)); + }; + + hostBehaviour.TargetSendInt(someInt); + ProcessMessages(); + Assert.That(virtualCallCount, Is.EqualTo(1)); + } + + [Test] + public void VirtualCommandWithNoOverrideIsCalled() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualNoOverrideTargetRpc hostBehaviour, NetworkServer.localConnection); + + const int someInt = 20; + + int virtualCallCount = 0; + hostBehaviour.onVirtualSendInt += incomingInt => + { + virtualCallCount++; + Assert.That(incomingInt, Is.EqualTo(someInt)); + }; + + hostBehaviour.TargetSendInt(someInt); + ProcessMessages(); + Assert.That(virtualCallCount, Is.EqualTo(1)); + } + + [Test] + public void OverrideVirtualRpcIsCalled() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out VirtualOverrideTargetRpc 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.TargetSendInt(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 VirtualOverrideTargetRpcWithBase 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.TargetSendInt(someInt); + ProcessMessages(); + Assert.That(virtualCallCount, Is.EqualTo(1)); + Assert.That(overrideCallCount, Is.EqualTo(1)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/TargetRpcOverrideTest.cs.meta b/Assets/Mirror/Tests/Editor/TargetRpcOverrideTest.cs.meta new file mode 100644 index 000000000..8cb1e07da --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TargetRpcOverrideTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56e76b5f8b5fe2d40ade963d179ef76e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/TargetRpcTest.cs b/Assets/Mirror/Tests/Editor/TargetRpcTest.cs new file mode 100644 index 000000000..b85fc9858 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TargetRpcTest.cs @@ -0,0 +1,181 @@ +using System; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests.RemoteAttrributeTest +{ + class TargetRpcBehaviour : NetworkBehaviour + { + public event Action onSendInt; + + [TargetRpc] + public void SendInt(int someInt) + { + onSendInt?.Invoke(someInt); + } + + [TargetRpc] + public void SendIntWithTarget(NetworkConnection target, int someInt) + { + onSendInt?.Invoke(someInt); + } + } + + class TargetRpcOverloads : NetworkBehaviour + { + public int firstCalled = 0; + public int secondCalled = 0; + + [TargetRpc] + public void TargetRpcTest(int _) => ++firstCalled; + + [TargetRpc] + public void TargetRpcTest(string _) => ++secondCalled; + } + + public class TargetRpcTest : RemoteTestBase + { + [Test] + public void TargetRpcIsCalled() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out TargetRpcBehaviour 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 TargetRpcIsCalledOnTarget() + { + // spawn without owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out TargetRpcBehaviour hostBehaviour); + + const int someInt = 20; + + int callCount = 0; + hostBehaviour.onSendInt += incomingInt => + { + callCount++; + Assert.That(incomingInt, Is.EqualTo(someInt)); + }; + hostBehaviour.SendIntWithTarget(NetworkServer.localConnection, someInt); + ProcessMessages(); + Assert.That(callCount, Is.EqualTo(1)); + } + + [Test] + public void ErrorForTargetRpcWithNoOwner() + { + // spawn without owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out TargetRpcBehaviour hostBehaviour); + + const int someInt = 20; + + hostBehaviour.onSendInt += incomingInt => + { + Assert.Fail("Event should not be invoked with error"); + }; + LogAssert.Expect(LogType.Error, $"TargetRPC System.Void Mirror.Tests.RemoteAttrributeTest.TargetRpcBehaviour::SendInt(System.Int32) was given a null connection, make sure the object has an owner or you pass in the target connection"); + hostBehaviour.SendInt(someInt); + } + + [Test] + public void ErrorForTargetRpcWithNullArgment() + { + // spawn without owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out TargetRpcBehaviour hostBehaviour); + + const int someInt = 20; + + hostBehaviour.onSendInt += incomingInt => + { + Assert.Fail("Event should not be invoked with error"); + }; + LogAssert.Expect(LogType.Error, $"TargetRPC System.Void Mirror.Tests.RemoteAttrributeTest.TargetRpcBehaviour::SendIntWithTarget(Mirror.NetworkConnection,System.Int32) was given a null connection, make sure the object has an owner or you pass in the target connection"); + hostBehaviour.SendIntWithTarget(null, someInt); + } + + [Test] + public void ErrorForTargetRpcWhenNotGivenConnectionToClient() + { + // spawn without owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out TargetRpcBehaviour hostBehaviour); + + const int someInt = 20; + + hostBehaviour.onSendInt += incomingInt => + { + Assert.Fail("Event should not be invoked with error"); + }; + LogAssert.Expect(LogType.Error, $"TargetRPC System.Void Mirror.Tests.RemoteAttrributeTest.TargetRpcBehaviour::SendIntWithTarget(Mirror.NetworkConnection,System.Int32) requires a NetworkConnectionToClient but was given {typeof(FakeConnection).Name}"); + hostBehaviour.SendIntWithTarget(new FakeConnection(), someInt); + } + class FakeConnection : NetworkConnection + { + public override string address => throw new NotImplementedException(); + public override void Disconnect() => throw new NotImplementedException(); + internal override void Send(ArraySegment segment, int channelId = 0) => throw new NotImplementedException(); + protected override void SendToTransport(ArraySegment segment, int channelId = Channels.Reliable) => throw new NotImplementedException(); + } + + [Test] + public void ErrorForTargetRpcWhenServerNotActive() + { + // spawn without owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out TargetRpcBehaviour hostBehaviour); + + const int someInt = 20; + + hostBehaviour.onSendInt += incomingInt => + { + Assert.Fail("Event should not be invoked with error"); + }; + NetworkServer.active = false; + LogAssert.Expect(LogType.Error, $"TargetRPC System.Void Mirror.Tests.RemoteAttrributeTest.TargetRpcBehaviour::SendInt(System.Int32) called when server not active"); + hostBehaviour.SendInt(someInt); + } + + [Test] + public void ErrorForTargetRpcWhenObjectNotSpawned() + { + // create without spawning + CreateNetworked(out GameObject _, out NetworkIdentity _, out TargetRpcBehaviour hostBehaviour); + + const int someInt = 20; + + hostBehaviour.onSendInt += incomingInt => + { + Assert.Fail("Event should not be invoked with error"); + }; + LogAssert.Expect(LogType.Warning, $"TargetRpc System.Void Mirror.Tests.RemoteAttrributeTest.TargetRpcBehaviour::SendInt(System.Int32) called on {hostBehaviour.name} but that object has not been spawned or has been unspawned"); + hostBehaviour.SendInt(someInt); + } + + // 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 TargetRpcOverload() + { + // spawn with owner + CreateNetworkedAndSpawn(out GameObject _, out NetworkIdentity _, out TargetRpcOverloads hostBehaviour, NetworkServer.localConnection); + + hostBehaviour.TargetRpcTest(42); + hostBehaviour.TargetRpcTest("A"); + ProcessMessages(); + Assert.That(hostBehaviour.firstCalled, Is.EqualTo(1)); + Assert.That(hostBehaviour.secondCalled, Is.EqualTo(1)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/TargetRpcTest.cs.meta b/Assets/Mirror/Tests/Editor/TargetRpcTest.cs.meta new file mode 100644 index 000000000..554ebb23c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TargetRpcTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53f71b8db0a8bac448b45ee86a0d355a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs.meta b/Assets/Mirror/Tests/Editor/TestPrefabs.meta new file mode 100644 index 000000000..b181ac967 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TestPrefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c5e7ba67b40c16049a463d65940445b7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab new file mode 100644 index 000000000..b445e7389 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab @@ -0,0 +1,97 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3117426950187087154 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 51440471024079650} + - component: {fileID: 5409067437793000088} + m_Layer: 0 + m_Name: PrefabWithChildrenForClientScene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &51440471024079650 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3117426950187087154} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6537489145038351880} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &5409067437793000088 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3117426950187087154} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 4227710719 + serverOnly: 0 + m_AssetId: + hasSpawned: 0 +--- !u!1 &3264653828050749140 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6537489145038351880} + - component: {fileID: 6548650892574975460} + m_Layer: 0 + m_Name: Child Network Identity + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6537489145038351880 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3264653828050749140} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 51440471024079650} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &6548650892574975460 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3264653828050749140} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 1033643234 + serverOnly: 0 + m_AssetId: + hasSpawned: 0 diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab.meta b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab.meta new file mode 100644 index 000000000..d6d295014 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a78e009e3f2dee44e8859516974ede43 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab new file mode 100644 index 000000000..38f91abdd --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab @@ -0,0 +1,49 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &9072865897540379920 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1476000898204696246} + - component: {fileID: 1423274632536207763} + m_Layer: 0 + m_Name: ValidPrefabForClientScene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1476000898204696246 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9072865897540379920} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1423274632536207763 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9072865897540379920} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 1350197626 + serverOnly: 0 + m_AssetId: + hasSpawned: 0 diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab.meta b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab.meta new file mode 100644 index 000000000..4f06339ab --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 33169286da0313d45ab5bfccc6cf3775 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/invalidPrefabForClientScene.prefab b/Assets/Mirror/Tests/Editor/TestPrefabs/invalidPrefabForClientScene.prefab new file mode 100644 index 000000000..7dea950b9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TestPrefabs/invalidPrefabForClientScene.prefab @@ -0,0 +1,32 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6662908427314056913 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4633208118073782447} + m_Layer: 0 + m_Name: invalidPrefabForClientScene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4633208118073782447 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6662908427314056913} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/invalidPrefabForClientScene.prefab.meta b/Assets/Mirror/Tests/Editor/TestPrefabs/invalidPrefabForClientScene.prefab.meta new file mode 100644 index 000000000..edf3b10fd --- /dev/null +++ b/Assets/Mirror/Tests/Editor/TestPrefabs/invalidPrefabForClientScene.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 78f0a3f755d35324e959f3ecdd993fb0 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/UtilsTests.cs b/Assets/Mirror/Tests/Editor/UtilsTests.cs new file mode 100644 index 000000000..cda272b2f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/UtilsTests.cs @@ -0,0 +1,31 @@ +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + public class UtilsTests + { + [Test] + public void GetTrueRandomUInt() + { + uint first = Utils.GetTrueRandomUInt(); + uint second = Utils.GetTrueRandomUInt(); + Assert.That(first, !Is.EqualTo(second)); + } + + [Test] + public void IsPointInScreen() + { + int width = Screen.width; + int height = Screen.height; + Assert.That(Utils.IsPointInScreen(new Vector2(-1, -1)), Is.False); + + Assert.That(Utils.IsPointInScreen(new Vector2(0, 0)), Is.True); + Assert.That(Utils.IsPointInScreen(new Vector2(width / 2, height / 2)), Is.True); + + Assert.That(Utils.IsPointInScreen(new Vector2(width, height / 2)), Is.False); + Assert.That(Utils.IsPointInScreen(new Vector2(width / 2, height)), Is.False); + Assert.That(Utils.IsPointInScreen(new Vector2(width + 1, height + 1)), Is.False); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/UtilsTests.cs.meta b/Assets/Mirror/Tests/Editor/UtilsTests.cs.meta new file mode 100644 index 000000000..fef4a5ef1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/UtilsTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4334d1acdbce04172b3faaa632129ba7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver.meta b/Assets/Mirror/Tests/Editor/Weaver.meta new file mode 100644 index 000000000..700c49534 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 90415d9cedbb1f14795854e529289171 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj b/Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj new file mode 100644 index 000000000..5fc5f018b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj @@ -0,0 +1,280 @@ + + + netstandard2.0 + _WeaverTests2.csproj + False + Temp~\obj\ + + + true + full + false + Temp~\bin\Debug\ + DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_3_6;UNITY_2018_3;UNITY_2018;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITES;ENABLE_GRID;ENABLE_TILEMAP;ENABLE_TERRAIN;ENABLE_TEXTURE_STREAMING;ENABLE_DIRECTOR;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_TIMELINE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;INCLUDE_DYNAMIC_GI;INCLUDE_GI;ENABLE_MONO_BDWGC;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_VIDEO;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_LOCALIZATION;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_STANDARD_2_0;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;ENABLE_VSTU;MIRROR;MIRROR_1726_OR_NEWER;MIRROR_3_0_OR_NEWER;MIRROR_3_12_OR_NEWER;MIRROR_4_0_OR_NEWER;MIRROR_5_0_OR_NEWER;MIRROR_6_0_OR_NEWER;MIRROR_7_0_OR_NEWER;MIRROR_8_0_OR_NEWER;MIRROR_9_0_OR_NEWER;MIRROR_10_0_OR_NEWER;MIRROR_11_0_OR_NEWER;MIRROR_12_0_OR_NEWER;MIRROR_13_0_OR_NEWER;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER + prompt + 4 + 0169 + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + G:/UnityEditors/2018.3.6f1/Editor/Data/Managed/UnityEngine/UnityEngine.CoreModule.dll + + + + + G:\UnityEditors\2018.3.6f1\Editor\Data\Managed/UnityEngine/UnityEngine.dll + + + G:\UnityEditors\2018.3.6f1\Editor\Data\Managed/UnityEditor.dll + + + G:\UnityEditors\2018.3.6f1\Editor\Data\Managed\UnityEngine\UnityEngine.TextRenderingModule.dll + + + + + {3AD0CCDA-2B85-9DF3-8D00-95C1C6C53989} + Mirror + + + {90A15647-DD13-BF94-74EC-7CAA0D39EE86} + WeaverTestExtraAssembly + + + + diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly.meta new file mode 100644 index 000000000..0938ea191 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c7ea1d20d9a008f479d65016372c9af9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/ComplexClass.cs b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/ComplexClass.cs new file mode 100644 index 000000000..57669332d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/ComplexClass.cs @@ -0,0 +1,14 @@ +namespace Mirror.Weaver.Tests.Extra +{ + public struct ComplexData + { + public AnotherData another; + public float q; + } + public struct AnotherData + { + public float a; + public float b; + public float c; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/ComplexClass.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/ComplexClass.cs.meta new file mode 100644 index 000000000..d388cb7be --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/ComplexClass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 48d47c0cf65be8d438e25f7039e50855 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/README.md b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/README.md new file mode 100644 index 000000000..b00c220c4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/README.md @@ -0,0 +1,3 @@ +We need this extra Assembly when testing multiple assembles. + +Weaver is currently unable to use reference to assembles that are not in `CompilationPipeline.GetAssemblies()` \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/README.md.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/README.md.meta new file mode 100644 index 000000000..01a20a1e2 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4020f17b5b2fe4842bea9d1e7e6102d8 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeData.cs b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeData.cs new file mode 100644 index 000000000..bc8615589 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeData.cs @@ -0,0 +1,7 @@ +namespace Mirror.Weaver.Tests.Extra +{ + public struct SomeData + { + public int usefulNumber; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeData.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeData.cs.meta new file mode 100644 index 000000000..3e39abd3e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 502dcd3fe9ec0e04185f2afd167c7aad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClass.cs b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClass.cs new file mode 100644 index 000000000..b93368de5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClass.cs @@ -0,0 +1,7 @@ +namespace Mirror.Weaver.Tests.Extra +{ + public class SomeDataClass + { + public int usefulNumber; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClass.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClass.cs.meta new file mode 100644 index 000000000..cf2961c97 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 13374a84968c7684782a54136703257d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClassWithConstructor.cs b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClassWithConstructor.cs new file mode 100644 index 000000000..b1fd9316a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClassWithConstructor.cs @@ -0,0 +1,12 @@ +namespace Mirror.Weaver.Tests.Extra +{ + public class SomeDataClassWithConstructor + { + public int usefulNumber; + + public SomeDataClassWithConstructor() + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClassWithConstructor.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClassWithConstructor.cs.meta new file mode 100644 index 000000000..a84b180df --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataClassWithConstructor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1e73b5c53b6d21b47ab1442e5ad2265a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataWithWriter.cs b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataWithWriter.cs new file mode 100644 index 000000000..d229527a8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataWithWriter.cs @@ -0,0 +1,19 @@ +namespace Mirror.Weaver.Tests.Extra +{ + public struct SomeDataWithWriter + { + public int usefulNumber; + } + + public static class ReadWrite + { + public static void WriteSomeData(this NetworkWriter writer, SomeDataWithWriter itemData) + { + writer.WriteInt(itemData.usefulNumber); + } + public static SomeDataWithWriter ReadSomeData(this NetworkReader reader) + { + return new SomeDataWithWriter { usefulNumber = reader.ReadInt() }; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataWithWriter.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataWithWriter.cs.meta new file mode 100644 index 000000000..48e7de714 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/SomeDataWithWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15ed55793bf39a04f9ef337575c3c6e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/WeaverTestExtraAssembly.asmdef b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/WeaverTestExtraAssembly.asmdef new file mode 100644 index 000000000..7098b96e8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/WeaverTestExtraAssembly.asmdef @@ -0,0 +1,16 @@ +{ + "name": "WeaverTestExtraAssembly", + "references": [ + "Mirror" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/WeaverTestExtraAssembly.asmdef.meta b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/WeaverTestExtraAssembly.asmdef.meta new file mode 100644 index 000000000..65c2e4b43 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/ExtraAssembly/WeaverTestExtraAssembly.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4887344a35d890449b35f8b1b0a3355a +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/README.md b/Assets/Mirror/Tests/Editor/Weaver/README.md new file mode 100644 index 000000000..9737cfa05 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/README.md @@ -0,0 +1,15 @@ +There are two types of Weaver tests: +* Success tests where we simply have to guarantee that a class is + weaved without issues. +* Failure tests where we need to make sure certain classes are not + weaved because they aren't allowed to. + +The success tests can be regular C# files. +=> Weaver runs automatically when creating them, so we don't even + need to weave those manually with AssemblyBuilder. +=> There are >100 of those tests. moving them to regular C# + removes a LOT of AssemblyBuilder time. + +The failure tests need to be weaved one at a time. +=> Weaver usually stops weaving after the first error. +=> So we weave them all separately to get all the errors. diff --git a/Assets/Mirror/Tests/Editor/Weaver/README.md.meta b/Assets/Mirror/Tests/Editor/Weaver/README.md.meta new file mode 100644 index 000000000..cfa2c7657 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/README.md.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fe0ea9aa683941dc832caafa91f6a72d +timeCreated: 1643081104 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverAssembler.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverAssembler.cs new file mode 100644 index 000000000..1f4618a2f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverAssembler.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using UnityEditor.Compilation; +using UnityEngine; + +namespace Mirror.Weaver.Tests +{ + public class WeaverAssembler : MonoBehaviour + { + static string _outputDirectory; + public static string OutputDirectory + { + get + { + if (string.IsNullOrEmpty(_outputDirectory)) + { + _outputDirectory = EditorHelper.FindPath(); + } + return _outputDirectory; + } + } + public static string OutputFile; + public static HashSet SourceFiles { get; private set; } + public static bool AllowUnsafe; + public static List CompilerMessages { get; private set; } + public static bool CompilerErrors { get; private set; } + public static bool DeleteOutputOnClear; + + // static constructor to initialize static properties + static WeaverAssembler() + { + SourceFiles = new HashSet(); + CompilerMessages = new List(); + } + + // Add a range of source files to compile + public static void AddSourceFiles(string[] sourceFiles) + { + foreach (string src in sourceFiles) + { + SourceFiles.Add(Path.Combine(OutputDirectory, src)); + } + } + + // Delete output dll / pdb / mdb + public static void DeleteOutput() + { + // "x.dll" shortest possible dll name + if (OutputFile.Length < 5) + { + return; + } + + string projPathFile = Path.Combine(OutputDirectory, OutputFile); + + try + { + File.Delete(projPathFile); + } + catch {} + + try + { + File.Delete(Path.ChangeExtension(projPathFile, ".pdb")); + } + catch {} + + try + { + File.Delete(Path.ChangeExtension(projPathFile, ".dll.mdb")); + } + catch {} + } + + // clear all settings except for referenced assemblies (which are cleared with ClearReferences) + public static void Clear() + { + if (DeleteOutputOnClear) + { + DeleteOutput(); + } + + CompilerErrors = false; + OutputFile = ""; + SourceFiles.Clear(); + CompilerMessages.Clear(); + AllowUnsafe = false; + DeleteOutputOnClear = false; + } + + public static void Build(Action OnWarning, Action OnError) + { + AssemblyBuilder assemblyBuilder = new AssemblyBuilder(Path.Combine(OutputDirectory, OutputFile), SourceFiles.ToArray()) + { + // "The type 'MonoBehaviour' is defined in an assembly that is not referenced" + referencesOptions = ReferencesOptions.UseEngineModules + }; + if (AllowUnsafe) + { + assemblyBuilder.compilerOptions.AllowUnsafeCode = true; + } + +#if UNITY_2020_3_OR_NEWER + // Unity automatically invokes ILPostProcessor after + // AssemblyBuilder.Build() (on windows at least. not on mac). + // => .buildFinished() below CompilerMessages would already contain + // the weaver messages, failing tests. + // => SyncVarTests->SyncVarSyncList fails too if ILPP was + // already applied by Unity, and we apply it again. + // + // we need to not run ILPP for WeaverTests assemblies here. + // -> we can't set member variables because Unity creates a new + // ILPP instance internally and invokes it + // -> define is passed through ILPP though, and avoids static state. + assemblyBuilder.additionalDefines = new []{ILPostProcessorHook.IgnoreDefine}; +#endif + + assemblyBuilder.buildFinished += delegate (string assemblyPath, CompilerMessage[] compilerMessages) + { + // CompilerMessages from compiling the original test assembly. + // note that we can see weaver messages here if Unity runs + // ILPostProcessor after AssemblyBuilder.Build(). + // => that's why we pass the ignore define above. + CompilerMessages.AddRange(compilerMessages); + foreach (CompilerMessage cm in compilerMessages) + { + if (cm.type == CompilerMessageType.Error) + { + Debug.LogError($"{cm.file}:{cm.line} -- {cm.message}"); + CompilerErrors = true; + } + } + +#if UNITY_2020_3_OR_NEWER + // on 2018/2019, CompilationFinishedHook weaves after building. + // on 2020, ILPostProcessor weaves after building. + // on windows, it runs after AssemblyBuilder.Build() + // on mac, it does not run after AssemblyBuidler.Build() + // => run it manually in all cases + // => this way we can feed result.Logs to test results too + // NOTE: we could simply call Weaver.Weave() here. + // but let's make all tests run through ILPP. + // just like regular projects would. + // helps catch issues early. + + // copy references from assemblyBuilder's references + List references = new List(); + if (assemblyBuilder.defaultReferences != null) + references.AddRange(assemblyBuilder.defaultReferences); + if (assemblyBuilder.additionalReferences != null) + references.AddRange(assemblyBuilder.additionalReferences); + + // invoke ILPostProcessor with an assembly from file. + // NOTE: code for creating and invoking the ILPostProcessor has + // to be in Weaver.dll where 'CompilationPipeline' is + // available due to name being of form 'Unity.*.CodeGen'. + // => we can't change tests to that Unity.*.CodeGen + // because some tests need to be weaved, but ILPP isn't + // ran on Unity.*.CodeGen assemblies itself. + ILPostProcessorFromFile.ILPostProcessFile(assemblyPath, references.ToArray(), OnWarning, OnError); +#endif + }; + + // Start build of assembly + if (!assemblyBuilder.Build()) + { + Debug.LogError($"Failed to start build of assembly {assemblyBuilder.assemblyPath}"); + return; + } + + while (assemblyBuilder.status != AssemblyBuilderStatus.Finished) + { + Thread.Sleep(10); + } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverAssembler.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverAssembler.cs.meta new file mode 100644 index 000000000..d41b1b4f2 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverAssembler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 049ac5abfba3c0943a2694cd502fcc80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests.cs new file mode 100644 index 000000000..7d59264ea --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests.cs @@ -0,0 +1,28 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverClientRpcTests : WeaverTestsBuildFromTestName + { + [Test] + public void ClientRpcCantBeStatic() + { + HasError("RpcCantBeStatic must not be static", + "System.Void WeaverClientRpcTests.ClientRpcCantBeStatic.ClientRpcCantBeStatic::RpcCantBeStatic()"); + } + + [Test] + public void AbstractClientRpc() + { + HasError("Abstract ClientRpc are currently not supported, use virtual method instead", + "System.Void WeaverClientRpcTests.AbstractClientRpc.AbstractClientRpc::RpcDoSomething()"); + } + + [Test] + public void OverrideAbstractClientRpc() + { + HasError("Abstract ClientRpc are currently not supported, use virtual method instead", + "System.Void WeaverClientRpcTests.OverrideAbstractClientRpc.BaseBehaviour::RpcDoSomething()"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests.cs.meta new file mode 100644 index 000000000..4af734600 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1707821d15f4c5e4eb0446b0d2e4a260 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess.meta new file mode 100644 index 000000000..af15d896b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4f961f3f925c41f295d7ef17978001fe +timeCreated: 1643081346 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/BehaviourCanBeSentInRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/BehaviourCanBeSentInRpc.cs new file mode 100644 index 000000000..c20bc69b5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/BehaviourCanBeSentInRpc.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverClientRpcTests.BehaviourCanBeSentInRpc +{ + class BehaviourCanBeSentInRpc : NetworkBehaviour + { + [ClientRpc] + void RpcDoSomething(BehaviourCanBeSentInRpc value) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/BehaviourCanBeSentInRpc.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/BehaviourCanBeSentInRpc.cs.meta new file mode 100644 index 000000000..86a338ed1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/BehaviourCanBeSentInRpc.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c93dd180605f644c2b24cde840095dd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcThatExcludesOwner.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcThatExcludesOwner.cs new file mode 100644 index 000000000..77706bab4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcThatExcludesOwner.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverClientRpcTests.ClientRpcThatExcludesOwner +{ + class ClientRpcThatExcludesOwner : NetworkBehaviour + { + [ClientRpc(includeOwner = false)] + void RpcDoSomething() + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcThatExcludesOwner.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcThatExcludesOwner.cs.meta new file mode 100644 index 000000000..3b237b58c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcThatExcludesOwner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d1e7f164257c4ce49acc45f27c9e16e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcValid.cs new file mode 100644 index 000000000..123d19f1d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcValid.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientRpcTests.ClientRpcValid +{ + class ClientRpcValid : NetworkBehaviour + { + [ClientRpc] + void RpcThatIsTotallyValid() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcValid.cs.meta new file mode 100644 index 000000000..58ad82d7e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/ClientRpcValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1b6e0531a6ce04c1e98316b51b1b4fda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/OverrideVirtualClientRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/OverrideVirtualClientRpc.cs new file mode 100644 index 000000000..ea8742b6d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/OverrideVirtualClientRpc.cs @@ -0,0 +1,23 @@ +using Mirror; + + +namespace WeaverClientRpcTests.OverrideVirtualClientRpc +{ + class OverrideVirtualClientRpc : baseBehaviour + { + [ClientRpc] + protected override void RpcDoSomething() + { + // do something + } + } + + class baseBehaviour : NetworkBehaviour + { + [ClientRpc] + protected virtual void RpcDoSomething() + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/OverrideVirtualClientRpc.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/OverrideVirtualClientRpc.cs.meta new file mode 100644 index 000000000..1ed894435 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/OverrideVirtualClientRpc.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b960c909aead4954a70aebf0427f2d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/VirtualClientRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/VirtualClientRpc.cs new file mode 100644 index 000000000..089f5e0af --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/VirtualClientRpc.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverClientRpcTests.VirtualClientRpc +{ + class VirtualCommand : NetworkBehaviour + { + [ClientRpc] + protected virtual void RpcDoSomething() + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/VirtualClientRpc.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/VirtualClientRpc.cs.meta new file mode 100644 index 000000000..1fcc4a797 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests_IsSuccess/VirtualClientRpc.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2bed05ae46099420281703e1956ec658 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/AbstractClientRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/AbstractClientRpc.cs new file mode 100644 index 000000000..ca91cc764 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/AbstractClientRpc.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientRpcTests.AbstractClientRpc +{ + abstract class AbstractClientRpc : NetworkBehaviour + { + [ClientRpc] + protected abstract void RpcDoSomething(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/ClientRpcCantBeStatic.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/ClientRpcCantBeStatic.cs new file mode 100644 index 000000000..49aa61fd2 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/ClientRpcCantBeStatic.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientRpcTests.ClientRpcCantBeStatic +{ + class ClientRpcCantBeStatic : NetworkBehaviour + { + [ClientRpc] + static void RpcCantBeStatic() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/OverrideAbstractClientRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/OverrideAbstractClientRpc.cs new file mode 100644 index 000000000..4310277d8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientRpcTests~/OverrideAbstractClientRpc.cs @@ -0,0 +1,20 @@ +using Mirror; + + +namespace WeaverClientRpcTests.OverrideAbstractClientRpc +{ + class OverrideAbstractClientRpc : BaseBehaviour + { + [ClientRpc] + protected override void RpcDoSomething() + { + // do something + } + } + + abstract class BaseBehaviour : NetworkBehaviour + { + [ClientRpc] + protected abstract void RpcDoSomething(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests.cs new file mode 100644 index 000000000..7b318164d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests.cs @@ -0,0 +1,130 @@ +using System.IO; +using System.Linq; +using Mono.CecilX; +using Mono.CecilX.Cil; +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverClientServerAttributeTests : WeaverTestsBuildFromTestName + { + // Debug.Log on WeaverTypes to see the strings + const string NetworkServerGetActive = "System.Boolean Mirror.NetworkServer::get_active()"; + const string NetworkClientGetActive = "System.Boolean Mirror.NetworkClient::get_active()"; + + [Test] + public void NetworkBehaviourServer() + { + IsSuccess(); + CheckAddedCode(NetworkServerGetActive, "WeaverClientServerAttributeTests.NetworkBehaviourServer.NetworkBehaviourServer", "ServerOnlyMethod"); + } + + [Test] + public void ServerAttributeOnVirutalMethod() + { + IsSuccess(); + CheckAddedCode(NetworkServerGetActive, "WeaverClientServerAttributeTests.ServerAttributeOnVirutalMethod.ServerAttributeOnVirutalMethod", "ServerOnlyMethod"); + } + + [Test] + public void ServerAttributeOnAbstractMethod() + { + HasError("Server or Client Attributes can't be added to abstract method. Server and Client Attributes are not inherited so they need to be applied to the override methods instead.", + "System.Void WeaverClientServerAttributeTests.ServerAttributeOnAbstractMethod.ServerAttributeOnAbstractMethod::ServerOnlyMethod()"); + } + + [Test] + public void ServerAttributeOnOverrideMethod() + { + IsSuccess(); + CheckAddedCode(NetworkServerGetActive, "WeaverClientServerAttributeTests.ServerAttributeOnOverrideMethod.ServerAttributeOnOverrideMethod", "ServerOnlyMethod"); + } + + [Test] + public void NetworkBehaviourClient() + { + IsSuccess(); + CheckAddedCode(NetworkClientGetActive, "WeaverClientServerAttributeTests.NetworkBehaviourClient.NetworkBehaviourClient", "ClientOnlyMethod"); + } + + [Test] + public void ClientAttributeOnVirutalMethod() + { + IsSuccess(); + CheckAddedCode(NetworkClientGetActive, "WeaverClientServerAttributeTests.ClientAttributeOnVirutalMethod.ClientAttributeOnVirutalMethod", "ClientOnlyMethod"); + } + + [Test] + public void ClientAttributeOnAbstractMethod() + { + HasError("Server or Client Attributes can't be added to abstract method. Server and Client Attributes are not inherited so they need to be applied to the override methods instead.", + "System.Void WeaverClientServerAttributeTests.ClientAttributeOnAbstractMethod.ClientAttributeOnAbstractMethod::ClientOnlyMethod()"); + } + + [Test] + public void ClientAttributeOnOverrideMethod() + { + IsSuccess(); + CheckAddedCode(NetworkClientGetActive, "WeaverClientServerAttributeTests.ClientAttributeOnOverrideMethod.ClientAttributeOnOverrideMethod", "ClientOnlyMethod"); + } + + [Test] + public void StaticClassClient() + { + IsSuccess(); + CheckAddedCode(NetworkClientGetActive, "WeaverClientServerAttributeTests.StaticClassClient.StaticClassClient", "ClientOnlyMethod"); + } + + [Test] + public void RegularClassClient() + { + IsSuccess(); + CheckAddedCode(NetworkClientGetActive, "WeaverClientServerAttributeTests.RegularClassClient.RegularClassClient", "ClientOnlyMethod"); + } + + [Test] + public void MonoBehaviourClient() + { + IsSuccess(); + CheckAddedCode(NetworkClientGetActive, "WeaverClientServerAttributeTests.MonoBehaviourClient.MonoBehaviourClient", "ClientOnlyMethod"); + } + + [Test] + public void StaticClassServer() + { + IsSuccess(); + CheckAddedCode(NetworkServerGetActive, "WeaverClientServerAttributeTests.StaticClassServer.StaticClassServer", "ServerOnlyMethod"); + } + + [Test] + public void RegularClassServer() + { + IsSuccess(); + CheckAddedCode(NetworkServerGetActive, "WeaverClientServerAttributeTests.RegularClassServer.RegularClassServer", "ServerOnlyMethod"); + } + + [Test] + public void MonoBehaviourServer() + { + IsSuccess(); + CheckAddedCode(NetworkServerGetActive, "WeaverClientServerAttributeTests.MonoBehaviourServer.MonoBehaviourServer", "ServerOnlyMethod"); + } + + // Checks that first Instructions in MethodBody is addedString + static void CheckAddedCode(string addedString, string className, string methodName) + { + string assemblyName = Path.Combine(WeaverAssembler.OutputDirectory, WeaverAssembler.OutputFile); + using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyName)) + { + TypeDefinition type = assembly.MainModule.GetType(className); + MethodDefinition method = type.Methods.First(m => m.Name == methodName); + MethodBody body = method.Body; + + Instruction top = body.Instructions[0]; + + Assert.AreEqual(top.OpCode, OpCodes.Call); + Assert.AreEqual(top.Operand.ToString(), addedString); + } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests.cs.meta new file mode 100644 index 000000000..7b9685839 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54da10f79c18fad49818f5ebc546aa90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnAbstractMethod.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnAbstractMethod.cs new file mode 100644 index 000000000..8fd30cac0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnAbstractMethod.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.ClientAttributeOnAbstractMethod +{ + abstract class ClientAttributeOnAbstractMethod : NetworkBehaviour + { + [Client] + protected abstract void ClientOnlyMethod(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnOverrideMethod.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnOverrideMethod.cs new file mode 100644 index 000000000..619402086 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnOverrideMethod.cs @@ -0,0 +1,21 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.ClientAttributeOnOverrideMethod +{ + class ClientAttributeOnOverrideMethod : BaseClass + { + [Client] + protected override void ClientOnlyMethod() + { + // test method + } + } + + class BaseClass : NetworkBehaviour + { + protected virtual void ClientOnlyMethod() + { + // test method + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnVirutalMethod.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnVirutalMethod.cs new file mode 100644 index 000000000..a541fc33e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ClientAttributeOnVirutalMethod.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.ClientAttributeOnVirutalMethod +{ + class ClientAttributeOnVirutalMethod : NetworkBehaviour + { + [Client] + protected virtual void ClientOnlyMethod() + { + // test method + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/MonoBehaviourClient.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/MonoBehaviourClient.cs new file mode 100644 index 000000000..9f9d381f9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/MonoBehaviourClient.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverClientServerAttributeTests.MonoBehaviourClient +{ + class MonoBehaviourClient : MonoBehaviour + { + [Client] + void ClientOnlyMethod() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/MonoBehaviourServer.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/MonoBehaviourServer.cs new file mode 100644 index 000000000..c5c77bd2e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/MonoBehaviourServer.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverClientServerAttributeTests.MonoBehaviourServer +{ + class MonoBehaviourServer : MonoBehaviour + { + [Server] + void ServerOnlyMethod() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/NetworkBehaviourClient.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/NetworkBehaviourClient.cs new file mode 100644 index 000000000..3fc0ad730 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/NetworkBehaviourClient.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.NetworkBehaviourClient +{ + class NetworkBehaviourClient : NetworkBehaviour + { + [Client] + void ClientOnlyMethod() + { + // test method + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/NetworkBehaviourServer.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/NetworkBehaviourServer.cs new file mode 100644 index 000000000..c5b972f08 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/NetworkBehaviourServer.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.NetworkBehaviourServer +{ + class NetworkBehaviourServer : NetworkBehaviour + { + [Server] + void ServerOnlyMethod() + { + // test method + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/RegularClassClient.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/RegularClassClient.cs new file mode 100644 index 000000000..0b3e6ebaf --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/RegularClassClient.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.RegularClassClient +{ + class RegularClassClient + { + [Client] + void ClientOnlyMethod() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/RegularClassServer.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/RegularClassServer.cs new file mode 100644 index 000000000..066bd968a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/RegularClassServer.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.RegularClassServer +{ + class RegularClassServer + { + [Server] + void ServerOnlyMethod() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnAbstractMethod.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnAbstractMethod.cs new file mode 100644 index 000000000..26e83bbc8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnAbstractMethod.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.ServerAttributeOnAbstractMethod +{ + abstract class ServerAttributeOnAbstractMethod : NetworkBehaviour + { + [Server] + protected abstract void ServerOnlyMethod(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnOverrideMethod.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnOverrideMethod.cs new file mode 100644 index 000000000..82beea580 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnOverrideMethod.cs @@ -0,0 +1,21 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.ServerAttributeOnOverrideMethod +{ + class ServerAttributeOnOverrideMethod : BaseClass + { + [Server] + protected override void ServerOnlyMethod() + { + // test method + } + } + + class BaseClass : NetworkBehaviour + { + protected virtual void ServerOnlyMethod() + { + // test method + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnVirutalMethod.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnVirutalMethod.cs new file mode 100644 index 000000000..9e1cccee1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/ServerAttributeOnVirutalMethod.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.ServerAttributeOnVirutalMethod +{ + class ServerAttributeOnVirutalMethod : NetworkBehaviour + { + [Server] + protected virtual void ServerOnlyMethod() + { + // test method + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/StaticClassClient.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/StaticClassClient.cs new file mode 100644 index 000000000..563bef3c1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/StaticClassClient.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.StaticClassClient +{ + static class StaticClassClient + { + [Client] + static void ClientOnlyMethod() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/StaticClassServer.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/StaticClassServer.cs new file mode 100644 index 000000000..483eb239b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverClientServerAttributeTests~/StaticClassServer.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverClientServerAttributeTests.StaticClassServer +{ + static class StaticClassServer + { + [Server] + static void ServerOnlyMethod() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests.cs new file mode 100644 index 000000000..158f054ab --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests.cs @@ -0,0 +1,42 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverCommandTests : WeaverTestsBuildFromTestName + { + [Test] + public void CommandCantBeStatic() + { + HasError("CmdCantBeStatic must not be static", + "System.Void WeaverCommandTests.CommandCantBeStatic.CommandCantBeStatic::CmdCantBeStatic()"); + } + + [Test] + public void ErrorForOptionalNetworkConnectionThatIsNotSenderConnection() + { + HasError("CmdFunction has invalid parameter connection, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", + "System.Void WeaverCommandTests.ErrorForOptionalNetworkConnectionThatIsNotSenderConnection.ErrorForOptionalNetworkConnectionThatIsNotSenderConnection::CmdFunction(Mirror.NetworkConnection)"); + } + + [Test] + public void ErrorForNetworkConnectionThatIsNotSenderConnection() + { + HasError("CmdFunction has invalid parameter connection, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", + "System.Void WeaverCommandTests.ErrorForNetworkConnectionThatIsNotSenderConnection.ErrorForNetworkConnectionThatIsNotSenderConnection::CmdFunction(Mirror.NetworkConnection)"); + } + + [Test] + public void AbstractCommand() + { + HasError("Abstract Commands are currently not supported, use virtual method instead", + "System.Void WeaverCommandTests.AbstractCommand.AbstractCommand::CmdDoSomething()"); + } + + [Test] + public void OverrideAbstractCommand() + { + HasError("Abstract Commands are currently not supported, use virtual method instead", + "System.Void WeaverCommandTests.OverrideAbstractCommand.BaseBehaviour::CmdDoSomething()"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests.cs.meta new file mode 100644 index 000000000..4760a2ea7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d2e2e1e726925e4fab5f2460542844d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess.meta new file mode 100644 index 000000000..90cf3e18a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fac079ccee24451589944f06cfb495ab +timeCreated: 1643081493 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthority.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthority.cs new file mode 100644 index 000000000..d43697215 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthority.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverCommandTests.CommandThatIgnoresAuthority +{ + class CommandThatIgnoresAuthority : NetworkBehaviour + { + [Command(requiresAuthority = false)] + void CmdFunction() + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthority.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthority.cs.meta new file mode 100644 index 000000000..879f4da18 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthority.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e4265cfab73ff4eef80a01723f6fb5fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthorityWithSenderConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthorityWithSenderConnection.cs new file mode 100644 index 000000000..1b2161ac6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthorityWithSenderConnection.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverCommandTests.CommandThatIgnoresAuthorityWithSenderConnection +{ + class CommandThatIgnoresAuthorityWithSenderConnection : NetworkBehaviour + { + [Command(requiresAuthority = false)] + void CmdFunction(NetworkConnectionToClient connection = null) + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthorityWithSenderConnection.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthorityWithSenderConnection.cs.meta new file mode 100644 index 000000000..59db1afee --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandThatIgnoresAuthorityWithSenderConnection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 10537f40690ce495486d55802e093ada +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandValid.cs new file mode 100644 index 000000000..5055cbf0f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandValid.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverCommandTests.CommandValid +{ + class CommandValid : NetworkBehaviour + { + [Command] + void CmdThatIsTotallyValid() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandValid.cs.meta new file mode 100644 index 000000000..61820d3f1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3d5568e41a2584f84a1bff2d39dd38a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithArguments.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithArguments.cs new file mode 100644 index 000000000..416775729 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithArguments.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverCommandTests.CommandWithArguments +{ + class CommandWithArguments : NetworkBehaviour + { + [Command] + void CmdThatIsTotallyValid(int someNumber, NetworkIdentity someTarget) + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithArguments.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithArguments.cs.meta new file mode 100644 index 000000000..1fa1dc61e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithArguments.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d47ddf01d3e142c1bfa1dd16d96bc27 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithSenderConnectionAndOtherArgs.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithSenderConnectionAndOtherArgs.cs new file mode 100644 index 000000000..7d4963fcb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithSenderConnectionAndOtherArgs.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverCommandTests.CommandWithSenderConnectionAndOtherArgs +{ + class CommandWithSenderConnectionAndOtherArgs : NetworkBehaviour + { + [Command(requiresAuthority = false)] + void CmdFunction(int someNumber, NetworkIdentity someTarget, NetworkConnectionToClient connection = null) + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithSenderConnectionAndOtherArgs.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithSenderConnectionAndOtherArgs.cs.meta new file mode 100644 index 000000000..55cb4e793 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/CommandWithSenderConnectionAndOtherArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5b439a62107449039062d996d83d063 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallBaseCommand.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallBaseCommand.cs new file mode 100644 index 000000000..0784e1700 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallBaseCommand.cs @@ -0,0 +1,23 @@ +using Mirror; + +namespace WeaverCommandTests.OverrideVirtualCallBaseCommand +{ + class OverrideVirtualCallBaseCommand : baseBehaviour + { + [Command] + protected override void CmdDoSomething() + { + // do somethin + base.CmdDoSomething(); + } + } + + class baseBehaviour : NetworkBehaviour + { + [Command] + protected virtual void CmdDoSomething() + { + // do more stuff + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallBaseCommand.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallBaseCommand.cs.meta new file mode 100644 index 000000000..6ce08108b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallBaseCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a333e7e4ee5544dc8ceb57c1615c2e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithMultipleBaseClasses.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithMultipleBaseClasses.cs new file mode 100644 index 000000000..842fa5561 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithMultipleBaseClasses.cs @@ -0,0 +1,27 @@ +using Mirror; + +namespace WeaverCommandTests.OverrideVirtualCallsBaseCommandWithMultipleBaseClasses +{ + class OverrideVirtualCallsBaseCommandWithMultipleBaseClasses : middleBehaviour + { + [Command] + protected override void CmdDoSomething() + { + // do somethin + base.CmdDoSomething(); + } + } + + class middleBehaviour : baseBehaviour + { + } + + class baseBehaviour : NetworkBehaviour + { + [Command] + protected virtual void CmdDoSomething() + { + // do more stuff + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithMultipleBaseClasses.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithMultipleBaseClasses.cs.meta new file mode 100644 index 000000000..61f26600e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithMultipleBaseClasses.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99460dd9fbe0e4ff2902469dc389ae96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithOverride.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithOverride.cs new file mode 100644 index 000000000..35736d783 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithOverride.cs @@ -0,0 +1,34 @@ +using Mirror; + +namespace WeaverCommandTests.OverrideVirtualCallsBaseCommandWithOverride +{ + class OverrideVirtualCallsBaseCommandWithOverride : middleBehaviour + { + [Command] + protected override void CmdDoSomething() + { + // do something + base.CmdDoSomething(); + } + } + + + class middleBehaviour : baseBehaviour + { + [Command] + protected override void CmdDoSomething() + { + // do something else + base.CmdDoSomething(); + } + } + + class baseBehaviour : NetworkBehaviour + { + [Command] + protected virtual void CmdDoSomething() + { + // do more stuff + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithOverride.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithOverride.cs.meta new file mode 100644 index 000000000..6f2cd2bc3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCallsBaseCommandWithOverride.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e0058cedd9d5b41cda88b402d7c303be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCommand.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCommand.cs new file mode 100644 index 000000000..c02684b24 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCommand.cs @@ -0,0 +1,22 @@ +using Mirror; + +namespace WeaverCommandTests.OverrideVirtualCommand +{ + class OverrideVirtualCommand : baseBehaviour + { + [Command] + protected override void CmdDoSomething() + { + // do something + } + } + + class baseBehaviour : NetworkBehaviour + { + [Command] + protected virtual void CmdDoSomething() + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCommand.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCommand.cs.meta new file mode 100644 index 000000000..a9676d391 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/OverrideVirtualCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4f0277f89a9642f794a6bd3300ab09d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/VirtualCommand.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/VirtualCommand.cs new file mode 100644 index 000000000..493f23cc7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/VirtualCommand.cs @@ -0,0 +1,14 @@ +using Mirror; + + +namespace WeaverCommandTests.VirtualCommand +{ + class VirtualCommand : NetworkBehaviour + { + [Command] + protected virtual void CmdDoSomething() + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/VirtualCommand.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/VirtualCommand.cs.meta new file mode 100644 index 000000000..04aa6930f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests_IsSuccess/VirtualCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d6761de4ffa14f0797f83e428c9f321 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/AbstractCommand.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/AbstractCommand.cs new file mode 100644 index 000000000..ba1c2b5c9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/AbstractCommand.cs @@ -0,0 +1,11 @@ +using Mirror; + + +namespace WeaverCommandTests.AbstractCommand +{ + abstract class AbstractCommand : NetworkBehaviour + { + [Command] + protected abstract void CmdDoSomething(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/CommandCantBeStatic.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/CommandCantBeStatic.cs new file mode 100644 index 000000000..e1ae6658f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/CommandCantBeStatic.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverCommandTests.CommandCantBeStatic +{ + class CommandCantBeStatic : NetworkBehaviour + { + [Command] + static void CmdCantBeStatic() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/ErrorForNetworkConnectionThatIsNotSenderConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/ErrorForNetworkConnectionThatIsNotSenderConnection.cs new file mode 100644 index 000000000..1443a861b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/ErrorForNetworkConnectionThatIsNotSenderConnection.cs @@ -0,0 +1,14 @@ +using Mirror; + + +namespace WeaverCommandTests.ErrorForNetworkConnectionThatIsNotSenderConnection +{ + class ErrorForNetworkConnectionThatIsNotSenderConnection : NetworkBehaviour + { + [Command(requiresAuthority = false)] + void CmdFunction(NetworkConnection connection) + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/ErrorForOptionalNetworkConnectionThatIsNotSenderConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/ErrorForOptionalNetworkConnectionThatIsNotSenderConnection.cs new file mode 100644 index 000000000..b50c7f31d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/ErrorForOptionalNetworkConnectionThatIsNotSenderConnection.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverCommandTests.ErrorForOptionalNetworkConnectionThatIsNotSenderConnection +{ + class ErrorForOptionalNetworkConnectionThatIsNotSenderConnection : NetworkBehaviour + { + [Command(requiresAuthority = false)] + void CmdFunction(NetworkConnection connection = null) + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/OverrideAbstractCommand.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/OverrideAbstractCommand.cs new file mode 100644 index 000000000..ae6679d3a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverCommandTests~/OverrideAbstractCommand.cs @@ -0,0 +1,20 @@ +using Mirror; + + +namespace WeaverCommandTests.OverrideAbstractCommand +{ + class OverrideAbstractCommand : BaseBehaviour + { + [Command] + protected override void CmdDoSomething() + { + // do something + } + } + + abstract class BaseBehaviour : NetworkBehaviour + { + [Command] + protected abstract void CmdDoSomething(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess.meta new file mode 100644 index 000000000..b00b4ea1d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 70d3b9bd4b7242e3a3c4170a476e7f51 +timeCreated: 1643081656 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/RecursionCount.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/RecursionCount.cs new file mode 100644 index 000000000..d5b55b723 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/RecursionCount.cs @@ -0,0 +1,22 @@ +using Mirror; + +namespace WeaverGeneralTests.RecursionCount +{ + class RecursionCount : NetworkBehaviour + { + public class Potato0 + { + public int hamburgers = 17; + public Potato1 p1; + } + + public class Potato1 + { + public int hamburgers = 18; + public Potato0 p0; + } + + [SyncVar] + Potato0 recursionTime; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/RecursionCount.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/RecursionCount.cs.meta new file mode 100644 index 000000000..50fc4ae12 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/RecursionCount.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: df60a572fc56841c284952761332bcec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/TestingScriptableObjectArraySerialization.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/TestingScriptableObjectArraySerialization.cs new file mode 100644 index 000000000..654d64a0f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/TestingScriptableObjectArraySerialization.cs @@ -0,0 +1,40 @@ +using Mirror; +using UnityEngine; + +namespace WeaverGeneralTests.TestingScriptableObjectArraySerialization +{ + public static class CustomSerializer + { + public static void Writedata(this NetworkWriter writer, Data arg) + { + writer.WriteInt(arg.Var1); + } + + public static Data Readdata(this NetworkReader reader) + { + return new Data + { + Var1 = reader.ReadInt() + }; + } + } + + public class Data : ScriptableObject + { + public int Var1; + } + + public class TestingScriptableObjectArraySerialization : NetworkBehaviour + { + [Command] + public void + // This gonna give error saying-- Mirror.Weaver error: + // Cannot generate writer for scriptable object Data[]. Use a supported type or provide a custom writer + CmdwriteArraydata( + Data[] arg) + { + + //some code + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/TestingScriptableObjectArraySerialization.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/TestingScriptableObjectArraySerialization.cs.meta new file mode 100644 index 000000000..bbe699aa7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneralTests_IsSuccess/TestingScriptableObjectArraySerialization.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2926d9420f39349e893ddec0a517529b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess.meta new file mode 100644 index 000000000..78436b223 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7588efe7b5b844dab82a9f3fb4360e7a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CanUseCustomReadWriteForTypesFromDifferentAssemblies.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CanUseCustomReadWriteForTypesFromDifferentAssemblies.cs new file mode 100644 index 000000000..0673eb49d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CanUseCustomReadWriteForTypesFromDifferentAssemblies.cs @@ -0,0 +1,14 @@ +using Mirror; +using Mirror.Weaver.Tests.Extra; + +namespace GeneratedReaderWriter.CanUseCustomReadWriteForTypesFromDifferentAssemblies +{ + public class CanUseCustomReadWriteForTypesFromDifferentAssemblies : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeDataWithWriter data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CanUseCustomReadWriteForTypesFromDifferentAssemblies.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CanUseCustomReadWriteForTypesFromDifferentAssemblies.cs.meta new file mode 100644 index 000000000..d70d24422 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CanUseCustomReadWriteForTypesFromDifferentAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b99201f4b462349fbbbaa30d63b7383c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssemblies.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssemblies.cs new file mode 100644 index 000000000..78fc9b20b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssemblies.cs @@ -0,0 +1,14 @@ +using Mirror; +using Mirror.Weaver.Tests.Extra; + +namespace GeneratedReaderWriter.CreatesForClassFromDifferentAssemblies +{ + public class CreatesForClassFromDifferentAssemblies : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeDataClass data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssemblies.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssemblies.cs.meta new file mode 100644 index 000000000..69ba0e983 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e098c02c1a4d543fea35eacb265bf185 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssembliesWithValidConstructor.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssembliesWithValidConstructor.cs new file mode 100644 index 000000000..ddb3a95a4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssembliesWithValidConstructor.cs @@ -0,0 +1,14 @@ +using Mirror; +using Mirror.Weaver.Tests.Extra; + +namespace GeneratedReaderWriter.CreatesForClassFromDifferentAssembliesWithValidConstructor +{ + public class CreatesForClassFromDifferentAssembliesWithValidConstructor : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeDataClassWithConstructor data) + { + // empty + } + } +} \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssembliesWithValidConstructor.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssembliesWithValidConstructor.cs.meta new file mode 100644 index 000000000..a382f1870 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForClassFromDifferentAssembliesWithValidConstructor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66afc9de1e55649088cc2e0ddc87e2bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForComplexTypeFromDifferentAssemblies.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForComplexTypeFromDifferentAssemblies.cs new file mode 100644 index 000000000..e9135dee6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForComplexTypeFromDifferentAssemblies.cs @@ -0,0 +1,14 @@ +using Mirror; +using Mirror.Weaver.Tests.Extra; + +namespace GeneratedReaderWriter.CreatesForComplexTypeFromDifferentAssemblies +{ + public class CreatesForComplexTypeFromDifferentAssemblies : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(ComplexData data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForComplexTypeFromDifferentAssemblies.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForComplexTypeFromDifferentAssemblies.cs.meta new file mode 100644 index 000000000..1cc3ce301 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForComplexTypeFromDifferentAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cca8bd4f61264c69b5cebb0d06e6ccd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForStructFromDifferentAssemblies.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForStructFromDifferentAssemblies.cs new file mode 100644 index 000000000..6ce7e3c35 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForStructFromDifferentAssemblies.cs @@ -0,0 +1,14 @@ +using Mirror; +using Mirror.Weaver.Tests.Extra; + +namespace GeneratedReaderWriter.CreatesForStructFromDifferentAssemblies +{ + public class CreatesForStructFromDifferentAssemblies : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeData data) + { + // empty + } + } +} \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForStructFromDifferentAssemblies.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForStructFromDifferentAssemblies.cs.meta new file mode 100644 index 000000000..fe30ea775 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForStructFromDifferentAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4fe20050b18ad437eaea18c81e4b7be5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForTypeThatUsesDifferentAssemblies.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForTypeThatUsesDifferentAssemblies.cs new file mode 100644 index 000000000..f36998481 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForTypeThatUsesDifferentAssemblies.cs @@ -0,0 +1,19 @@ +using Mirror; +using Mirror.Weaver.Tests.Extra; + +namespace GeneratedReaderWriter.CreatesForTypeThatUsesDifferentAssemblies +{ + public class CreatesForTypeThatUsesDifferentAssemblies : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(DataHolder data) + { + // empty + } + } + public struct DataHolder + { + public AnotherData another; + public float q; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForTypeThatUsesDifferentAssemblies.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForTypeThatUsesDifferentAssemblies.cs.meta new file mode 100644 index 000000000..a2c4f8f01 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterAnotherAssemblyTests_IsSuccess/CreatesForTypeThatUsesDifferentAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2ac5c5eaeb6241ccb87cd4d7b104f5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs new file mode 100644 index 000000000..6cd4e232e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs @@ -0,0 +1,133 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverGeneratedReaderWriterTests : WeaverTestsBuildFromTestName + { + [Test] + public void GivesErrorForClassWithNoValidConstructor() + { + HasError("SomeOtherData can't be deserialized because it has no default constructor. Don't use SomeOtherData in [SyncVar]s, Rpcs, Cmds, etc.", + "GeneratedReaderWriter.GivesErrorForClassWithNoValidConstructor.SomeOtherData"); + } + + [Test] + public void GivesErrorWhenUsingUnityAsset() + { + HasError("Material can't be deserialized because it has no default constructor. Don't use Material in [SyncVar]s, Rpcs, Cmds, etc.", + "UnityEngine.Material"); + } + + [Test] + public void GivesErrorWhenUsingObject() + { + // TODO: decide if we want to block sending of Object + // would only want to be send as an arg as a base type for an Inherited object + HasError("Cannot generate writer for Object. Use a supported type or provide a custom writer", + "UnityEngine.Object"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for Object. Use a supported type or provide a custom reader", + // "UnityEngine.Object"); + } + + [Test] + public void GivesErrorWhenUsingScriptableObject() + { + // TODO: decide if we want to block sending of ScripableObject + // would only want to be send as an arg as a base type for an Inherited object + HasError("Cannot generate writer for ScriptableObject. Use a supported type or provide a custom writer", + "UnityEngine.ScriptableObject"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for ScriptableObject. Use a supported type or provide a custom reader", + // "UnityEngine.ScriptableObject"); + } + + [Test] + public void GivesErrorWhenUsingMonoBehaviour() + { + HasError("Cannot generate writer for component type MonoBehaviour. Use a supported type or provide a custom writer", + "UnityEngine.MonoBehaviour"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for component type MonoBehaviour. Use a supported type or provide a custom reader", + // "UnityEngine.MonoBehaviour"); + } + + [Test] + public void GivesErrorWhenUsingTypeInheritedFromMonoBehaviour() + { + HasError("Cannot generate writer for component type MyBehaviour. Use a supported type or provide a custom writer", + "GeneratedReaderWriter.GivesErrorWhenUsingTypeInheritedFromMonoBehaviour.MyBehaviour"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for component type MyBehaviour. Use a supported type or provide a custom reader", + // "GeneratedReaderWriter.GivesErrorWhenUsingTypeInheritedFromMonoBehaviour.MyBehaviour"); + } + + [Test] + public void GivesErrorWhenUsingInterface() + { + HasError("Cannot generate writer for interface IData. Use a supported type or provide a custom writer", + "GeneratedReaderWriter.GivesErrorWhenUsingInterface.IData"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for interface IData. Use a supported type or provide a custom reader", + // "GeneratedReaderWriter.GivesErrorWhenUsingInterface.IData"); + } + + [Test] + public void GivesErrorWhenUsingAbstractClass() + { + HasError("Cannot generate writer for abstract class DataBase. Use a supported type or provide a custom writer", + "GeneratedReaderWriter.GivesErrorWhenUsingAbstractClass.DataBase"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for abstract class DataBase. Use a supported type or provide a custom reader", + // "GeneratedReaderWriter.GivesErrorWhenUsingAbstractClass.DataBase"); + } + + [Test] + public void GivesErrorForMultidimensionalArray() + { + HasError("Int32[0...,0...] is an unsupported type. Multidimensional arrays are not supported", + "System.Int32[0...,0...]"); + } + + [Test] + public void GivesErrorForInvalidArrayType() + { + HasError("Cannot generate writer for UnityEngine.MonoBehaviour[]. Use a supported type or provide a custom writer", + "UnityEngine.MonoBehaviour[]"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for Array because element MonoBehaviour does not have a reader. Use a supported type or provide a custom reader", + // "UnityEngine.MonoBehaviour[]"); + } + + [Test] + public void GivesErrorForInvalidArraySegmentType() + { + HasError("Cannot generate writer for System.ArraySegment`1. Use a supported type or provide a custom writer", + "System.ArraySegment`1"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for ArraySegment because element MonoBehaviour does not have a reader. Use a supported type or provide a custom reader", + // "System.ArraySegment`1"); + } + + [Test] + public void GivesErrorForInvalidListType() + { + HasError("Cannot generate writer for System.Collections.Generic.List`1. Use a supported type or provide a custom writer", + "System.Collections.Generic.List`1"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for List because element MonoBehaviour does not have a reader. Use a supported type or provide a custom reader", + // "System.Collections.Generic.List`1"); + } + + [Test, Ignore("Enable again when we don't have obsoletes in NetworkWriter anymore.")] + public void GivesWarningWhenRegisteringExistingExtensionMethod() + { + const string typeName = "GeneratedReaderWriter.GivesWarningWhenRegisteringExistingExtensionMethod.MyType"; + HasNoErrors(); + HasWarning($"Registering a Write method for {typeName} when one already exists", + "System.Void GeneratedReaderWriter.GivesWarningWhenRegisteringExistingExtensionMethod.ReadWriteExtension::WriteMyType2(Mirror.NetworkWriter,GeneratedReaderWriter.GivesWarningWhenRegisteringExistingExtensionMethod.MyType)"); + HasWarning($"Registering a Read method for {typeName} when one already exists", + "GeneratedReaderWriter.GivesWarningWhenRegisteringExistingExtensionMethod.MyType GeneratedReaderWriter.GivesWarningWhenRegisteringExistingExtensionMethod.ReadWriteExtension::ReadMyType2(Mirror.NetworkReader)"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs.meta new file mode 100644 index 000000000..53522fbf4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ee83eef537268344880fa60f2c7a059 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess.meta new file mode 100644 index 000000000..321d91334 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 481ded00d806459b98c56f88b4c63cac +timeCreated: 1643082170 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForAbstractClass.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForAbstractClass.cs new file mode 100644 index 000000000..831ab34ae --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForAbstractClass.cs @@ -0,0 +1,62 @@ +using Mirror; + +namespace GeneratedReaderWriter.CanUseCustomReadWriteForAbstractClass +{ + public class CanUseCustomReadWriteForAbstractClass : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(DataBase data) + { + // empty + } + } + + public abstract class DataBase + { + public int someField; + public abstract int id { get; } + } + + public class SomeData : DataBase + { + public float anotherField; + public override int id => 1; + } + + public static class DataReadWrite + { + public static void WriteData(this NetworkWriter writer, DataBase data) + { + writer.WriteInt(data.id); + // write extra stuff depending on id here + writer.WriteInt(data.someField); + + if (data.id == 1) + { + SomeData someData = (SomeData)data; + writer.WriteFloat(someData.anotherField); + } + } + + public static DataBase ReadData(this NetworkReader reader) + { + int id = reader.ReadInt(); + + int someField = reader.ReadInt(); + DataBase data = null; + if (data.id == 1) + { + SomeData someData = new SomeData() + { + someField = someField + }; + // read extra stuff depending on id here + + someData.anotherField = reader.ReadFloat(); + + data = someData; + } + return data; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForAbstractClass.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForAbstractClass.cs.meta new file mode 100644 index 000000000..38342051d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForAbstractClass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec3aff614cf064fcfb647c5bf2427ab9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForInterfaces.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForInterfaces.cs new file mode 100644 index 000000000..e80f22613 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForInterfaces.cs @@ -0,0 +1,44 @@ +using Mirror; + +namespace GeneratedReaderWriter.CanUseCustomReadWriteForInterfaces +{ + public class CanUseCustomReadWriteForInterfaces : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(IData data) + { + // empty + } + } + + public interface IData + { + int id { get; } + } + + public class SomeData : IData + { + public int id => 1; + } + + public static class DataReadWrite + { + public static void WriteData(this NetworkWriter writer, IData data) + { + writer.WriteInt(data.id); + // write extra stuff depending on id here + } + + public static IData ReadData(this NetworkReader reader) + { + int id = reader.ReadInt(); + // do something with id + + SomeData someData = new SomeData(); + // read extra stuff depending on id here + + return someData; + } + } + +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForInterfaces.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForInterfaces.cs.meta new file mode 100644 index 000000000..86c8e62de --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CanUseCustomReadWriteForInterfaces.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d362bab10072f4254b5b5ec2dec6a219 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForArraySegment.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForArraySegment.cs new file mode 100644 index 000000000..d43da5bf7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForArraySegment.cs @@ -0,0 +1,14 @@ +using System; +using Mirror; + +namespace GeneratedReaderWriter.CreatesForArraySegment +{ + public class CreatesForArraySegment : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(ArraySegment data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForArraySegment.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForArraySegment.cs.meta new file mode 100644 index 000000000..8189ae88d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForArraySegment.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8e170776a26948e5a371acd9d108e23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClass.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClass.cs new file mode 100644 index 000000000..8441d15ca --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClass.cs @@ -0,0 +1,18 @@ +using Mirror; + +namespace GeneratedReaderWriter.CreatesForClass +{ + public class CreatesForClass : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeOtherData data) + { + // empty + } + } + + public class SomeOtherData + { + public int usefulNumber; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClass.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClass.cs.meta new file mode 100644 index 000000000..5a1dae021 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c40f8d67a65164c4cbee03c0e0e25238 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassInherited.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassInherited.cs new file mode 100644 index 000000000..81ad2cec1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassInherited.cs @@ -0,0 +1,23 @@ +using Mirror; + + +namespace GeneratedReaderWriter.CreatesForClassInherited +{ + public class CreatesForClassInherited : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeOtherData data) + { + // empty + } + } + + public class BaseData + { + public bool yes; + } + public class SomeOtherData : BaseData + { + public int usefulNumber; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassInherited.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassInherited.cs.meta new file mode 100644 index 000000000..63021aa18 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassInherited.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7403ae2e88bb94bb68fb52724eb80b9e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassWithValidConstructor.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassWithValidConstructor.cs new file mode 100644 index 000000000..8c8e604c9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassWithValidConstructor.cs @@ -0,0 +1,23 @@ +using Mirror; + +namespace GeneratedReaderWriter.CreatesForClassWithValidConstructor +{ + public class CreatesForClassWithValidConstructor : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeOtherData data) + { + // empty + } + } + + public class SomeOtherData + { + public int usefulNumber; + + public SomeOtherData() + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassWithValidConstructor.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassWithValidConstructor.cs.meta new file mode 100644 index 000000000..1f272c650 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForClassWithValidConstructor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c8bdbd0be145445769dd26795ebb1e14 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForEnums.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForEnums.cs new file mode 100644 index 000000000..379972c02 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForEnums.cs @@ -0,0 +1,21 @@ +using Mirror; + +namespace GeneratedReaderWriter.CreatesForEnums +{ + class CreatesForEnums : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(FunEnum data) + { + // empty + } + } + + public enum FunEnum + { + A, + B, + C, + D, + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForEnums.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForEnums.cs.meta new file mode 100644 index 000000000..65782e104 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForEnums.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e9b99d242e2d4ec791120b90992f9f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForInheritedFromScriptableObject.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForInheritedFromScriptableObject.cs new file mode 100644 index 000000000..bc28938ac --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForInheritedFromScriptableObject.cs @@ -0,0 +1,19 @@ +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.CreatesForInheritedFromScriptableObject +{ + public class CreatesForInheritedFromScriptableObject : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(DataScriptableObject data) + { + // empty + } + } + + public class DataScriptableObject : ScriptableObject + { + public int usefulNumber; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForInheritedFromScriptableObject.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForInheritedFromScriptableObject.cs.meta new file mode 100644 index 000000000..b3ac125e8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForInheritedFromScriptableObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eaa774dd59f314483be65bb1fecaf240 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForList.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForList.cs new file mode 100644 index 000000000..12dabd46b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForList.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Mirror; + +namespace GeneratedReaderWriter.CreatesForList +{ + public class CreatesForList : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(List data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForList.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForList.cs.meta new file mode 100644 index 000000000..210bcc75f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01e98a567df124dbdae4da004b786676 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructArraySegment.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructArraySegment.cs new file mode 100644 index 000000000..06e535949 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructArraySegment.cs @@ -0,0 +1,21 @@ +using System; +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.CreatesForStructArraySegment +{ + public class CreatesForStructArraySegment : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(ArraySegment data) + { + // empty + } + } + + public struct MyStruct + { + public int someValue; + public Vector3 anotherValue; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructArraySegment.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructArraySegment.cs.meta new file mode 100644 index 000000000..41b8c8ed8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructArraySegment.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4115a88d5bbc6468193943b69c777be5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructList.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructList.cs new file mode 100644 index 000000000..7b21dbd04 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructList.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.CreatesForStructList +{ + public class CreatesForStructList : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(List data) + { + // empty + } + } + + public struct MyStruct + { + public int someValue; + public Vector3 anotherValue; + } +} + diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructList.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructList.cs.meta new file mode 100644 index 000000000..cbbd21de2 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5794897895bef48588d561811d35fe3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructs.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructs.cs new file mode 100644 index 000000000..67ccb3e02 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructs.cs @@ -0,0 +1,18 @@ +using Mirror; + +namespace GeneratedReaderWriter.CreatesForStructs +{ + public class CreatesForStructs : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeOtherData data) + { + // empty + } + } + + public struct SomeOtherData + { + public int usefulNumber; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructs.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructs.cs.meta new file mode 100644 index 000000000..a22041537 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/CreatesForStructs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53df702fb349b4529bdc8d0ae2795cbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/ExcludesNonSerializedFields.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/ExcludesNonSerializedFields.cs new file mode 100644 index 000000000..89509f109 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/ExcludesNonSerializedFields.cs @@ -0,0 +1,20 @@ +using Mirror; + +namespace GeneratedReaderWriter.ExcludesNonSerializedFields +{ + public class ExcludesNonSerializedFields : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeDataUsingNonSerialized data) + { + // empty + } + } + + public struct SomeDataUsingNonSerialized + { + public int usefulNumber; + // Object is a not allowed type + [System.NonSerialized] public UnityEngine.Object obj; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/ExcludesNonSerializedFields.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/ExcludesNonSerializedFields.cs.meta new file mode 100644 index 000000000..aef32872c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/ExcludesNonSerializedFields.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a30a5f4499dde487baad74ec2eb09279 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/GivesErrorForJaggedArray.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/GivesErrorForJaggedArray.cs new file mode 100644 index 000000000..236c54e4e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/GivesErrorForJaggedArray.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace GeneratedReaderWriter.GivesErrorForJaggedArray +{ + public class GivesErrorForJaggedArray : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(int[][] data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/GivesErrorForJaggedArray.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/GivesErrorForJaggedArray.cs.meta new file mode 100644 index 000000000..ec4f4c051 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests_IsSuccess/GivesErrorForJaggedArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bfb5cd2f56ad544369df0ec231dd91e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForClassWithNoValidConstructor.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForClassWithNoValidConstructor.cs new file mode 100644 index 000000000..440bdf53a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForClassWithNoValidConstructor.cs @@ -0,0 +1,23 @@ +using Mirror; + +namespace GeneratedReaderWriter.GivesErrorForClassWithNoValidConstructor +{ + public class GivesErrorForClassWithNoValidConstructor : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(SomeOtherData data) + { + // empty + } + } + + public class SomeOtherData + { + public int usefulNumber; + + public SomeOtherData(int usefulNumber) + { + this.usefulNumber = usefulNumber; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidArraySegmentType.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidArraySegmentType.cs new file mode 100644 index 000000000..d81c3ddbc --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidArraySegmentType.cs @@ -0,0 +1,15 @@ +using System; +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorForInvalidArraySegmentType +{ + public class GivesErrorForInvalidArraySegmentType : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(ArraySegment data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidArrayType.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidArrayType.cs new file mode 100644 index 000000000..a63124e9d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidArrayType.cs @@ -0,0 +1,14 @@ +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorForInvalidArrayType +{ + public class GivesErrorForInvalidArrayType : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(MonoBehaviour[] data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidListType.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidListType.cs new file mode 100644 index 000000000..8dfaeab11 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForInvalidListType.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorForInvalidListType +{ + public class GivesErrorForInvalidListType : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(List data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForMultidimensionalArray.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForMultidimensionalArray.cs new file mode 100644 index 000000000..297fafa3f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorForMultidimensionalArray.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace GeneratedReaderWriter.GivesErrorForMultidimensionalArray +{ + public class GivesErrorForMultidimensionalArray : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(int[,] data) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingAbstractClass.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingAbstractClass.cs new file mode 100644 index 000000000..a8929f446 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingAbstractClass.cs @@ -0,0 +1,19 @@ +using Mirror; + +namespace GeneratedReaderWriter.GivesErrorWhenUsingAbstractClass +{ + public class GivesErrorWhenUsingAbstractClass : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(DataBase data) + { + // empty + } + } + + public abstract class DataBase + { + public int someField; + public abstract int id { get; } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingInterface.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingInterface.cs new file mode 100644 index 000000000..2feaab8da --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingInterface.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace GeneratedReaderWriter.GivesErrorWhenUsingInterface +{ + public class GivesErrorWhenUsingInterface : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(IData data) + { + // empty + } + } + + public interface IData { } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingMonoBehaviour.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingMonoBehaviour.cs new file mode 100644 index 000000000..28ff7dcec --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingMonoBehaviour.cs @@ -0,0 +1,14 @@ +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorWhenUsingMonoBehaviour +{ + public class GivesErrorWhenUsingMonoBehaviour : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(MonoBehaviour behaviour) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingObject.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingObject.cs new file mode 100644 index 000000000..753b8a80b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingObject.cs @@ -0,0 +1,14 @@ +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorWhenUsingObject +{ + public class GivesErrorWhenUsingObject : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(Object obj) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingScriptableObject.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingScriptableObject.cs new file mode 100644 index 000000000..fe0c3a55d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingScriptableObject.cs @@ -0,0 +1,14 @@ +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorWhenUsingScriptableObject +{ + public class GivesErrorWhenUsingScriptableObject : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(ScriptableObject obj) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingTypeInheritedFromMonoBehaviour.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingTypeInheritedFromMonoBehaviour.cs new file mode 100644 index 000000000..628070d62 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingTypeInheritedFromMonoBehaviour.cs @@ -0,0 +1,19 @@ +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorWhenUsingTypeInheritedFromMonoBehaviour +{ + public class GivesErrorWhenUsingTypeInheritedFromMonoBehaviour : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(MyBehaviour behaviour) + { + // empty + } + } + + public class MyBehaviour : MonoBehaviour + { + public int usefulNumber; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingUnityAsset.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingUnityAsset.cs new file mode 100644 index 000000000..db2fbd8dd --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesErrorWhenUsingUnityAsset.cs @@ -0,0 +1,14 @@ +using Mirror; +using UnityEngine; + +namespace GeneratedReaderWriter.GivesErrorWhenUsingUnityAsset +{ + public class GivesErrorForClassWithNoValidConstructor : NetworkBehaviour + { + [ClientRpc] + public void RpcDoSomething(Material material) + { + // empty + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesWarningWhenRegisteringExistingExtensionMethod.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesWarningWhenRegisteringExistingExtensionMethod.cs new file mode 100644 index 000000000..733589b79 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests~/GivesWarningWhenRegisteringExistingExtensionMethod.cs @@ -0,0 +1,32 @@ +using Mirror; + +namespace GeneratedReaderWriter.GivesWarningWhenRegisteringExistingExtensionMethod +{ + public struct MyType + { + public int number; + } + + public static class ReadWriteExtension + { + public static void WriteMyType(this NetworkWriter writer, MyType data) + { + writer.WriteInt(data.number); + } + + public static void WriteMyType2(this NetworkWriter writer, MyType data) + { + writer.WriteInt(data.number); + } + + public static MyType ReadMyType(this NetworkReader reader) + { + return new MyType { number = reader.ReadInt() }; + } + + public static MyType ReadMyType2(this NetworkReader reader) + { + return new MyType { number = reader.ReadInt() }; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests.cs new file mode 100644 index 000000000..21db09456 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests.cs @@ -0,0 +1,30 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverMessageTests : WeaverTestsBuildFromTestName + { + [Test] + public void MessageMemberGeneric() + { + HasError("Cannot generate reader for generic variable HasGeneric`1. Use a supported type or provide a custom reader", + "WeaverMessageTests.MessageMemberGeneric.HasGeneric`1"); + HasError("invalidField has an unsupported type", + "WeaverMessageTests.MessageMemberGeneric.HasGeneric`1 WeaverMessageTests.MessageMemberGeneric.MessageMemberGeneric::invalidField"); + HasError("Cannot generate writer for generic type HasGeneric`1. Use a supported type or provide a custom writer", + "WeaverMessageTests.MessageMemberGeneric.HasGeneric`1"); + } + + [Test] + public void MessageMemberInterface() + { + HasError("Cannot generate reader for interface SuperCoolInterface. Use a supported type or provide a custom reader", + "WeaverMessageTests.MessageMemberInterface.SuperCoolInterface"); + HasError("invalidField has an unsupported type", + "WeaverMessageTests.MessageMemberInterface.SuperCoolInterface WeaverMessageTests.MessageMemberInterface.MessageMemberInterface::invalidField"); + HasError("Cannot generate writer for interface SuperCoolInterface. Use a supported type or provide a custom writer", + "WeaverMessageTests.MessageMemberInterface.SuperCoolInterface"); + } + } +} + diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests.cs.meta new file mode 100644 index 000000000..dae37dbf4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 29c6628d42055df42a471ce273c5a7c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess.meta new file mode 100644 index 000000000..1e58f68c3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 189a9592d5ca4459aadeb4933ad579b4 +timeCreated: 1643082574 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/AbstractMessageMethods.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/AbstractMessageMethods.cs new file mode 100644 index 000000000..0ca28ecea --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/AbstractMessageMethods.cs @@ -0,0 +1,20 @@ +using Mirror; + +namespace WeaverMessageTests.AbstractMessageMethods +{ + abstract class AbstractMessage : NetworkMessage + { + public abstract void Deserialize(NetworkReader reader); + public abstract void Serialize(NetworkWriter writer); + } + + class OverrideMessage : AbstractMessage + { + public int someValue; + + // Mirror will fill out these empty methods + + public override void Serialize(NetworkWriter writer) { } + public override void Deserialize(NetworkReader reader) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/AbstractMessageMethods.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/AbstractMessageMethods.cs.meta new file mode 100644 index 000000000..2e91fefcb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/AbstractMessageMethods.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2526f960017544a9ac51109e40e5ca2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageNestedInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageNestedInheritance.cs new file mode 100644 index 000000000..cd8113b94 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageNestedInheritance.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverMessageTests.MessageNestedInheritance +{ + public class Message : NetworkMessage + { + public class Request : Message + { + + } + + public class Response : Message + { + public int errorCode; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageNestedInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageNestedInheritance.cs.meta new file mode 100644 index 000000000..f8259e0b0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageNestedInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57ca45dd13a334f3e82bec8161e2a309 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageSelfReferencing.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageSelfReferencing.cs new file mode 100644 index 000000000..9d861fe86 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageSelfReferencing.cs @@ -0,0 +1,16 @@ +using System; +using Mirror; +using UnityEngine; + +namespace WeaverMessageTests.MessageSelfReferencing +{ + class MessageSelfReferencing : NetworkMessage + { + public uint netId; + public Guid assetId; + public Vector3 position; + public Quaternion rotation; + public MessageSelfReferencing selfReference = new MessageSelfReferencing(); + public byte[] payload; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageSelfReferencing.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageSelfReferencing.cs.meta new file mode 100644 index 000000000..64a2e846f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageSelfReferencing.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80d871bb7d76d4ba4bb61c4463a18611 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageValid.cs new file mode 100644 index 000000000..a4f130701 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageValid.cs @@ -0,0 +1,15 @@ +using System; +using Mirror; +using UnityEngine; + +namespace WeaverMessageTests.MessageValid +{ + class MessageValid : NetworkMessage + { + public uint netId; + public Guid assetId; + public Vector3 position; + public Quaternion rotation; + public byte[] payload; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageValid.cs.meta new file mode 100644 index 000000000..eb84a92d6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f9e80c0fd45740d1923469de369a8d3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageWithBaseClass.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageWithBaseClass.cs new file mode 100644 index 000000000..31bae57e4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageWithBaseClass.cs @@ -0,0 +1,20 @@ +using System; +using Mirror; +using UnityEngine; + +namespace WeaverMessageTests.MessageWithBaseClass +{ + class MessageWithBaseClass : SomeBaseMessage + { + public uint netId; + public Guid assetId; + public Vector3 position; + public Quaternion rotation; + public byte[] payload; + } + + class SomeBaseMessage : NetworkMessage + { + public int myExtraType; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageWithBaseClass.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageWithBaseClass.cs.meta new file mode 100644 index 000000000..c457c158f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests_IsSuccess/MessageWithBaseClass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ad4ec02f843347b7bbbc9b85d455124 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests~/MessageMemberGeneric.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests~/MessageMemberGeneric.cs new file mode 100644 index 000000000..9dcca9b9b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests~/MessageMemberGeneric.cs @@ -0,0 +1,18 @@ +using System; +using Mirror; +using UnityEngine; + +namespace WeaverMessageTests.MessageMemberGeneric +{ + class HasGeneric { } + + class MessageMemberGeneric : NetworkMessage + { + public uint netId; + public Guid assetId; + public Vector3 position; + public Quaternion rotation; + public HasGeneric invalidField; + public byte[] payload; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests~/MessageMemberInterface.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests~/MessageMemberInterface.cs new file mode 100644 index 000000000..96c55221f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMessageTests~/MessageMemberInterface.cs @@ -0,0 +1,18 @@ +using System; +using Mirror; +using UnityEngine; + +namespace WeaverMessageTests.MessageMemberInterface +{ + interface SuperCoolInterface { } + + class MessageMemberInterface : NetworkMessage + { + public uint netId; + public Guid assetId; + public Vector3 position; + public Quaternion rotation; + public SuperCoolInterface invalidField; + public byte[] payload; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests.cs new file mode 100644 index 000000000..3469e7bd5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests.cs @@ -0,0 +1,42 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverMonoBehaviourTests : WeaverTestsBuildFromTestName + { + [Test] + public void MonoBehaviourSyncVar() + { + HasError("SyncVar potato must be inside a NetworkBehaviour. MonoBehaviourSyncVar is not a NetworkBehaviour", + "System.Int32 WeaverMonoBehaviourTests.MonoBehaviourSyncVar.MonoBehaviourSyncVar::potato"); + } + + [Test] + public void MonoBehaviourSyncList() + { + HasError("potato is a SyncObject and must be inside a NetworkBehaviour. MonoBehaviourSyncList is not a NetworkBehaviour", + "Mirror.SyncList`1 WeaverMonoBehaviourTests.MonoBehaviourSyncList.MonoBehaviourSyncList::potato"); + } + + [Test] + public void MonoBehaviourCommand() + { + HasError("Command CmdThisCantBeOutsideNetworkBehaviour must be declared inside a NetworkBehaviour", + "System.Void WeaverMonoBehaviourTests.MonoBehaviourCommand.MonoBehaviourCommand::CmdThisCantBeOutsideNetworkBehaviour()"); + } + + [Test] + public void MonoBehaviourClientRpc() + { + HasError("ClientRpc RpcThisCantBeOutsideNetworkBehaviour must be declared inside a NetworkBehaviour", + "System.Void WeaverMonoBehaviourTests.MonoBehaviourClientRpc.MonoBehaviourClientRpc::RpcThisCantBeOutsideNetworkBehaviour()"); + } + + [Test] + public void MonoBehaviourTargetRpc() + { + HasError("TargetRpc TargetThisCantBeOutsideNetworkBehaviour must be declared inside a NetworkBehaviour", + "System.Void WeaverMonoBehaviourTests.MonoBehaviourTargetRpc.MonoBehaviourTargetRpc::TargetThisCantBeOutsideNetworkBehaviour(Mirror.NetworkConnection)"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests.cs.meta new file mode 100644 index 000000000..9a127a7d8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49f43099dd42441488962e7baf02a35e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess.meta new file mode 100644 index 000000000..fb0609a12 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ec2bed0a50964e698c382457a790ef26 +timeCreated: 1643082698 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClient.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClient.cs new file mode 100644 index 000000000..a94b5e273 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClient.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourClient +{ + class MonoBehaviourClient : MonoBehaviour + { + [Client] + void ThisCantBeOutsideNetworkBehaviour() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClient.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClient.cs.meta new file mode 100644 index 000000000..3d1d9bcf3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c0dbb08bb37f4bc0bdf0c2b0c414adb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClientCallback.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClientCallback.cs new file mode 100644 index 000000000..49c22323e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClientCallback.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourClientCallback +{ + class MonoBehaviourClientCallback : MonoBehaviour + { + [ClientCallback] + void ThisCantBeOutsideNetworkBehaviour() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClientCallback.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClientCallback.cs.meta new file mode 100644 index 000000000..332609184 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourClientCallback.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b06345372a7df4b31b37cac6fc6e3b55 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServer.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServer.cs new file mode 100644 index 000000000..548812654 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServer.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourServer +{ + class MonoBehaviourServer : MonoBehaviour + { + [Server] + void ThisCantBeOutsideNetworkBehaviour() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServer.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServer.cs.meta new file mode 100644 index 000000000..11c1c1334 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 06ff2af14a7e04598b10c38cebb0dfd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServerCallback.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServerCallback.cs new file mode 100644 index 000000000..1ce6075cb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServerCallback.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourServerCallback +{ + class MonoBehaviourServerCallback : MonoBehaviour + { + [ServerCallback] + void ThisCantBeOutsideNetworkBehaviour() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServerCallback.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServerCallback.cs.meta new file mode 100644 index 000000000..7ab973762 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourServerCallback.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 267c93d64d13540e1a8440b0327bc4a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourValid.cs new file mode 100644 index 000000000..5268ac902 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourValid.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourValid +{ + class MonoBehaviourValid : MonoBehaviour + { +#pragma warning disable CS0414 + int monkeys = 12; +#pragma warning restore CS0414 + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourValid.cs.meta new file mode 100644 index 000000000..458a598a3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests_IsSuccess/MonoBehaviourValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 92be36d5dc0cd45a3a73c7185c51e4ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourClientRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourClientRpc.cs new file mode 100644 index 000000000..c85b92f12 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourClientRpc.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourClientRpc +{ + class MonoBehaviourClientRpc : MonoBehaviour + { + [ClientRpc] + void RpcThisCantBeOutsideNetworkBehaviour() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourCommand.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourCommand.cs new file mode 100644 index 000000000..fd35c0551 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourCommand.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourCommand +{ + class MonoBehaviourCommand : MonoBehaviour + { + [Command] + void CmdThisCantBeOutsideNetworkBehaviour() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourSyncList.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourSyncList.cs new file mode 100644 index 000000000..cba92f48d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourSyncList.cs @@ -0,0 +1,10 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourSyncList +{ + class MonoBehaviourSyncList : MonoBehaviour + { + SyncList potato; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourSyncVar.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourSyncVar.cs new file mode 100644 index 000000000..5727bab49 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourSyncVar.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourSyncVar +{ + class MonoBehaviourSyncVar : MonoBehaviour + { + [SyncVar] + int potato; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourTargetRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourTargetRpc.cs new file mode 100644 index 000000000..21318e671 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverMonoBehaviourTests~/MonoBehaviourTargetRpc.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverMonoBehaviourTests.MonoBehaviourTargetRpc +{ + class MonoBehaviourTargetRpc : MonoBehaviour + { + [TargetRpc] + void TargetThisCantBeOutsideNetworkBehaviour(NetworkConnection nc) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests.cs new file mode 100644 index 000000000..a794a2d7e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests.cs @@ -0,0 +1,243 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverNetworkBehaviourTests : WeaverTestsBuildFromTestName + { + [Test] + public void NetworkBehaviourGeneric() + { + HasError("NetworkBehaviourGeneric`1 cannot have generic parameters", + "WeaverNetworkBehaviourTests.NetworkBehaviourGeneric.NetworkBehaviourGeneric`1"); + } + + [Test] + public void NetworkBehaviourCmdGenericParam() + { + HasError("CmdCantHaveGeneric cannot have generic parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdGenericParam.NetworkBehaviourCmdGenericParam::CmdCantHaveGeneric()"); + } + + [Test] + public void NetworkBehaviourCmdCoroutine() + { + HasError("CmdCantHaveCoroutine cannot be a coroutine", + "System.Collections.IEnumerator WeaverNetworkBehaviourTests.NetworkBehaviourCmdCoroutine.NetworkBehaviourCmdCoroutine::CmdCantHaveCoroutine()"); + } + + [Test] + public void NetworkBehaviourCmdVoidReturn() + { + HasError("CmdCantHaveNonVoidReturn cannot return a value. Make it void instead", + "System.Int32 WeaverNetworkBehaviourTests.NetworkBehaviourCmdVoidReturn.NetworkBehaviourCmdVoidReturn::CmdCantHaveNonVoidReturn()"); + } + + [Test] + public void NetworkBehaviourTargetRpcGenericParam() + { + HasError("TargetRpcCantHaveGeneric cannot have generic parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcGenericParam.NetworkBehaviourTargetRpcGenericParam::TargetRpcCantHaveGeneric()"); + } + + [Test] + public void NetworkBehaviourTargetRpcCoroutine() + { + HasError("TargetRpcCantHaveCoroutine cannot be a coroutine", + "System.Collections.IEnumerator WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcCoroutine.NetworkBehaviourTargetRpcCoroutine::TargetRpcCantHaveCoroutine()"); + } + + [Test] + public void NetworkBehaviourTargetRpcVoidReturn() + { + HasError("TargetRpcCantHaveNonVoidReturn cannot return a value. Make it void instead", + "System.Int32 WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcVoidReturn.NetworkBehaviourTargetRpcVoidReturn::TargetRpcCantHaveNonVoidReturn()"); + } + + [Test] + public void NetworkBehaviourTargetRpcParamOut() + { + HasError("TargetRpcCantHaveParamOut cannot have out parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamOut.NetworkBehaviourTargetRpcParamOut::TargetRpcCantHaveParamOut(Mirror.NetworkConnection,System.Int32&)"); + } + + [Test] + public void NetworkBehaviourTargetRpcParamOptional() + { + HasError("TargetRpcCantHaveParamOptional cannot have optional parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamOptional.NetworkBehaviourTargetRpcParamOptional::TargetRpcCantHaveParamOptional(Mirror.NetworkConnection,System.Int32)"); + } + + [Test] + public void NetworkBehaviourTargetRpcParamRef() + { + HasError("Cannot pass Int32& by reference", + "System.Int32&"); + HasError("TargetRpcCantHaveParamRef has invalid parameter monkeys", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamRef.NetworkBehaviourTargetRpcParamRef::TargetRpcCantHaveParamRef(Mirror.NetworkConnection,System.Int32&)"); + HasError("Cannot pass type Int32& by reference", + "System.Int32&"); + HasError("TargetRpcCantHaveParamRef has invalid parameter monkeys. Unsupported type System.Int32&, use a supported Mirror type instead", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamRef.NetworkBehaviourTargetRpcParamRef::TargetRpcCantHaveParamRef(Mirror.NetworkConnection,System.Int32&)"); + } + + [Test] + public void NetworkBehaviourTargetRpcParamAbstract() + { + HasError("Cannot generate writer for abstract class AbstractClass. Use a supported type or provide a custom writer", + "WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamAbstract.NetworkBehaviourTargetRpcParamAbstract/AbstractClass"); + // TODO change weaver to run checks for write/read at the same time + //HasError("AbstractClass can't be deserialized because it has no default constructor", + // "WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamAbstract.NetworkBehaviourTargetRpcParamAbstract/AbstractClass"); + } + + [Test] + public void NetworkBehaviourTargetRpcParamComponent() + { + HasError("Cannot generate writer for component type ComponentClass. Use a supported type or provide a custom writer", + "WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent.NetworkBehaviourTargetRpcParamComponent/ComponentClass"); + HasError("TargetRpcCantHaveParamComponent has invalid parameter monkeyComp", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent.NetworkBehaviourTargetRpcParamComponent::TargetRpcCantHaveParamComponent(Mirror.NetworkConnection,WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent.NetworkBehaviourTargetRpcParamComponent/ComponentClass)"); + HasError("Cannot generate reader for component type ComponentClass. Use a supported type or provide a custom reader", + "WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent.NetworkBehaviourTargetRpcParamComponent/ComponentClass"); + HasError("TargetRpcCantHaveParamComponent has invalid parameter monkeyComp. Unsupported type WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent.NetworkBehaviourTargetRpcParamComponent/ComponentClass, use a supported Mirror type instead", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent.NetworkBehaviourTargetRpcParamComponent::TargetRpcCantHaveParamComponent(Mirror.NetworkConnection,WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent.NetworkBehaviourTargetRpcParamComponent/ComponentClass)"); + } + + [Test] + public void NetworkBehaviourClientRpcGenericParam() + { + HasError("RpcCantHaveGeneric cannot have generic parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcGenericParam.NetworkBehaviourClientRpcGenericParam::RpcCantHaveGeneric()"); + } + + [Test] + public void NetworkBehaviourClientRpcCoroutine() + { + HasError("RpcCantHaveCoroutine cannot be a coroutine", + "System.Collections.IEnumerator WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcCoroutine.NetworkBehaviourClientRpcCoroutine::RpcCantHaveCoroutine()"); + } + + [Test] + public void NetworkBehaviourClientRpcVoidReturn() + { + HasError("RpcCantHaveNonVoidReturn cannot return a value. Make it void instead", + "System.Int32 WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcVoidReturn.NetworkBehaviourClientRpcVoidReturn::RpcCantHaveNonVoidReturn()"); + } + + [Test] + public void NetworkBehaviourClientRpcParamOut() + { + HasError("RpcCantHaveParamOut cannot have out parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamOut.NetworkBehaviourClientRpcParamOut::RpcCantHaveParamOut(System.Int32&)"); + } + + [Test] + public void NetworkBehaviourClientRpcParamOptional() + { + HasError("RpcCantHaveParamOptional cannot have optional parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamOptional.NetworkBehaviourClientRpcParamOptional::RpcCantHaveParamOptional(System.Int32)"); + } + + [Test] + public void NetworkBehaviourClientRpcParamRef() + { + HasError("Cannot pass Int32& by reference", + "System.Int32&"); + HasError("RpcCantHaveParamRef has invalid parameter monkeys", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamRef.NetworkBehaviourClientRpcParamRef::RpcCantHaveParamRef(System.Int32&)"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot pass type Int32& by reference", + // "System.Int32&"); + //HasError("RpcCantHaveParamRef has invalid parameter monkeys. Unsupported type System.Int32&, use a supported Mirror type instead", + // "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamRef.NetworkBehaviourClientRpcParamRef::RpcCantHaveParamRef(System.Int32&)"); + } + + [Test] + public void NetworkBehaviourClientRpcParamAbstract() + { + HasError("Cannot generate writer for abstract class AbstractClass. Use a supported type or provide a custom writer", + "WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamAbstract.NetworkBehaviourClientRpcParamAbstract/AbstractClass"); + // TODO change weaver to run checks for write/read at the same time + //HasError("AbstractClass can't be deserialized because it has no default constructor", + // "WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamAbstract.NetworkBehaviourClientRpcParamAbstract/AbstractClass"); + } + + [Test] + public void NetworkBehaviourClientRpcParamComponent() + { + HasError("Cannot generate writer for component type ComponentClass. Use a supported type or provide a custom writer", + "WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent.NetworkBehaviourClientRpcParamComponent/ComponentClass"); + HasError("RpcCantHaveParamComponent has invalid parameter monkeyComp", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent.NetworkBehaviourClientRpcParamComponent::RpcCantHaveParamComponent(WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent.NetworkBehaviourClientRpcParamComponent/ComponentClass)"); + // TODO change weaver to run checks for write/read at the same time + //HasError("Cannot generate reader for component type ComponentClass. Use a supported type or provide a custom reader", + // "WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent.NetworkBehaviourClientRpcParamComponent/ComponentClass"); + //HasError("RpcCantHaveParamComponent has invalid parameter monkeyComp. Unsupported type WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent.NetworkBehaviourClientRpcParamComponent/ComponentClass, use a supported Mirror type instead", + // "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent.NetworkBehaviourClientRpcParamComponent::RpcCantHaveParamComponent(WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent.NetworkBehaviourClientRpcParamComponent/ComponentClass)"); + } + + [Test] + public void NetworkBehaviourClientRpcParamNetworkConnection() + { + HasError("RpcCantHaveParamOptional has invalid parameter monkeyCon. Cannot pass NetworkConnections", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamNetworkConnection.NetworkBehaviourClientRpcParamNetworkConnection::RpcCantHaveParamOptional(Mirror.NetworkConnection)"); + } + + [Test] + public void NetworkBehaviourCmdParamOut() + { + HasError("CmdCantHaveParamOut cannot have out parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamOut.NetworkBehaviourCmdParamOut::CmdCantHaveParamOut(System.Int32&)"); + } + + [Test] + public void NetworkBehaviourCmdParamOptional() + { + HasError("CmdCantHaveParamOptional cannot have optional parameters", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamOptional.NetworkBehaviourCmdParamOptional::CmdCantHaveParamOptional(System.Int32)"); + } + + [Test] + public void NetworkBehaviourCmdParamRef() + { + HasError("Cannot pass Int32& by reference", + "System.Int32&"); + HasError("CmdCantHaveParamRef has invalid parameter monkeys", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamRef.NetworkBehaviourCmdParamRef::CmdCantHaveParamRef(System.Int32&)"); + HasError("Cannot pass type Int32& by reference", + "System.Int32&"); + HasError("CmdCantHaveParamRef has invalid parameter monkeys. Unsupported type System.Int32&, use a supported Mirror type instead", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamRef.NetworkBehaviourCmdParamRef::CmdCantHaveParamRef(System.Int32&)"); + } + + [Test] + public void NetworkBehaviourCmdParamAbstract() + { + HasError("Cannot generate writer for abstract class AbstractClass. Use a supported type or provide a custom writer", + "WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamAbstract.NetworkBehaviourCmdParamAbstract/AbstractClass"); + // TODO change weaver to run checks for write/read at the same time + //HasError("AbstractClass can't be deserialized because it has no default constructor", + // "WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamAbstract.NetworkBehaviourCmdParamAbstract/AbstractClass"); + } + + [Test] + public void NetworkBehaviourCmdParamComponent() + { + HasError("Cannot generate writer for component type ComponentClass. Use a supported type or provide a custom writer", + "WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent.NetworkBehaviourCmdParamComponent/ComponentClass"); + HasError("CmdCantHaveParamComponent has invalid parameter monkeyComp", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent.NetworkBehaviourCmdParamComponent::CmdCantHaveParamComponent(WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent.NetworkBehaviourCmdParamComponent/ComponentClass)"); + HasError("Cannot generate reader for component type ComponentClass. Use a supported type or provide a custom reader", + "WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent.NetworkBehaviourCmdParamComponent/ComponentClass"); + HasError("CmdCantHaveParamComponent has invalid parameter monkeyComp. Unsupported type WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent.NetworkBehaviourCmdParamComponent/ComponentClass, use a supported Mirror type instead", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent.NetworkBehaviourCmdParamComponent::CmdCantHaveParamComponent(WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent.NetworkBehaviourCmdParamComponent/ComponentClass)"); + } + + [Test] + public void NetworkBehaviourCmdParamNetworkConnection() + { + HasError("CmdCantHaveParamOptional has invalid parameter monkeyCon, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", + "System.Void WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamNetworkConnection.NetworkBehaviourCmdParamNetworkConnection::CmdCantHaveParamOptional(Mirror.NetworkConnection)"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests.cs.meta new file mode 100644 index 000000000..db360898f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f8a452672efda084f8743b29ae9592f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid.meta new file mode 100644 index 000000000..f2050a176 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e8c0f412bf9048d7a9992621f6a0d8de +timeCreated: 1643082894 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourAbstractBaseValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourAbstractBaseValid.cs new file mode 100644 index 000000000..624e3fe44 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourAbstractBaseValid.cs @@ -0,0 +1,18 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourAbstractBaseValid +{ + public abstract class EntityBase : NetworkBehaviour { } + + public class EntityConcrete : EntityBase + { + [SyncVar] + public int abstractDerivedSync; + } + + public class NetworkBehaviourAbstractBaseValid : EntityConcrete + { + [SyncVar] + public int concreteDerivedSync; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourAbstractBaseValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourAbstractBaseValid.cs.meta new file mode 100644 index 000000000..8221d53cb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourAbstractBaseValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1f37afe8878840bd9369afa4d017ba8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourClientRpcDuplicateName.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourClientRpcDuplicateName.cs new file mode 100644 index 000000000..17ef3f9eb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourClientRpcDuplicateName.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcDuplicateName +{ + class NetworkBehaviourClientRpcDuplicateName : NetworkBehaviour + { + // remote call overloads are now supported + [ClientRpc] + public void RpcWithSameName(int abc) {} + + [ClientRpc] + public void RpcWithSameName(int abc, int def) {} + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourClientRpcDuplicateName.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourClientRpcDuplicateName.cs.meta new file mode 100644 index 000000000..aa12a0f8b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourClientRpcDuplicateName.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47702bfe436d8462bb5b937eb08ea120 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcDuplicateName.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcDuplicateName.cs new file mode 100644 index 000000000..f69bb65e5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcDuplicateName.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcDuplicateName +{ + class NetworkBehaviourTargetRpcDuplicateName : NetworkBehaviour + { + // remote call overloads are now supported + [TargetRpc] + public void TargetRpcWithSameName(NetworkConnection monkeyCon, int abc) { } + + [TargetRpc] + public void TargetRpcWithSameName(NetworkConnection monkeyCon, int abc, int def) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcDuplicateName.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcDuplicateName.cs.meta new file mode 100644 index 000000000..1597143f9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcDuplicateName.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b920ec9538e4b42cda6116d5d6238b6b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcParamNetworkConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcParamNetworkConnection.cs new file mode 100644 index 000000000..9b5559176 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcParamNetworkConnection.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamNetworkConnection +{ + class NetworkBehaviourTargetRpcParamNetworkConnection : NetworkBehaviour + { + [TargetRpc] + public void TargetRpcCantHaveParamOptional(NetworkConnection monkeyCon) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcParamNetworkConnection.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcParamNetworkConnection.cs.meta new file mode 100644 index 000000000..d61ef2a66 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourTargetRpcParamNetworkConnection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a608f5852eaf44fba9f1f855abc2a3ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourValid.cs new file mode 100644 index 000000000..bb9f6c975 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourValid.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourValid +{ + class MirrorTestPlayer : NetworkBehaviour + { + [SyncVar] + public int durpatron9000 = 12; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourValid.cs.meta new file mode 100644 index 000000000..db4c8619d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests_IsValid/NetworkBehaviourValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b7fdf376724a439f9524301a442e6f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcCoroutine.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcCoroutine.cs new file mode 100644 index 000000000..0434b94fe --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcCoroutine.cs @@ -0,0 +1,14 @@ +using System.Collections; +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcCoroutine +{ + class NetworkBehaviourClientRpcCoroutine : NetworkBehaviour + { + [ClientRpc] + public IEnumerator RpcCantHaveCoroutine() + { + yield return null; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcGenericParam.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcGenericParam.cs new file mode 100644 index 000000000..5cef7dc41 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcGenericParam.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcGenericParam +{ + class NetworkBehaviourClientRpcGenericParam : NetworkBehaviour + { + [ClientRpc] + public void RpcCantHaveGeneric() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamAbstract.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamAbstract.cs new file mode 100644 index 000000000..a5c51de73 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamAbstract.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamAbstract +{ + class NetworkBehaviourClientRpcParamAbstract : NetworkBehaviour + { + public abstract class AbstractClass + { + int monkeys = 12; + } + + [ClientRpc] + public void RpcCantHaveParamAbstract(AbstractClass monkeys) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamComponent.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamComponent.cs new file mode 100644 index 000000000..1208e3822 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamComponent.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamComponent +{ + class NetworkBehaviourClientRpcParamComponent : NetworkBehaviour + { + public class ComponentClass : UnityEngine.Component + { + int monkeys = 12; + } + + [ClientRpc] + public void RpcCantHaveParamComponent(ComponentClass monkeyComp) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamNetworkConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamNetworkConnection.cs new file mode 100644 index 000000000..912c2f2ee --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamNetworkConnection.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamNetworkConnection +{ + class NetworkBehaviourClientRpcParamNetworkConnection : NetworkBehaviour + { + [ClientRpc] + public void RpcCantHaveParamOptional(NetworkConnection monkeyCon) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamOptional.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamOptional.cs new file mode 100644 index 000000000..03215101c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamOptional.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamOptional +{ + class NetworkBehaviourClientRpcParamOptional : NetworkBehaviour + { + [ClientRpc] + public void RpcCantHaveParamOptional(int monkeys = 12) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamOut.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamOut.cs new file mode 100644 index 000000000..7225adb0b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamOut.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamOut +{ + class NetworkBehaviourClientRpcParamOut : NetworkBehaviour + { + [ClientRpc] + public void RpcCantHaveParamOut(out int monkeys) + { + monkeys = 12; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamRef.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamRef.cs new file mode 100644 index 000000000..3de256360 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcParamRef.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcParamRef +{ + class NetworkBehaviourClientRpcParamRef : NetworkBehaviour + { + [ClientRpc] + public void RpcCantHaveParamRef(ref int monkeys) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcVoidReturn.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcVoidReturn.cs new file mode 100644 index 000000000..75d31847a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourClientRpcVoidReturn.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourClientRpcVoidReturn +{ + class NetworkBehaviourClientRpcVoidReturn : NetworkBehaviour + { + [ClientRpc] + public int RpcCantHaveNonVoidReturn() + { + return 1; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdCoroutine.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdCoroutine.cs new file mode 100644 index 000000000..39bf66076 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdCoroutine.cs @@ -0,0 +1,14 @@ +using System.Collections; +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdCoroutine +{ + class NetworkBehaviourCmdCoroutine : NetworkBehaviour + { + [Command] + public IEnumerator CmdCantHaveCoroutine() + { + yield return null; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdDuplicateName.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdDuplicateName.cs new file mode 100644 index 000000000..acb8c78c3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdDuplicateName.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdDuplicateName +{ + class NetworkBehaviourCmdDuplicateName : NetworkBehaviour + { + // remote call overloads are now supported + [Command] + public void CmdWithSameName(int abc) { } + + [Command] + public void CmdWithSameName(int abc, int def) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdGenericParam.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdGenericParam.cs new file mode 100644 index 000000000..2e10933f5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdGenericParam.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdGenericParam +{ + class NetworkBehaviourCmdGenericParam : NetworkBehaviour + { + [Command] + public void CmdCantHaveGeneric() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamAbstract.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamAbstract.cs new file mode 100644 index 000000000..d6bac6044 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamAbstract.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamAbstract +{ + class NetworkBehaviourCmdParamAbstract : NetworkBehaviour + { + public abstract class AbstractClass + { + int monkeys = 12; + } + + [Command] + public void CmdCantHaveParamAbstract(AbstractClass monkeys) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamComponent.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamComponent.cs new file mode 100644 index 000000000..e39f8a313 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamComponent.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamComponent +{ + class NetworkBehaviourCmdParamComponent : NetworkBehaviour + { + public class ComponentClass : UnityEngine.Component + { + int monkeys = 12; + } + + [Command] + public void CmdCantHaveParamComponent(ComponentClass monkeyComp) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamNetworkConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamNetworkConnection.cs new file mode 100644 index 000000000..9ad198a1b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamNetworkConnection.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamNetworkConnection +{ + class NetworkBehaviourCmdParamNetworkConnection : NetworkBehaviour + { + [Command] + public void CmdCantHaveParamOptional(NetworkConnection monkeyCon) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamOptional.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamOptional.cs new file mode 100644 index 000000000..509715d2a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamOptional.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamOptional +{ + class NetworkBehaviourCmdParamOptional : NetworkBehaviour + { + [Command] + public void CmdCantHaveParamOptional(int monkeys = 12) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamOut.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamOut.cs new file mode 100644 index 000000000..2999339fe --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamOut.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamOut +{ + class NetworkBehaviourCmdParamOut : NetworkBehaviour + { + [Command] + public void CmdCantHaveParamOut(out int monkeys) + { + monkeys = 12; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamRef.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamRef.cs new file mode 100644 index 000000000..54fd0d0e4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdParamRef.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdParamRef +{ + class NetworkBehaviourCmdParamRef : NetworkBehaviour + { + [Command] + public void CmdCantHaveParamRef(ref int monkeys) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdVoidReturn.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdVoidReturn.cs new file mode 100644 index 000000000..1015efdee --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourCmdVoidReturn.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourCmdVoidReturn +{ + class NetworkBehaviourCmdVoidReturn : NetworkBehaviour + { + [Command] + public int CmdCantHaveNonVoidReturn() + { + return 1; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourGeneric.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourGeneric.cs new file mode 100644 index 000000000..c6deba0bb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourGeneric.cs @@ -0,0 +1,9 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourGeneric +{ + class NetworkBehaviourGeneric : NetworkBehaviour + { + T genericsNotAllowed; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcCoroutine.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcCoroutine.cs new file mode 100644 index 000000000..dbd8c13fe --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcCoroutine.cs @@ -0,0 +1,14 @@ +using System.Collections; +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcCoroutine +{ + class NetworkBehaviourTargetRpcCoroutine : NetworkBehaviour + { + [TargetRpc] + public IEnumerator TargetRpcCantHaveCoroutine() + { + yield return null; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcGenericParam.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcGenericParam.cs new file mode 100644 index 000000000..c37431d67 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcGenericParam.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcGenericParam +{ + class NetworkBehaviourTargetRpcGenericParam : NetworkBehaviour + { + [TargetRpc] + public void TargetRpcCantHaveGeneric() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamAbstract.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamAbstract.cs new file mode 100644 index 000000000..fb8938fb3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamAbstract.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamAbstract +{ + class NetworkBehaviourTargetRpcParamAbstract : NetworkBehaviour + { + public abstract class AbstractClass + { + int monkeys = 12; + } + + [TargetRpc] + public void TargetRpcCantHaveParamAbstract(NetworkConnection monkeyCon, AbstractClass monkeys) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamComponent.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamComponent.cs new file mode 100644 index 000000000..b2287761d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamComponent.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamComponent +{ + class NetworkBehaviourTargetRpcParamComponent : NetworkBehaviour + { + public class ComponentClass : UnityEngine.Component + { + int monkeys = 12; + } + + [TargetRpc] + public void TargetRpcCantHaveParamComponent(NetworkConnection monkeyCon, ComponentClass monkeyComp) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamNetworkConnectionNotFirst.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamNetworkConnectionNotFirst.cs new file mode 100644 index 000000000..38466a3b8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamNetworkConnectionNotFirst.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamNetworkConnectionNotFirst +{ + class NetworkBehaviourTargetRpcParamNetworkConnectionNotFirst : NetworkBehaviour + { + [TargetRpc] + public void TargetRpcCantHaveParamOptional(int abc, NetworkConnection monkeyCon) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamOptional.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamOptional.cs new file mode 100644 index 000000000..ea4655c52 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamOptional.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamOptional +{ + class NetworkBehaviourTargetRpcParamOptional : NetworkBehaviour + { + [TargetRpc] + public void TargetRpcCantHaveParamOptional(NetworkConnection monkeyCon, int monkeys = 12) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamOut.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamOut.cs new file mode 100644 index 000000000..0892aa1a7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamOut.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamOut +{ + class NetworkBehaviourTargetRpcParamOut : NetworkBehaviour + { + [TargetRpc] + public void TargetRpcCantHaveParamOut(NetworkConnection monkeyCon, out int monkeys) + { + monkeys = 12; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamRef.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamRef.cs new file mode 100644 index 000000000..3279878a0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcParamRef.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcParamRef +{ + class NetworkBehaviourTargetRpcParamRef : NetworkBehaviour + { + [TargetRpc] + public void TargetRpcCantHaveParamRef(NetworkConnection monkeyCon, ref int monkeys) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcVoidReturn.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcVoidReturn.cs new file mode 100644 index 000000000..e44356aa1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverNetworkBehaviourTests~/NetworkBehaviourTargetRpcVoidReturn.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverNetworkBehaviourTests.NetworkBehaviourTargetRpcVoidReturn +{ + class NetworkBehaviourTargetRpcVoidReturn : NetworkBehaviour + { + [TargetRpc] + public int TargetRpcCantHaveNonVoidReturn() + { + return 1; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests.cs new file mode 100644 index 000000000..e7f6cd7c4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests.cs @@ -0,0 +1,26 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + // Some tests for SyncObjects are in WeaverSyncListTests and apply to SyncDictionary too + public class WeaverSyncDictionaryTests : WeaverTestsBuildFromTestName + { + [Test] + public void SyncDictionaryErrorForGenericStructKey() + { + HasError("Cannot generate reader for generic variable MyGenericStruct`1. Use a supported type or provide a custom reader", + "WeaverSyncDictionaryTests.SyncDictionaryErrorForGenericStructKey.SyncDictionaryErrorForGenericStructKey/MyGenericStruct`1"); + HasError("Cannot generate writer for generic type MyGenericStruct`1. Use a supported type or provide a custom writer", + "WeaverSyncDictionaryTests.SyncDictionaryErrorForGenericStructKey.SyncDictionaryErrorForGenericStructKey/MyGenericStruct`1"); + } + + [Test] + public void SyncDictionaryErrorForGenericStructItem() + { + HasError("Cannot generate reader for generic variable MyGenericStruct`1. Use a supported type or provide a custom reader", + "WeaverSyncDictionaryTests.SyncDictionaryErrorForGenericStructItem.SyncDictionaryErrorForGenericStructItem/MyGenericStruct`1"); + HasError("Cannot generate writer for generic type MyGenericStruct`1. Use a supported type or provide a custom writer", + "WeaverSyncDictionaryTests.SyncDictionaryErrorForGenericStructItem.SyncDictionaryErrorForGenericStructItem/MyGenericStruct`1"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests.cs.meta new file mode 100644 index 000000000..a96f8c071 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de75571c46d27c34bb2c6a67d5eac558 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess.meta new file mode 100644 index 000000000..e47cffdeb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 213434594cfc42e6a93f05a7d16f15fd +timeCreated: 1643083057 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/GenericSyncDictionaryCanBeUsed.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/GenericSyncDictionaryCanBeUsed.cs new file mode 100644 index 000000000..1b85dd439 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/GenericSyncDictionaryCanBeUsed.cs @@ -0,0 +1,11 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.GenericSyncDictionaryCanBeUsed +{ + class GenericSyncDictionaryCanBeUsed : NetworkBehaviour + { + readonly SomeSyncDictionary someDictionary; + + public class SomeSyncDictionary : SyncDictionary { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/GenericSyncDictionaryCanBeUsed.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/GenericSyncDictionaryCanBeUsed.cs.meta new file mode 100644 index 000000000..16e37dfaf --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/GenericSyncDictionaryCanBeUsed.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b80f4132d92cf42dfbf75cad97d84513 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionary.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionary.cs new file mode 100644 index 000000000..0c1e03177 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionary.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionary +{ + class SyncDictionaryValid : NetworkBehaviour + { + public class SyncDictionaryIntString : SyncDictionary { } + + public readonly SyncDictionaryIntString Foo; + } + + +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionary.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionary.cs.meta new file mode 100644 index 000000000..32b959aba --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b5d1ec150fb64234b3cdbae16ebb12f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericAbstractInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericAbstractInheritance.cs new file mode 100644 index 000000000..fd62e9643 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericAbstractInheritance.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryGenericAbstractInheritance +{ + class SyncDictionaryGenericAbstractInheritance : NetworkBehaviour + { + readonly SomeDictionaryIntString dictionary = new SomeDictionaryIntString(); + + public abstract class SomeDictionary : SyncDictionary { } + + public class SomeDictionaryIntString : SomeDictionary { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericAbstractInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericAbstractInheritance.cs.meta new file mode 100644 index 000000000..ac85cbc65 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericAbstractInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e951e9979768745aeb9a6dcf260dd20c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericInheritance.cs new file mode 100644 index 000000000..47778b85a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericInheritance.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryGenericInheritance +{ + class SyncDictionaryGenericInheritance : NetworkBehaviour + { + readonly SomeDictionaryIntString dictionary = new SomeDictionaryIntString(); + + public class SomeDictionary : SyncDictionary { } + + public class SomeDictionaryIntString : SomeDictionary { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericInheritance.cs.meta new file mode 100644 index 000000000..617a87018 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b7161b20ae9041b6a5dc17074967813 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructItemWithCustomMethods.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructItemWithCustomMethods.cs new file mode 100644 index 000000000..049ee8f83 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructItemWithCustomMethods.cs @@ -0,0 +1,27 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryGenericStructItemWithCustomMethods +{ + class SyncDictionaryGenericStructItemWithCustomMethods : NetworkBehaviour + { + readonly SyncDictionary> harpseals; + } + + public struct MyGenericStruct + { + public T genericpotato; + } + + public static class MyGenericStructDictionary + { + public static void WriteItem(this NetworkWriter writer, MyGenericStruct item) + { + writer.WriteFloat(item.genericpotato); + } + + public static MyGenericStruct ReadItem(this NetworkReader reader) + { + return new MyGenericStruct() { genericpotato = reader.ReadFloat() }; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructItemWithCustomMethods.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructItemWithCustomMethods.cs.meta new file mode 100644 index 000000000..b6db60497 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructItemWithCustomMethods.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4eb89b9d2c0db4ebd9e6f8f4bc5d4f24 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructKeyWithCustomMethods.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructKeyWithCustomMethods.cs new file mode 100644 index 000000000..a5942f22e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructKeyWithCustomMethods.cs @@ -0,0 +1,27 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryGenericStructKeyWithCustomMethods +{ + class SyncDictionaryGenericStructKeyWithCustomMethods : NetworkBehaviour + { + readonly SyncDictionary, int> harpseals; + } + + public struct MyGenericStruct + { + public T genericpotato; + } + + public static class MyGenericStructDictionary + { + public static void WriteKey(this NetworkWriter writer, MyGenericStruct item) + { + writer.WriteFloat(item.genericpotato); + } + + public static MyGenericStruct ReadKey(this NetworkReader reader) + { + return new MyGenericStruct() { genericpotato = reader.ReadFloat() }; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructKeyWithCustomMethods.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructKeyWithCustomMethods.cs.meta new file mode 100644 index 000000000..6e5e17258 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryGenericStructKeyWithCustomMethods.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce03c1e3290a94e2390a4bf1ec2982d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryInheritance.cs new file mode 100644 index 000000000..6d2f207fe --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryInheritance.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryInheritance +{ + class SyncDictionaryInheritance : NetworkBehaviour + { + readonly SuperDictionary dictionary = new SuperDictionary(); + + public class SomeDictionary : SyncDictionary { } + + public class SomeDictionaryIntString : SomeDictionary { } + + public class SuperDictionary : SomeDictionaryIntString + { + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryInheritance.cs.meta new file mode 100644 index 000000000..8ff9429c3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1cdebedceef1e4dbb8438b4d9ef22d01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructItem.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructItem.cs new file mode 100644 index 000000000..8a63b53fe --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructItem.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryStructItem +{ + class SyncDictionaryStructItem : NetworkBehaviour + { + readonly MyStructDictionary Foo; + + struct MyStruct + { + public int potato; + public float floatingpotato; + public double givemetwopotatoes; + } + class MyStructDictionary : SyncDictionary { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructItem.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructItem.cs.meta new file mode 100644 index 000000000..840061b39 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d83c6882b2bef4a37a9592f6895a14f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructKey.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructKey.cs new file mode 100644 index 000000000..7476a228d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructKey.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryStructKey +{ + class SyncDictionaryStructKey : NetworkBehaviour + { + readonly MyStructDictionary Foo; + + struct MyStruct + { + public int potato; + public float floatingpotato; + public double givemetwopotatoes; + } + class MyStructDictionary : SyncDictionary { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructKey.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructKey.cs.meta new file mode 100644 index 000000000..e23b74cc3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests_IsSuccess/SyncDictionaryStructKey.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1923cdb541a94e9dad0b19cfcd53ea4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests~/SyncDictionaryErrorForGenericStructItem.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests~/SyncDictionaryErrorForGenericStructItem.cs new file mode 100644 index 000000000..75e80b81d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests~/SyncDictionaryErrorForGenericStructItem.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryErrorForGenericStructItem +{ + class SyncDictionaryErrorForGenericStructItem : NetworkBehaviour + { + struct MyGenericStruct + { + T genericpotato; + } + + class MyGenericStructDictionary : SyncDictionary> { }; + + readonly MyGenericStructDictionary harpseals; + } + +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests~/SyncDictionaryErrorForGenericStructKey.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests~/SyncDictionaryErrorForGenericStructKey.cs new file mode 100644 index 000000000..9b3b4bfd9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncDictionaryTests~/SyncDictionaryErrorForGenericStructKey.cs @@ -0,0 +1,16 @@ +using Mirror; + +namespace WeaverSyncDictionaryTests.SyncDictionaryErrorForGenericStructKey +{ + class SyncDictionaryErrorForGenericStructKey : NetworkBehaviour + { + readonly MyGenericStructDictionary harpseals; + + struct MyGenericStruct + { + T genericpotato; + } + + class MyGenericStructDictionary : SyncDictionary, int> { }; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests.cs new file mode 100644 index 000000000..d6326e6c1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverSyncListTests : WeaverTestsBuildFromTestName + { + [Test] + public void SyncListNestedInAbstractClassWithInvalid() + { + // we need this negative test to make sure that SyncList is being processed + HasError("Cannot generate reader for Object. Use a supported type or provide a custom reader", + "UnityEngine.Object"); + HasError("target has an unsupported type", + "UnityEngine.Object WeaverSyncListTests.SyncListNestedInAbstractClassWithInvalid.SyncListNestedStructWithInvalid/SomeAbstractClass/MyNestedStruct::target"); + HasError("Cannot generate writer for Object. Use a supported type or provide a custom writer", + "UnityEngine.Object"); + } + + [Test] + public void SyncListNestedInStructWithInvalid() + { + // we need this negative test to make sure that SyncList is being processed + HasError("Cannot generate reader for Object. Use a supported type or provide a custom reader", + "UnityEngine.Object"); + HasError("target has an unsupported type", + "UnityEngine.Object WeaverSyncListTests.SyncListNestedInStructWithInvalid.SyncListNestedInStructWithInvalid/SomeData::target"); + HasError("Cannot generate writer for Object. Use a supported type or provide a custom writer", + "UnityEngine.Object"); + } + + [Test] + public void SyncListErrorForGenericStruct() + { + HasError("Cannot generate reader for generic variable MyGenericStruct`1. Use a supported type or provide a custom reader", + "WeaverSyncListTests.SyncListErrorForGenericStruct.SyncListErrorForGenericStruct/MyGenericStruct`1"); + HasError("Cannot generate writer for generic type MyGenericStruct`1. Use a supported type or provide a custom writer", + "WeaverSyncListTests.SyncListErrorForGenericStruct.SyncListErrorForGenericStruct/MyGenericStruct`1"); + } + + // IsSuccess test, but still in here because it shows an error + // if we move to regular C# + [Test] + public void SyncListGenericStructWithCustomMethods() + { + IsSuccess(); + } + + [Test] + public void SyncListErrorForInterface() + { + HasError("Cannot generate reader for interface MyInterface. Use a supported type or provide a custom reader", + "WeaverSyncListTests.SyncListErrorForInterface.MyInterface"); + HasError("Cannot generate writer for interface MyInterface. Use a supported type or provide a custom writer", + "WeaverSyncListTests.SyncListErrorForInterface.MyInterface"); + } + + // IsSuccess test, but still in here because it shows an error + // if we move to regular C# + [Test] + public void SyncListInterfaceWithCustomMethods() + { + IsSuccess(); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests.cs.meta new file mode 100644 index 000000000..4fff813e9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40a07120cf158ca4e98a01d79c7b48e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess.meta new file mode 100644 index 000000000..16fca91c4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b5dd2567fffc48f9a1ebdf53819aa513 +timeCreated: 1643083178 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/GenericSyncListCanBeUsed.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/GenericSyncListCanBeUsed.cs new file mode 100644 index 000000000..86be36c42 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/GenericSyncListCanBeUsed.cs @@ -0,0 +1,12 @@ +using Mirror; + +namespace WeaverSyncListTests.GenericSyncListCanBeUsed +{ + class GenericSyncListCanBeUsed : NetworkBehaviour + { + readonly SomeList someList; + + + public class SomeList : SyncList { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/GenericSyncListCanBeUsed.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/GenericSyncListCanBeUsed.cs.meta new file mode 100644 index 000000000..21cb0cf80 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/GenericSyncListCanBeUsed.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42f9700f4998548c498565f7b55edbb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncList.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncList.cs new file mode 100644 index 000000000..174d28803 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncList.cs @@ -0,0 +1,9 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncList +{ + class SyncList : NetworkBehaviour + { + public readonly SyncList Foo; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncList.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncList.cs.meta new file mode 100644 index 000000000..cc1414223 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2de457dfaaa35451fa002b19623ea678 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListByteValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListByteValid.cs new file mode 100644 index 000000000..c073ca875 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListByteValid.cs @@ -0,0 +1,11 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListByteValid +{ + class SyncListByteValid : NetworkBehaviour + { + class MyByteClass : SyncList { }; + + readonly MyByteClass Foo; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListByteValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListByteValid.cs.meta new file mode 100644 index 000000000..44398f4b5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListByteValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 639ccd9a8ac3e4d669b11291a9d0cf39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericAbstractInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericAbstractInheritance.cs new file mode 100644 index 000000000..8821f81aa --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericAbstractInheritance.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListGenericAbstractInheritance +{ + class SyncListGenericAbstractInheritance : NetworkBehaviour + { + readonly SomeListInt superSyncListString = new SomeListInt(); + + + public abstract class SomeList : SyncList { } + + public class SomeListInt : SomeList { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericAbstractInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericAbstractInheritance.cs.meta new file mode 100644 index 000000000..f58da9aae --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericAbstractInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac1eb2763b78a442d82c1e5c6ec06cbb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritance.cs new file mode 100644 index 000000000..5b1a56f86 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritance.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListGenericInheritance +{ + class SyncListGenericInheritance : NetworkBehaviour + { + readonly SomeListInt someList = new SomeListInt(); + + + public class SomeList : SyncList { } + + public class SomeListInt : SomeList { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritance.cs.meta new file mode 100644 index 000000000..2851a19e9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a6efc238f8074df3b8c0628c49e9b23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritanceWithMultipleGeneric.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritanceWithMultipleGeneric.cs new file mode 100644 index 000000000..c23619f51 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritanceWithMultipleGeneric.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListGenericInheritanceWithMultipleGeneric +{ + /* + This test should pass + */ + class SyncListGenericInheritanceWithMultipleGeneric : NetworkBehaviour + { + readonly SomeListInt someList = new SomeListInt(); + + + public class SomeList : SyncList { } + + public class SomeListInt : SomeList { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritanceWithMultipleGeneric.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritanceWithMultipleGeneric.cs.meta new file mode 100644 index 000000000..207808c45 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListGenericInheritanceWithMultipleGeneric.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ebef79e4b70014d799f0ccb14b544dca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListInheritance.cs new file mode 100644 index 000000000..4f92edd86 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListInheritance.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListInheritance +{ + class SyncListInheritance : NetworkBehaviour + { + readonly SuperSyncList superSyncListString = new SuperSyncList(); + + + public class SuperSyncList : SyncList + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListInheritance.cs.meta new file mode 100644 index 000000000..55a08e4da --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d98ef375fb0614946906b2f438a43d82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtor.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtor.cs new file mode 100644 index 000000000..e410c166a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtor.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListMissingParamlessCtor +{ + class SyncListMissingParamlessCtor : NetworkBehaviour + { + public readonly SyncListString2 Foo; + + public class SyncListString2 : SyncList + { + public SyncListString2(int phooey) { } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtor.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtor.cs.meta new file mode 100644 index 000000000..50fd94e17 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57b22f7b42094409a9a1158aabad83d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtorManuallyInitialized.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtorManuallyInitialized.cs new file mode 100644 index 000000000..b16cd20d1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtorManuallyInitialized.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListMissingParamlessCtorManuallyInitialized +{ + class SyncListMissingParamlessCtorManuallyInitialized : NetworkBehaviour + { + public readonly SyncListString2 Foo = new SyncListString2(20); + + public class SyncListString2 : SyncList + { + public SyncListString2(int phooey) { } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtorManuallyInitialized.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtorManuallyInitialized.cs.meta new file mode 100644 index 000000000..046be8783 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListMissingParamlessCtorManuallyInitialized.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 762689087c636418e8e261eef542dcb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInAbstractClass.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInAbstractClass.cs new file mode 100644 index 000000000..8d03d58dc --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInAbstractClass.cs @@ -0,0 +1,20 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListNestedInAbstractClass +{ + class SyncListNestedStruct : NetworkBehaviour + { + readonly SomeAbstractClass.MyNestedStructList Foo; + + public abstract class SomeAbstractClass + { + public struct MyNestedStruct + { + public int potato; + public float floatingpotato; + public double givemetwopotatoes; + } + public class MyNestedStructList : SyncList { } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInAbstractClass.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInAbstractClass.cs.meta new file mode 100644 index 000000000..1e38710ef --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInAbstractClass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47ed2f59dc03d417ab513677ae404a18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInStruct.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInStruct.cs new file mode 100644 index 000000000..e611f9ba6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInStruct.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListNestedInStruct +{ + class SyncListNestedStruct : NetworkBehaviour + { + readonly SomeData.SyncList Foo; + + public struct SomeData + { + public int usefulNumber; + public class SyncList : Mirror.SyncList { } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInStruct.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInStruct.cs.meta new file mode 100644 index 000000000..038d182de --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedInStruct.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18f2e8d5c893344259ac8b2e17cb6d3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedStruct.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedStruct.cs new file mode 100644 index 000000000..da834ad7b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedStruct.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListNestedStruct +{ + class SyncListNestedStruct : NetworkBehaviour + { + readonly MyNestedStructList Foo; + + struct MyNestedStruct + { + public int potato; + public float floatingpotato; + public double givemetwopotatoes; + } + class MyNestedStructList : SyncList { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedStruct.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedStruct.cs.meta new file mode 100644 index 000000000..9107dbc39 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListNestedStruct.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a60a4daf5b784bafadc07ffc475b596 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListStruct.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListStruct.cs new file mode 100644 index 000000000..82cd68988 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListStruct.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListStruct +{ + class SyncListStruct : NetworkBehaviour + { + readonly MyStructList Foo; + + struct MyStruct + { + public int potato; + public float floatingpotato; + public double givemetwopotatoes; + } + class MyStructList : SyncList { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListStruct.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListStruct.cs.meta new file mode 100644 index 000000000..e26869672 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests_IsSuccess/SyncListStruct.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0bfa51174087b486ebb97cf94dc702a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListErrorForGenericStruct.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListErrorForGenericStruct.cs new file mode 100644 index 000000000..3809172b4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListErrorForGenericStruct.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListErrorForGenericStruct +{ + class SyncListErrorForGenericStruct : NetworkBehaviour + { + MyGenericStructList harpseals; + + + struct MyGenericStruct + { + T genericpotato; + } + + class MyGenericStructList : SyncList> { }; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListErrorForInterface.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListErrorForInterface.cs new file mode 100644 index 000000000..2e059b324 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListErrorForInterface.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListErrorForInterface +{ + class SyncListErrorForInterface : NetworkBehaviour + { + MyInterfaceList Foo; + } + interface MyInterface + { + int someNumber { get; set; } + } + class MyInterfaceList : SyncList { } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListGenericStructWithCustomMethods.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListGenericStructWithCustomMethods.cs new file mode 100644 index 000000000..8273b4f61 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListGenericStructWithCustomMethods.cs @@ -0,0 +1,27 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListGenericStructWithCustomMethods +{ + class SyncListGenericStructWithCustomMethods : NetworkBehaviour + { + readonly SyncList> harpseals; + } + + struct MyGenericStruct + { + public T genericpotato; + } + + static class MyGenericStructList + { + static void SerializeItem(this NetworkWriter writer, MyGenericStruct item) + { + writer.WriteFloat(item.genericpotato); + } + + static MyGenericStruct DeserializeItem(this NetworkReader reader) + { + return new MyGenericStruct() { genericpotato = reader.ReadFloat() }; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListInterfaceWithCustomMethods.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListInterfaceWithCustomMethods.cs new file mode 100644 index 000000000..d94ffd9dc --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListInterfaceWithCustomMethods.cs @@ -0,0 +1,31 @@ +using Mirror; + +namespace WeaverSyncListTests.SyncListInterfaceWithCustomMethods +{ + class SyncListInterfaceWithCustomMethods : NetworkBehaviour + { + readonly SyncList Foo; + } + + interface IMyInterface + { + int someNumber { get; set; } + } + + class MyUser : IMyInterface + { + public int someNumber { get; set; } + } + + static class MyInterfaceList + { + static void SerializeItem(this NetworkWriter writer, IMyInterface item) + { + writer.WriteInt(item.someNumber); + } + static IMyInterface DeserializeItem(this NetworkReader reader) + { + return new MyUser { someNumber = reader.ReadInt() }; + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListNestedInAbstractClassWithInvalid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListNestedInAbstractClassWithInvalid.cs new file mode 100644 index 000000000..ed6566317 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListNestedInAbstractClassWithInvalid.cs @@ -0,0 +1,20 @@ +using Mirror; +using UnityEngine; + +namespace WeaverSyncListTests.SyncListNestedInAbstractClassWithInvalid +{ + class SyncListNestedStructWithInvalid : NetworkBehaviour + { + readonly SomeAbstractClass.MyNestedStructList Foo; + + public abstract class SomeAbstractClass + { + public struct MyNestedStruct + { + public int potato; + public Object target; + } + public class MyNestedStructList : SyncList { } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListNestedInStructWithInvalid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListNestedInStructWithInvalid.cs new file mode 100644 index 000000000..16c8c633e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncListTests~/SyncListNestedInStructWithInvalid.cs @@ -0,0 +1,18 @@ +using Mirror; +using UnityEngine; + +namespace WeaverSyncListTests.SyncListNestedInStructWithInvalid +{ + class SyncListNestedInStructWithInvalid : NetworkBehaviour + { + readonly SomeData.SyncList Foo; + + public struct SomeData + { + public int usefulNumber; + public Object target; + + public class SyncList : Mirror.SyncList { } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests.cs new file mode 100644 index 000000000..b9de424b0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests.cs @@ -0,0 +1,21 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverSyncObjectsTests : WeaverTestsBuildFromTestName + { + [Test] + public void SyncObjectsMoreThanMax() + { + HasError("SyncObjectsMoreThanMax has > 64 SyncObjects (SyncLists etc). Consider refactoring your class into multiple components", + "WeaverSyncObjectsTest.SyncObjectsMoreThanMax.SyncObjectsMoreThanMax"); + } + + [Test] + public void RecommendsReadonly() + { + HasWarning("list should have a 'readonly' keyword in front of the variable because Mirror.SyncObjects always need to be initialized by the Weaver.", + "Mirror.SyncList`1 WeaverSyncObjectsTest.SyncObjectsMoreThanMax.RecommendsReadonly::list"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests.cs.meta new file mode 100644 index 000000000..dfafec92d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1aef13e7dbbc42df816347e4fb1ae8fd +timeCreated: 1631798559 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess.meta new file mode 100644 index 000000000..af83855bc --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b6889a5a83d34ab1b7cec95d6d437efd +timeCreated: 1643083530 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess/SyncObjectsExactlyMax.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess/SyncObjectsExactlyMax.cs new file mode 100644 index 000000000..a7b5e7cc5 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess/SyncObjectsExactlyMax.cs @@ -0,0 +1,80 @@ +using Mirror; + +namespace WeaverSyncObjectsTest.SyncObjectsExactlyMax +{ + // SyncObjects mask is 64 bit. exactly 64 SyncObjects needs to work. + public class SyncObjectsExactlyMax : NetworkBehaviour + { + // from 1..64 + public readonly SyncList list1 = new SyncList(); + public readonly SyncList list2 = new SyncList(); + public readonly SyncList list3 = new SyncList(); + public readonly SyncList list4 = new SyncList(); + public readonly SyncList list5 = new SyncList(); + public readonly SyncList list6 = new SyncList(); + public readonly SyncList list7 = new SyncList(); + public readonly SyncList list8 = new SyncList(); + public readonly SyncList list9 = new SyncList(); + + public readonly SyncList list10 = new SyncList(); + public readonly SyncList list11 = new SyncList(); + public readonly SyncList list12 = new SyncList(); + public readonly SyncList list13 = new SyncList(); + public readonly SyncList list14 = new SyncList(); + public readonly SyncList list15 = new SyncList(); + public readonly SyncList list16 = new SyncList(); + public readonly SyncList list17 = new SyncList(); + public readonly SyncList list18 = new SyncList(); + public readonly SyncList list19 = new SyncList(); + + public readonly SyncList list20 = new SyncList(); + public readonly SyncList list21 = new SyncList(); + public readonly SyncList list22 = new SyncList(); + public readonly SyncList list23 = new SyncList(); + public readonly SyncList list24 = new SyncList(); + public readonly SyncList list25 = new SyncList(); + public readonly SyncList list26 = new SyncList(); + public readonly SyncList list27 = new SyncList(); + public readonly SyncList list28 = new SyncList(); + public readonly SyncList list29 = new SyncList(); + + public readonly SyncList list30 = new SyncList(); + public readonly SyncList list31 = new SyncList(); + public readonly SyncList list32 = new SyncList(); + public readonly SyncList list33 = new SyncList(); + public readonly SyncList list34 = new SyncList(); + public readonly SyncList list35 = new SyncList(); + public readonly SyncList list36 = new SyncList(); + public readonly SyncList list37 = new SyncList(); + public readonly SyncList list38 = new SyncList(); + public readonly SyncList list39 = new SyncList(); + + public readonly SyncList list40 = new SyncList(); + public readonly SyncList list41 = new SyncList(); + public readonly SyncList list42 = new SyncList(); + public readonly SyncList list43 = new SyncList(); + public readonly SyncList list44 = new SyncList(); + public readonly SyncList list45 = new SyncList(); + public readonly SyncList list46 = new SyncList(); + public readonly SyncList list47 = new SyncList(); + public readonly SyncList list48 = new SyncList(); + public readonly SyncList list49 = new SyncList(); + + public readonly SyncList list50 = new SyncList(); + public readonly SyncList list51 = new SyncList(); + public readonly SyncList list52 = new SyncList(); + public readonly SyncList list53 = new SyncList(); + public readonly SyncList list54 = new SyncList(); + public readonly SyncList list55 = new SyncList(); + public readonly SyncList list56 = new SyncList(); + public readonly SyncList list57 = new SyncList(); + public readonly SyncList list58 = new SyncList(); + public readonly SyncList list59 = new SyncList(); + + public readonly SyncList list60 = new SyncList(); + public readonly SyncList list61 = new SyncList(); + public readonly SyncList list62 = new SyncList(); + public readonly SyncList list63 = new SyncList(); + public readonly SyncList list64 = new SyncList(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess/SyncObjectsExactlyMax.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess/SyncObjectsExactlyMax.cs.meta new file mode 100644 index 000000000..85d7b4e00 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests_IsSuccess/SyncObjectsExactlyMax.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9013959e50bc44e9b2b691b85f61bd55 +timeCreated: 1631799264 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/RecommendsReadonly.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/RecommendsReadonly.cs new file mode 100644 index 000000000..477dd9ed7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/RecommendsReadonly.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverSyncObjectsTest.SyncObjectsMoreThanMax +{ + public class RecommendsReadonly : NetworkBehaviour + { + // NOT readonly. should show weaver recommendation. + public SyncList list = new SyncList(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/RecommendsReadonly.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/RecommendsReadonly.cs.meta new file mode 100644 index 000000000..be1b1d59b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/RecommendsReadonly.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8d7133c3dad54d3dbe76f2f8be0803ec +timeCreated: 1632459283 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/SyncObjectsMoreThanMax.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/SyncObjectsMoreThanMax.cs new file mode 100644 index 000000000..7e2093b0d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/SyncObjectsMoreThanMax.cs @@ -0,0 +1,81 @@ +using Mirror; + +namespace WeaverSyncObjectsTest.SyncObjectsMoreThanMax +{ + // SyncObjects mask is 64 bit. we can't have more than 64 SyncObjects. + public class SyncObjectsMoreThanMax : NetworkBehaviour + { + // from 1..65 + public readonly SyncList list1 = new SyncList(); + public readonly SyncList list2 = new SyncList(); + public readonly SyncList list3 = new SyncList(); + public readonly SyncList list4 = new SyncList(); + public readonly SyncList list5 = new SyncList(); + public readonly SyncList list6 = new SyncList(); + public readonly SyncList list7 = new SyncList(); + public readonly SyncList list8 = new SyncList(); + public readonly SyncList list9 = new SyncList(); + + public readonly SyncList list10 = new SyncList(); + public readonly SyncList list11 = new SyncList(); + public readonly SyncList list12 = new SyncList(); + public readonly SyncList list13 = new SyncList(); + public readonly SyncList list14 = new SyncList(); + public readonly SyncList list15 = new SyncList(); + public readonly SyncList list16 = new SyncList(); + public readonly SyncList list17 = new SyncList(); + public readonly SyncList list18 = new SyncList(); + public readonly SyncList list19 = new SyncList(); + + public readonly SyncList list20 = new SyncList(); + public readonly SyncList list21 = new SyncList(); + public readonly SyncList list22 = new SyncList(); + public readonly SyncList list23 = new SyncList(); + public readonly SyncList list24 = new SyncList(); + public readonly SyncList list25 = new SyncList(); + public readonly SyncList list26 = new SyncList(); + public readonly SyncList list27 = new SyncList(); + public readonly SyncList list28 = new SyncList(); + public readonly SyncList list29 = new SyncList(); + + public readonly SyncList list30 = new SyncList(); + public readonly SyncList list31 = new SyncList(); + public readonly SyncList list32 = new SyncList(); + public readonly SyncList list33 = new SyncList(); + public readonly SyncList list34 = new SyncList(); + public readonly SyncList list35 = new SyncList(); + public readonly SyncList list36 = new SyncList(); + public readonly SyncList list37 = new SyncList(); + public readonly SyncList list38 = new SyncList(); + public readonly SyncList list39 = new SyncList(); + + public readonly SyncList list40 = new SyncList(); + public readonly SyncList list41 = new SyncList(); + public readonly SyncList list42 = new SyncList(); + public readonly SyncList list43 = new SyncList(); + public readonly SyncList list44 = new SyncList(); + public readonly SyncList list45 = new SyncList(); + public readonly SyncList list46 = new SyncList(); + public readonly SyncList list47 = new SyncList(); + public readonly SyncList list48 = new SyncList(); + public readonly SyncList list49 = new SyncList(); + + public readonly SyncList list50 = new SyncList(); + public readonly SyncList list51 = new SyncList(); + public readonly SyncList list52 = new SyncList(); + public readonly SyncList list53 = new SyncList(); + public readonly SyncList list54 = new SyncList(); + public readonly SyncList list55 = new SyncList(); + public readonly SyncList list56 = new SyncList(); + public readonly SyncList list57 = new SyncList(); + public readonly SyncList list58 = new SyncList(); + public readonly SyncList list59 = new SyncList(); + + public readonly SyncList list60 = new SyncList(); + public readonly SyncList list61 = new SyncList(); + public readonly SyncList list62 = new SyncList(); + public readonly SyncList list63 = new SyncList(); + public readonly SyncList list64 = new SyncList(); + public readonly SyncList list65 = new SyncList(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/SyncObjectsMoreThanMax.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/SyncObjectsMoreThanMax.cs.meta new file mode 100644 index 000000000..e44a2f523 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncObjectsTests~/SyncObjectsMoreThanMax.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5f8ac61059614330bdcf0f9305c53136 +timeCreated: 1631798423 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess.meta new file mode 100644 index 000000000..70e7449f6 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba9b37979cbbf4e3987f40254af0b291 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSet.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSet.cs new file mode 100644 index 000000000..e5d75e99a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSet.cs @@ -0,0 +1,9 @@ +using Mirror; + +namespace WeaverSyncSetTests.SyncSet +{ + class SyncSet : NetworkBehaviour + { + public readonly SyncList Foo; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSet.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSet.cs.meta new file mode 100644 index 000000000..d146af972 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f81e2e962dfc4eaa8650c8bb1c32512 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetByteValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetByteValid.cs new file mode 100644 index 000000000..ac78ad2e9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetByteValid.cs @@ -0,0 +1,11 @@ +using Mirror; + +namespace WeaverSyncSetTests.SyncSetByteValid +{ + class SyncSetByteValid : NetworkBehaviour + { + class MyByteClass : SyncHashSet { }; + + readonly MyByteClass Foo; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetByteValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetByteValid.cs.meta new file mode 100644 index 000000000..8b58d0c57 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetByteValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f4eb6f81ca19c4af69e957887a8cd83d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericAbstractInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericAbstractInheritance.cs new file mode 100644 index 000000000..fc895897d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericAbstractInheritance.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverSyncSetTests.SyncSetGenericAbstractInheritance +{ + class SyncSetGenericAbstractInheritance : NetworkBehaviour + { + readonly SomeSetInt superSyncSetString = new SomeSetInt(); + + public abstract class SomeSet : SyncHashSet { } + + public class SomeSetInt : SomeSet { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericAbstractInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericAbstractInheritance.cs.meta new file mode 100644 index 000000000..62885aba8 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericAbstractInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2aa229fc500d04d2884bcef3b1aaa81a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericInheritance.cs new file mode 100644 index 000000000..9ecdbfd79 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericInheritance.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverSyncSetTests.SyncSetGenericInheritance +{ + class SyncSetGenericInheritance : NetworkBehaviour + { + readonly SomeSetInt someSet = new SomeSetInt(); + + public class SomeSet : SyncHashSet { } + + public class SomeSetInt : SomeSet { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericInheritance.cs.meta new file mode 100644 index 000000000..8e107b9d4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetGenericInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03b44aff9b61b44768bcd3871e43f479 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetInheritance.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetInheritance.cs new file mode 100644 index 000000000..6ce3855c0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetInheritance.cs @@ -0,0 +1,13 @@ +using Mirror; + +namespace WeaverSyncSetTests.SyncSetInheritance +{ + class SyncSetInheritance : NetworkBehaviour + { + readonly SuperSet superSet = new SuperSet(); + + public class SomeSet : SyncHashSet { } + + public class SuperSet : SomeSet { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetInheritance.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetInheritance.cs.meta new file mode 100644 index 000000000..d16e42611 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetInheritance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0c1252122cf5434589e95b6eb4e4b44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetStruct.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetStruct.cs new file mode 100644 index 000000000..086d67c49 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetStruct.cs @@ -0,0 +1,17 @@ +using Mirror; + +namespace WeaverSyncSetTests.SyncSetStruct +{ + class SyncSetStruct : NetworkBehaviour + { + readonly MyStructSet Foo; + + struct MyStruct + { + public int potato; + public float floatingpotato; + public double givemetwopotatoes; + } + class MyStructSet : SyncHashSet { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetStruct.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetStruct.cs.meta new file mode 100644 index 000000000..b81f8e85c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncSetTests_IsSuccess/SyncSetStruct.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91c50a30665544ce9863a61c334c116c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests.cs new file mode 100644 index 000000000..95f3c8a74 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests.cs @@ -0,0 +1,40 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverSyncVarAttributeHookTests : WeaverTestsBuildFromTestName + { + static string OldNewMethodFormat(string hookName, string ValueType) + { + return string.Format("void {0}({1} oldValue, {1} newValue)", hookName, ValueType); + } + + [Test] + public void ErrorWhenNoHookFound() + { + HasError($"Could not find hook for 'health', hook name 'onChangeHealth'. Method signature should be {OldNewMethodFormat("onChangeHealth", "System.Int32")}", + "System.Int32 WeaverSyncVarHookTests.ErrorWhenNoHookFound.ErrorWhenNoHookFound::health"); + } + + [Test] + public void ErrorWhenNoHookWithCorrectParametersFound() + { + HasError($"Could not find hook for 'health', hook name 'onChangeHealth'. Method signature should be {OldNewMethodFormat("onChangeHealth", "System.Int32")}", + "System.Int32 WeaverSyncVarHookTests.ErrorWhenNoHookWithCorrectParametersFound.ErrorWhenNoHookWithCorrectParametersFound::health"); + } + + [Test] + public void ErrorForWrongTypeOldParameter() + { + HasError($"Wrong type for Parameter in hook for 'health', hook name 'onChangeHealth'. Method signature should be {OldNewMethodFormat("onChangeHealth", "System.Int32")}", + "System.Int32 WeaverSyncVarHookTests.ErrorForWrongTypeOldParameter.ErrorForWrongTypeOldParameter::health"); + } + + [Test] + public void ErrorForWrongTypeNewParameter() + { + HasError($"Wrong type for Parameter in hook for 'health', hook name 'onChangeHealth'. Method signature should be {OldNewMethodFormat("onChangeHealth", "System.Int32")}", + "System.Int32 WeaverSyncVarHookTests.ErrorForWrongTypeNewParameter.ErrorForWrongTypeNewParameter::health"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests.cs.meta new file mode 100644 index 000000000..8b4b77a25 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b8a829462efd04479519a188e458616 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess.meta new file mode 100644 index 000000000..d4bd9a646 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e70e01ab41584cecb0bb277ebcec26b6 +timeCreated: 1643083684 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithGameObjects.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithGameObjects.cs new file mode 100644 index 000000000..c15648e78 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithGameObjects.cs @@ -0,0 +1,16 @@ +using Mirror; +using UnityEngine; + +namespace WeaverSyncVarHookTests.FindsHookWithGameObjects +{ + class FindsHookWithGameObjects : NetworkBehaviour + { + [SyncVar(hook = nameof(onTargetChanged))] + GameObject target; + + void onTargetChanged(GameObject oldValue, GameObject newValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithGameObjects.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithGameObjects.cs.meta new file mode 100644 index 000000000..48caf7359 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithGameObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91ba1f1636e994d35926247a1b5ddfeb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithNetworkIdentity.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithNetworkIdentity.cs new file mode 100644 index 000000000..b9f5faa48 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithNetworkIdentity.cs @@ -0,0 +1,16 @@ +using Mirror; + + +namespace WeaverSyncVarHookTests.FindsHookWithNetworkIdentity +{ + class FindsHookWithNetworkIdentity : NetworkBehaviour + { + [SyncVar(hook = nameof(onTargetChanged))] + NetworkIdentity target; + + void onTargetChanged(NetworkIdentity oldValue, NetworkIdentity newValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithNetworkIdentity.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithNetworkIdentity.cs.meta new file mode 100644 index 000000000..c05f649e4 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithNetworkIdentity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa0f9d77be7ab465d8eaecf94109e28c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInOrder.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInOrder.cs new file mode 100644 index 000000000..adb9a3c2d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInOrder.cs @@ -0,0 +1,21 @@ +using Mirror; +using UnityEngine; + +namespace WeaverSyncVarHookTests.FindsHookWithOtherOverloadsInOrder +{ + class FindsHookWithOtherOverloadsInOrder : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + void onChangeHealth(int oldValue, int newValue) + { + + } + + void onChangeHealth(Vector3 anotherValue, bool secondArg) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInOrder.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInOrder.cs.meta new file mode 100644 index 000000000..5d2db7f05 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInOrder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ca8d2a8003b643deb817d22a5c18be9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInReverseOrder.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInReverseOrder.cs new file mode 100644 index 000000000..6b9aa1e30 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInReverseOrder.cs @@ -0,0 +1,21 @@ +using Mirror; +using UnityEngine; + +namespace WeaverSyncVarHookTests.FindsHookWithOtherOverloadsInReverseOrder +{ + class FindsHookWithOtherOverloadsInReverseOrder : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + void onChangeHealth(Vector3 anotherValue, bool secondArg) + { + + } + + void onChangeHealth(int oldValue, int newValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInReverseOrder.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInReverseOrder.cs.meta new file mode 100644 index 000000000..cfe2d8767 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsHookWithOtherOverloadsInReverseOrder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4aa8df7feb1364a86a5088146558de8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPrivateHook.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPrivateHook.cs new file mode 100644 index 000000000..c523785ed --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPrivateHook.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncVarHookTests.FindsPrivateHook +{ + class FindsPrivateHook : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + void onChangeHealth(int oldValue, int newValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPrivateHook.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPrivateHook.cs.meta new file mode 100644 index 000000000..d752182c7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPrivateHook.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4635f4b5c7734921bb185fb09daef52 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPublicHook.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPublicHook.cs new file mode 100644 index 000000000..8252d2282 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPublicHook.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncVarHookTests.FindsPublicHook +{ + class FindsPublicHook : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + public void onChangeHealth(int oldValue, int newValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPublicHook.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPublicHook.cs.meta new file mode 100644 index 000000000..1cb18561e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsPublicHook.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd5da6f7c90d9446787ab8f1c0bbb499 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsStaticHook.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsStaticHook.cs new file mode 100644 index 000000000..dae2c8245 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsStaticHook.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncVarHookTests.FindsStaticHook +{ + class FindsStaticHook : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + static void onChangeHealth(int oldValue, int newValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsStaticHook.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsStaticHook.cs.meta new file mode 100644 index 000000000..625aeecdd --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests_IsSuccess/FindsStaticHook.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5099d9a55253341919aeff0513a096de +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorForWrongTypeNewParameter.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorForWrongTypeNewParameter.cs new file mode 100644 index 000000000..bd2b6db7a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorForWrongTypeNewParameter.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncVarHookTests.ErrorForWrongTypeNewParameter +{ + class ErrorForWrongTypeNewParameter : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + void onChangeHealth(int oldValue, float wrongNewValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorForWrongTypeOldParameter.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorForWrongTypeOldParameter.cs new file mode 100644 index 000000000..3e75887a0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorForWrongTypeOldParameter.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncVarHookTests.ErrorForWrongTypeOldParameter +{ + class ErrorForWrongTypeOldParameter : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + void onChangeHealth(float wrongOldValue, int newValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorWhenNoHookFound.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorWhenNoHookFound.cs new file mode 100644 index 000000000..0e78f7213 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorWhenNoHookFound.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverSyncVarHookTests.ErrorWhenNoHookFound +{ + class ErrorWhenNoHookFound : NetworkBehaviour + { + [SyncVar(hook = "onChangeHealth")] + int health; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorWhenNoHookWithCorrectParametersFound.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorWhenNoHookWithCorrectParametersFound.cs new file mode 100644 index 000000000..74592f053 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeHookTests~/ErrorWhenNoHookWithCorrectParametersFound.cs @@ -0,0 +1,20 @@ +using Mirror; + +namespace WeaverSyncVarHookTests.ErrorWhenNoHookWithCorrectParametersFound +{ + class ErrorWhenNoHookWithCorrectParametersFound : NetworkBehaviour + { + [SyncVar(hook = nameof(onChangeHealth))] + int health; + + void onChangeHealth(int someOtherValue) + { + + } + + void onChangeHealth(int someOtherValue, int moreValue, bool anotherValue) + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests.cs new file mode 100644 index 000000000..08fa8c03a --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests.cs @@ -0,0 +1,71 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverSyncVarAttributeTests : WeaverTestsBuildFromTestName + { + [Test] + public void SyncVarsStatic() + { + HasError("invalidVar cannot be static", + "System.Int32 WeaverSyncVarTests.SyncVarsStatic.SyncVarsStatic::invalidVar"); + } + + [Test] + public void SyncVarsGenericParam() + { + HasError("Cannot generate writer for generic type MySyncVar`1. Use a supported type or provide a custom writer", + "WeaverSyncVarTests.SyncVarsGenericParam.SyncVarsGenericParam/MySyncVar`1"); + HasError("invalidVar has unsupported type. Use a supported Mirror type instead", + "WeaverSyncVarTests.SyncVarsGenericParam.SyncVarsGenericParam/MySyncVar`1 WeaverSyncVarTests.SyncVarsGenericParam.SyncVarsGenericParam::invalidVar"); + } + + [Test] + public void SyncVarsInterface() + { + HasError("Cannot generate writer for interface IMySyncVar. Use a supported type or provide a custom writer", + "WeaverSyncVarTests.SyncVarsInterface.SyncVarsInterface/IMySyncVar"); + HasError("invalidVar has unsupported type. Use a supported Mirror type instead", + "WeaverSyncVarTests.SyncVarsInterface.SyncVarsInterface/IMySyncVar WeaverSyncVarTests.SyncVarsInterface.SyncVarsInterface::invalidVar"); + } + + [Test] + public void SyncVarsUnityComponent() + { + HasError("Cannot generate writer for component type TextMesh. Use a supported type or provide a custom writer", + "UnityEngine.TextMesh"); + HasError("invalidVar has unsupported type. Use a supported Mirror type instead", + "UnityEngine.TextMesh WeaverSyncVarTests.SyncVarsUnityComponent.SyncVarsUnityComponent::invalidVar"); + } + + [Test] + public void SyncVarsCantBeArray() + { + HasError("thisShouldntWork has invalid type. Use SyncLists instead of arrays", + "System.Int32[] WeaverSyncVarTests.SyncVarsCantBeArray.SyncVarsCantBeArray::thisShouldntWork"); + } + + [Test] + public void SyncVarsSyncList() + { + // NOTE if this test fails without a warning: + // that happens if after WeaverAssembler->AssemblyBuilder.Build(), + // Unity invokes ILPostProcessor internally. + // and we invoke it from WeaverAssembler buildFinished again. + // => make sure that our ILPostProcessor does nto run on + // WeaverAssembler assemblies + HasNoErrors(); + HasWarning("syncobj has [SyncVar] attribute. SyncLists should not be marked with SyncVar", + "WeaverSyncVarTests.SyncVarsSyncList.SyncVarsSyncList/SyncObjImplementer WeaverSyncVarTests.SyncVarsSyncList.SyncVarsSyncList::syncobj"); + HasWarning("syncints has [SyncVar] attribute. SyncLists should not be marked with SyncVar", + "Mirror.SyncList`1 WeaverSyncVarTests.SyncVarsSyncList.SyncVarsSyncList::syncints"); + } + + [Test] + public void SyncVarsMoreThanMax() + { + HasError("SyncVarsMoreThanMax has > 64 SyncVars. Consider refactoring your class into multiple components", + "WeaverSyncVarTests.SyncVarsMoreThanMax.SyncVarsMoreThanMax"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests.cs.meta new file mode 100644 index 000000000..770100cfe --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c619cb5c467af2a4dafb611d50b84cbf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess.meta new file mode 100644 index 000000000..f7a809d01 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9aa873725d92477cbdf19f333bb5ca96 +timeCreated: 1643083781 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsDerivedNetworkBehaviour.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsDerivedNetworkBehaviour.cs new file mode 100644 index 000000000..fd97fca85 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsDerivedNetworkBehaviour.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsDerivedNetworkBehaviour +{ + class MyBehaviour : NetworkBehaviour + { + public int abc = 123; + } + class SyncVarsDerivedNetworkBehaviour : NetworkBehaviour + { + [SyncVar] + MyBehaviour invalidVar; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsDerivedNetworkBehaviour.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsDerivedNetworkBehaviour.cs.meta new file mode 100644 index 000000000..fffb7a402 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsDerivedNetworkBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f453a8bb28a4a4deb9417d2a29c20aef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsExactlyMax.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsExactlyMax.cs new file mode 100644 index 000000000..97f4c4de0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsExactlyMax.cs @@ -0,0 +1,74 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsExactlyMax +{ + // SyncVar dirty mask is 64 bit. exactly 64 should work. + class SyncVarsExactlyMax : NetworkBehaviour + { + // 1..64 + [SyncVar] int var1; + [SyncVar] int var2; + [SyncVar] int var3; + [SyncVar] int var4; + [SyncVar] int var5; + [SyncVar] int var6; + [SyncVar] int var7; + [SyncVar] int var8; + [SyncVar] int var9; + [SyncVar] int var10; + [SyncVar] int var11; + [SyncVar] int var12; + [SyncVar] int var13; + [SyncVar] int var14; + [SyncVar] int var15; + [SyncVar] int var16; + [SyncVar] int var17; + [SyncVar] int var18; + [SyncVar] int var19; + [SyncVar] int var20; + [SyncVar] int var21; + [SyncVar] int var22; + [SyncVar] int var23; + [SyncVar] int var24; + [SyncVar] int var25; + [SyncVar] int var26; + [SyncVar] int var27; + [SyncVar] int var28; + [SyncVar] int var29; + [SyncVar] int var30; + [SyncVar] int var31; + [SyncVar] int var32; + [SyncVar] int var33; + [SyncVar] int var34; + [SyncVar] int var35; + [SyncVar] int var36; + [SyncVar] int var37; + [SyncVar] int var38; + [SyncVar] int var39; + [SyncVar] int var40; + [SyncVar] int var41; + [SyncVar] int var42; + [SyncVar] int var43; + [SyncVar] int var44; + [SyncVar] int var45; + [SyncVar] int var46; + [SyncVar] int var47; + [SyncVar] int var48; + [SyncVar] int var49; + [SyncVar] int var50; + [SyncVar] int var51; + [SyncVar] int var52; + [SyncVar] int var53; + [SyncVar] int var54; + [SyncVar] int var55; + [SyncVar] int var56; + [SyncVar] int var57; + [SyncVar] int var58; + [SyncVar] int var59; + [SyncVar] int var60; + [SyncVar] int var61; + [SyncVar] int var62; + [SyncVar] int var63; + [SyncVar] int var64; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsExactlyMax.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsExactlyMax.cs.meta new file mode 100644 index 000000000..bda0ddfc0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsExactlyMax.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ecee7f6e569f44afbaa0b6c1cbf2d16c +timeCreated: 1631799739 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsValid.cs new file mode 100644 index 000000000..710181819 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsValid.cs @@ -0,0 +1,86 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsValid +{ + class SyncVarsValid : NetworkBehaviour + { + [SyncVar(hook = nameof(OnChangeHealth))] + int health; + + [SyncVar] int var2; + [SyncVar] int var3; + [SyncVar] int var4; + [SyncVar] int var5; + [SyncVar] int var6; + [SyncVar] int var7; + [SyncVar] int var8; + [SyncVar] int var9; + [SyncVar] int var10; + [SyncVar] int var11; + [SyncVar] int var12; + [SyncVar] int var13; + [SyncVar] int var14; + [SyncVar] int var15; + [SyncVar] int var16; + [SyncVar] int var17; + [SyncVar] int var18; + [SyncVar] int var19; + [SyncVar] int var20; + [SyncVar] int var21; + [SyncVar] int var22; + [SyncVar] int var23; + [SyncVar] int var24; + [SyncVar] int var25; + [SyncVar] int var26; + [SyncVar] int var27; + [SyncVar] int var28; + [SyncVar] int var29; + [SyncVar] int var30; + [SyncVar] int var31; + [SyncVar] int var32; + [SyncVar] int var33; + [SyncVar] int var34; + [SyncVar] int var35; + [SyncVar] int var36; + [SyncVar] int var37; + [SyncVar] int var38; + [SyncVar] int var39; + [SyncVar] int var40; + [SyncVar] int var41; + [SyncVar] int var42; + [SyncVar] int var43; + [SyncVar] int var44; + [SyncVar] int var45; + [SyncVar] int var46; + [SyncVar] int var47; + [SyncVar] int var48; + [SyncVar] int var49; + [SyncVar] int var50; + [SyncVar] int var51; + [SyncVar] int var52; + [SyncVar] int var53; + [SyncVar] int var54; + [SyncVar] int var55; + [SyncVar] int var56; + [SyncVar] int var57; + [SyncVar] int var58; + [SyncVar] int var59; + [SyncVar] int var60; + [SyncVar] int var61; + [SyncVar] int var62; + [SyncVar] int var63; + + public void TakeDamage(int amount) + { + if (!isServer) + return; + + health -= amount; + } + + void OnChangeHealth(int oldHealth, int newHealth) + { + // do things with your health bar + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsValid.cs.meta new file mode 100644 index 000000000..08d50f232 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests_IsSuccess/SyncVarsValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05594340f435e4de28388efc46a33ae1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsCantBeArray.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsCantBeArray.cs new file mode 100644 index 000000000..7f346da6f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsCantBeArray.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsCantBeArray +{ + class SyncVarsCantBeArray : NetworkBehaviour + { + [SyncVar] + int[] thisShouldntWork = new int[100]; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsGenericParam.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsGenericParam.cs new file mode 100644 index 000000000..b965e711b --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsGenericParam.cs @@ -0,0 +1,15 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsGenericParam +{ + class SyncVarsGenericParam : NetworkBehaviour + { + struct MySyncVar + { + T abc; + } + + [SyncVar] + MySyncVar invalidVar = new MySyncVar(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsInterface.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsInterface.cs new file mode 100644 index 000000000..c32cb3092 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsInterface.cs @@ -0,0 +1,14 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsInterface +{ + class SyncVarsInterface : NetworkBehaviour + { + interface IMySyncVar + { + void interfaceMethod(); + } + [SyncVar] + IMySyncVar invalidVar; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsMoreThanMax.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsMoreThanMax.cs new file mode 100644 index 000000000..7f9fde206 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsMoreThanMax.cs @@ -0,0 +1,74 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsMoreThanMax +{ + // SyncVar dirty mask is 64 bit. more than 64 should show an error. + class SyncVarsMoreThanMax : NetworkBehaviour + { + [SyncVar] int var1; + [SyncVar] int var2; + [SyncVar] int var3; + [SyncVar] int var4; + [SyncVar] int var5; + [SyncVar] int var6; + [SyncVar] int var7; + [SyncVar] int var8; + [SyncVar] int var9; + [SyncVar] int var10; + [SyncVar] int var11; + [SyncVar] int var12; + [SyncVar] int var13; + [SyncVar] int var14; + [SyncVar] int var15; + [SyncVar] int var16; + [SyncVar] int var17; + [SyncVar] int var18; + [SyncVar] int var19; + [SyncVar] int var20; + [SyncVar] int var21; + [SyncVar] int var22; + [SyncVar] int var23; + [SyncVar] int var24; + [SyncVar] int var25; + [SyncVar] int var26; + [SyncVar] int var27; + [SyncVar] int var28; + [SyncVar] int var29; + [SyncVar] int var30; + [SyncVar] int var31; + [SyncVar] int var32; + [SyncVar] int var33; + [SyncVar] int var34; + [SyncVar] int var35; + [SyncVar] int var36; + [SyncVar] int var37; + [SyncVar] int var38; + [SyncVar] int var39; + [SyncVar] int var40; + [SyncVar] int var41; + [SyncVar] int var42; + [SyncVar] int var43; + [SyncVar] int var44; + [SyncVar] int var45; + [SyncVar] int var46; + [SyncVar] int var47; + [SyncVar] int var48; + [SyncVar] int var49; + [SyncVar] int var50; + [SyncVar] int var51; + [SyncVar] int var52; + [SyncVar] int var53; + [SyncVar] int var54; + [SyncVar] int var55; + [SyncVar] int var56; + [SyncVar] int var57; + [SyncVar] int var58; + [SyncVar] int var59; + [SyncVar] int var60; + [SyncVar] int var61; + [SyncVar] int var62; + [SyncVar] int var63; + [SyncVar] int var64; + [SyncVar] int var65; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsStatic.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsStatic.cs new file mode 100644 index 000000000..a89a71f32 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsStatic.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverSyncVarTests.SyncVarsStatic +{ + class SyncVarsStatic : NetworkBehaviour + { + [SyncVar] + static int invalidVar = 123; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsSyncList.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsSyncList.cs new file mode 100644 index 000000000..9851fc4ce --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsSyncList.cs @@ -0,0 +1,25 @@ +using Mirror; +using System; + +namespace WeaverSyncVarTests.SyncVarsSyncList +{ + + class SyncVarsSyncList : NetworkBehaviour + { + public class SyncObjImplementer : SyncObject + { + public override void ClearChanges() { } + public override void OnSerializeAll(NetworkWriter writer) { } + public override void OnSerializeDelta(NetworkWriter writer) { } + public override void OnDeserializeAll(NetworkReader reader) { } + public override void OnDeserializeDelta(NetworkReader reader) { } + public override void Reset() { } + } + + [SyncVar] + SyncObjImplementer syncobj; + + [SyncVar] + SyncList syncints; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsUnityComponent.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsUnityComponent.cs new file mode 100644 index 000000000..05dfcacbb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverSyncVarAttributeTests~/SyncVarsUnityComponent.cs @@ -0,0 +1,11 @@ +using Mirror; +using UnityEngine; + +namespace WeaverSyncVarTests.SyncVarsUnityComponent +{ + class SyncVarsUnityComponent : NetworkBehaviour + { + [SyncVar] + TextMesh invalidVar; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests.cs new file mode 100644 index 000000000..b2f575bb7 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests.cs @@ -0,0 +1,35 @@ +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public class WeaverTargetRpcTests : WeaverTestsBuildFromTestName + { + [Test] + public void ErrorWhenTargetRpcIsStatic() + { + HasError("TargetCantBeStatic must not be static", + "System.Void WeaverTargetRpcTests.ErrorWhenTargetRpcIsStatic.ErrorWhenTargetRpcIsStatic::TargetCantBeStatic(Mirror.NetworkConnection)"); + } + + [Test] + public void ErrorWhenNetworkConnectionIsNotTheFirstParameter() + { + HasError("TargetRpcMethod has invalid parameter nc. Cannot pass NetworkConnections", + "System.Void WeaverTargetRpcTests.ErrorWhenNetworkConnectionIsNotTheFirstParameter.ErrorWhenNetworkConnectionIsNotTheFirstParameter::TargetRpcMethod(System.Int32,Mirror.NetworkConnection)"); + } + + [Test] + public void AbstractTargetRpc() + { + HasError("Abstract TargetRpc are currently not supported, use virtual method instead", + "System.Void WeaverTargetRpcTests.AbstractTargetRpc.AbstractTargetRpc::TargetDoSomething()"); + } + + [Test] + public void OverrideAbstractTargetRpc() + { + HasError("Abstract TargetRpc are currently not supported, use virtual method instead", + "System.Void WeaverTargetRpcTests.OverrideAbstractTargetRpc.BaseBehaviour::TargetDoSomething()"); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests.cs.meta new file mode 100644 index 000000000..e31608660 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e7275e59ed5b274eac8299115b54a56 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess.meta new file mode 100644 index 000000000..73c2747e1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 61b1dec97fbc4c9cae92609b5bbd9322 +timeCreated: 1643083911 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/OverrideVirtualTargetRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/OverrideVirtualTargetRpc.cs new file mode 100644 index 000000000..546b91ddc --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/OverrideVirtualTargetRpc.cs @@ -0,0 +1,23 @@ +using Mirror; + + +namespace WeaverTargetRpcTests.OverrideVirtualTargetRpc +{ + class OverrideVirtualTargetRpc : baseBehaviour + { + [TargetRpc] + protected override void TargetDoSomething() + { + // do something + } + } + + class baseBehaviour : NetworkBehaviour + { + [TargetRpc] + protected virtual void TargetDoSomething() + { + + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/OverrideVirtualTargetRpc.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/OverrideVirtualTargetRpc.cs.meta new file mode 100644 index 000000000..ea7d34217 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/OverrideVirtualTargetRpc.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd443394428a741c19d59c118cc8ab6c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection.cs new file mode 100644 index 000000000..ca7b08ae3 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverTargetRpcTests.TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection +{ + class TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection : NetworkBehaviour + { + [TargetRpc] + void TargetRpcMethod(int usefulNumber) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection.cs.meta new file mode 100644 index 000000000..5b045fce9 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanHaveOtherParametersWhileSkipingNetworkConnection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 852a7a1887709494ba0b3719389da4d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanSkipNetworkConnection.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanSkipNetworkConnection.cs new file mode 100644 index 000000000..ec0858925 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanSkipNetworkConnection.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverTargetRpcTests.TargetRpcCanSkipNetworkConnection +{ + class TargetRpcCanSkipNetworkConnection : NetworkBehaviour + { + [TargetRpc] + void TargetRpcMethod() { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanSkipNetworkConnection.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanSkipNetworkConnection.cs.meta new file mode 100644 index 000000000..3428d838e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcCanSkipNetworkConnection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b5612ba5b8ef645568b6b79d5548518f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcValid.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcValid.cs new file mode 100644 index 000000000..38e046054 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcValid.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverTargetRpcTests.TargetRpcValid +{ + class TargetRpcValid : NetworkBehaviour + { + [TargetRpc] + void TargetThatIsTotallyValid(NetworkConnection nc) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcValid.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcValid.cs.meta new file mode 100644 index 000000000..1c46edade --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/TargetRpcValid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ec5e487c9cb7465b80ebdc3cfed7b2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/VirtualTargetRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/VirtualTargetRpc.cs new file mode 100644 index 000000000..dd00afd41 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/VirtualTargetRpc.cs @@ -0,0 +1,14 @@ +using Mirror; + + +namespace WeaverTargetRpcTests.VirtualTargetRpc +{ + class VirtualTargetRpc : NetworkBehaviour + { + [TargetRpc] + protected virtual void TargetDoSomething() + { + // do something + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/VirtualTargetRpc.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/VirtualTargetRpc.cs.meta new file mode 100644 index 000000000..72d39d085 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests_IsSuccess/VirtualTargetRpc.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b717a43817274f04b5a0fcf90418c59 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/AbstractTargetRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/AbstractTargetRpc.cs new file mode 100644 index 000000000..4cc89278e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/AbstractTargetRpc.cs @@ -0,0 +1,11 @@ +using Mirror; + + +namespace WeaverTargetRpcTests.AbstractTargetRpc +{ + abstract class AbstractTargetRpc : NetworkBehaviour + { + [TargetRpc] + protected abstract void TargetDoSomething(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/ErrorWhenNetworkConnectionIsNotTheFirstParameter.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/ErrorWhenNetworkConnectionIsNotTheFirstParameter.cs new file mode 100644 index 000000000..60201be5f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/ErrorWhenNetworkConnectionIsNotTheFirstParameter.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverTargetRpcTests.ErrorWhenNetworkConnectionIsNotTheFirstParameter +{ + class ErrorWhenNetworkConnectionIsNotTheFirstParameter : NetworkBehaviour + { + [TargetRpc] + void TargetRpcMethod(int potatoesRcool, NetworkConnection nc) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/ErrorWhenTargetRpcIsStatic.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/ErrorWhenTargetRpcIsStatic.cs new file mode 100644 index 000000000..3cd686759 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/ErrorWhenTargetRpcIsStatic.cs @@ -0,0 +1,10 @@ +using Mirror; + +namespace WeaverTargetRpcTests.ErrorWhenTargetRpcIsStatic +{ + class ErrorWhenTargetRpcIsStatic : NetworkBehaviour + { + [TargetRpc] + static void TargetCantBeStatic(NetworkConnection nc) { } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/OverrideAbstractTargetRpc.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/OverrideAbstractTargetRpc.cs new file mode 100644 index 000000000..63b7fe794 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTargetRpcTests~/OverrideAbstractTargetRpc.cs @@ -0,0 +1,20 @@ +using Mirror; + + +namespace WeaverTargetRpcTests.OverrideAbstractTargetRpc +{ + class OverrideAbstractTargetRpc : BaseBehaviour + { + [TargetRpc] + protected override void TargetDoSomething() + { + // do something + } + } + + abstract class BaseBehaviour : NetworkBehaviour + { + [TargetRpc] + protected abstract void TargetDoSomething(); + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTests.cs new file mode 100644 index 000000000..8259249cb --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTests.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.IO; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Weaver.Tests +{ + [TestFixture] + [Category("Weaver")] + public abstract class WeaverTests + { + protected List weaverErrors = new List(); + void HandleWeaverError(string msg) + { + LogAssert.ignoreFailingMessages = true; + Debug.LogError(msg); + LogAssert.ignoreFailingMessages = false; + + weaverErrors.Add(msg); + } + + protected List weaverWarnings = new List(); + void HandleWeaverWarning(string msg) + { + Debug.LogWarning(msg); + weaverWarnings.Add(msg); + } + + protected void BuildAndWeaveTestAssembly(string className, string testName) + { + string testSourceDirectory = className + "~"; + WeaverAssembler.OutputFile = Path.Combine(testSourceDirectory, testName + ".dll"); + WeaverAssembler.AddSourceFiles(new string[] { Path.Combine(testSourceDirectory, testName + ".cs") }); + WeaverAssembler.Build(HandleWeaverWarning, HandleWeaverError); + + Assert.That(WeaverAssembler.CompilerErrors, Is.False); + foreach (string error in weaverErrors) + { + // ensure all errors have a location + Assert.That(error, Does.Match(@"\(at .*\)$")); + } + } + + [OneTimeSetUp] + public void FixtureSetup() + { +#if !UNITY_2020_3_OR_NEWER + // CompilationFinishedHook is used for tests pre-2020 ILPostProcessor + CompilationFinishedHook.UnityLogEnabled = false; + CompilationFinishedHook.OnWeaverError += HandleWeaverError; + CompilationFinishedHook.OnWeaverWarning += HandleWeaverWarning; +#endif + } + + [OneTimeTearDown] + public void FixtureCleanup() + { +#if !UNITY_2020_3_OR_NEWER + // CompilationFinishedHook is used for tests pre-2020 ILPostProcessor + CompilationFinishedHook.OnWeaverError -= HandleWeaverError; + CompilationFinishedHook.OnWeaverWarning -= HandleWeaverWarning; + CompilationFinishedHook.UnityLogEnabled = true; +#endif + } + + [TearDown] + public void TestCleanup() + { + WeaverAssembler.DeleteOutputOnClear = true; + WeaverAssembler.Clear(); + + weaverWarnings.Clear(); + weaverErrors.Clear(); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTests.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTests.cs.meta new file mode 100644 index 000000000..a98d9222f --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c3f52dab9c479dd4586d0aceeb2390f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTestsBuildFromTestName.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverTestsBuildFromTestName.cs new file mode 100644 index 000000000..8341f5c56 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTestsBuildFromTestName.cs @@ -0,0 +1,40 @@ +using System.Linq; +using NUnit.Framework; + +namespace Mirror.Weaver.Tests +{ + public abstract class WeaverTestsBuildFromTestName : WeaverTests + { + [SetUp] + public virtual void TestSetup() + { + string className = TestContext.CurrentContext.Test.ClassName.Split('.').Last(); + + BuildAndWeaveTestAssembly(className, TestContext.CurrentContext.Test.Name); + } + + // IMPORTANT: IsSuccess() tests can almost ALL be moved into regular + // C#/folders without running AssemblyBuilder on them. + // See README.md int his folder. + protected void IsSuccess() + { + Assert.That(weaverErrors, Is.Empty); + Assert.That(weaverWarnings, Is.Empty); + } + + protected void HasNoErrors() + { + Assert.That(weaverErrors, Is.Empty); + } + + protected void HasError(string messsage, string atType) + { + Assert.That(weaverErrors, Contains.Item($"{messsage} (at {atType})")); + } + + protected void HasWarning(string messsage, string atType) + { + Assert.That(weaverWarnings, Contains.Item($"{messsage} (at {atType})")); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverTestsBuildFromTestName.cs.meta b/Assets/Mirror/Tests/Editor/Weaver/WeaverTestsBuildFromTestName.cs.meta new file mode 100644 index 000000000..bbaf34125 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverTestsBuildFromTestName.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aa404c2d6a88488f9b6e42a87b01cd61 +timeCreated: 1629525351 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Runtime.meta b/Assets/Mirror/Tests/Runtime.meta new file mode 100644 index 000000000..41c7a7c0b --- /dev/null +++ b/Assets/Mirror/Tests/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 925dd1ba8ae9d4a969a57221e025309c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs b/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs new file mode 100644 index 000000000..22197d033 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Runtime.ClientSceneTests +{ + public class TestListenerBehaviour : NetworkBehaviour + { + // If object is destroyed then both OnDisable and OnDestroy will be called + public event Action onDestroyCalled; + public event Action onDisableCalled; + + public void OnDisable() => onDisableCalled?.Invoke(); + void OnDestroy() => onDestroyCalled?.Invoke(); + } + + // A network Behaviour that changes NetworkIdentity.spawned in OnDisable + public class BadBehaviour : NetworkBehaviour + { + public void OnDisable() + { + GameObject go = new GameObject(); + NetworkIdentity netId = go.AddComponent(); + const int id = 32032; + netId.netId = id; + + NetworkClient.spawned.Add(id, netId); + } + } + + public class ClientSceneTests_DestroyAllClientObjects : MirrorPlayModeTest + { + Dictionary unspawnHandlers => NetworkClient.unspawnHandlers; + + [UnitySetUp] + public override IEnumerator UnitySetUp() + { + yield return base.UnitySetUp(); + + // start server & client and wait 1 frame + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + yield return null; + } + + [UnityTearDown] + public override IEnumerator UnityTearDown() + { + unspawnHandlers.Clear(); + base.TearDown(); + yield return null; + } + + TestListenerBehaviour CreateAndAddObject(ulong sceneId) + { + CreateNetworkedAndSpawn(out GameObject go, out NetworkIdentity identity, out TestListenerBehaviour listener); + identity.sceneId = sceneId; + return listener; + } + + [UnityTest] + public IEnumerator DestroysAllNetworkPrefabsInScene() + { + // sceneId 0 is prefab + TestListenerBehaviour listener1 = CreateAndAddObject(0); + TestListenerBehaviour listener2 = CreateAndAddObject(0); + + int destroyCalled1 = 0; + int destroyCalled2 = 0; + + listener1.onDestroyCalled += () => destroyCalled1++; + listener2.onDestroyCalled += () => destroyCalled2++; + + NetworkClient.DestroyAllClientObjects(); + + // wait for frame to make sure unity events are called + yield return null; + + Assert.That(destroyCalled1, Is.EqualTo(1)); + Assert.That(destroyCalled2, Is.EqualTo(1)); + } + + [UnityTest] + public IEnumerator DisablesAllNetworkSceneObjectsInScene() + { + // sceneId 0 is prefab + TestListenerBehaviour listener1 = CreateAndAddObject(101); + TestListenerBehaviour listener2 = CreateAndAddObject(102); + + int disableCalled1 = 0; + int disableCalled2 = 0; + + listener1.onDisableCalled += () => disableCalled1++; + listener2.onDisableCalled += () => disableCalled2++; + + int destroyCalled1 = 0; + int destroyCalled2 = 0; + + listener1.onDestroyCalled += () => destroyCalled1++; + listener2.onDestroyCalled += () => destroyCalled2++; + + NetworkClient.DestroyAllClientObjects(); + + // wait for frame to make sure unity events are called + yield return null; + + Assert.That(disableCalled1, Is.EqualTo(1)); + Assert.That(disableCalled2, Is.EqualTo(1)); + + Assert.That(destroyCalled1, Is.EqualTo(0), "Scene objects should not be destroyed"); + Assert.That(destroyCalled2, Is.EqualTo(0), "Scene objects should not be destroyed"); + } + + [Test] + public void CallsUnspawnHandlerInsteadOfDestroy() + { + // sceneId 0 is prefab + TestListenerBehaviour listener1 = CreateAndAddObject(0); + TestListenerBehaviour listener2 = CreateAndAddObject(0); + + Guid guid1 = Guid.NewGuid(); + Guid guid2 = Guid.NewGuid(); + + int unspawnCalled1 = 0; + int unspawnCalled2 = 0; + + unspawnHandlers.Add(guid1, x => unspawnCalled1++); + unspawnHandlers.Add(guid2, x => unspawnCalled2++); + listener1.GetComponent().assetId = guid1; + listener2.GetComponent().assetId = guid2; + + int disableCalled1 = 0; + int disableCalled2 = 0; + + listener1.onDisableCalled += () => disableCalled1++; + listener2.onDisableCalled += () => disableCalled2++; + + NetworkClient.DestroyAllClientObjects(); + + Assert.That(unspawnCalled1, Is.EqualTo(1)); + Assert.That(unspawnCalled2, Is.EqualTo(1)); + + Assert.That(disableCalled1, Is.EqualTo(0), "Object with UnspawnHandler should not be destroyed"); + Assert.That(disableCalled2, Is.EqualTo(0), "Object with UnspawnHandler should not be destroyed"); + } + + [Test] + public void ClearsSpawnedList() + { + // sceneId 0 is prefab + TestListenerBehaviour listener1 = CreateAndAddObject(0); + TestListenerBehaviour listener2 = CreateAndAddObject(0); + + NetworkClient.DestroyAllClientObjects(); + + Assert.That(NetworkClient.spawned, Is.Empty); + } + + [Test] + public void CatchesAndLogsExeptionWhenSpawnedListIsChanged() + { + // create spawned (needs to be added to .spawned!) + CreateNetworkedAndSpawn(out GameObject badGameObject, out NetworkIdentity identity, out BadBehaviour bad); + + LogAssert.Expect(LogType.Exception, new Regex("InvalidOperationException")); + LogAssert.Expect(LogType.Error, "Could not DestroyAllClientObjects because spawned list was modified during loop, make sure you are not modifying NetworkIdentity.spawned by calling NetworkServer.Destroy or NetworkServer.Spawn in OnDestroy or OnDisable."); + NetworkClient.DestroyAllClientObjects(); + } + } +} diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs.meta b/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs.meta new file mode 100644 index 000000000..420611c3f --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a5f4e31bf3f4dc4fa194eabc2cf80bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer.cs b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer.cs new file mode 100644 index 000000000..91e306f92 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer.cs @@ -0,0 +1,68 @@ +using System.Collections; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Runtime.ClientSceneTests +{ + public class ClientSceneTests_LocalPlayer : ClientSceneTestsBase + { + [SetUp] + public override void SetUp() + { + base.SetUp(); + + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + } + + [UnityTest] + public IEnumerator LocalPlayerIsSetToNullAfterDestroy() + { + // need spawned local player + CreateNetworkedAndSpawnPlayer(out GameObject go, out _, NetworkServer.localConnection); + + // need to have localPlayer set for this test + Assert.That(NetworkClient.localPlayer, !Is.Null); + + // destroy, wait one frame, localPlayer should be cleared + GameObject.Destroy(go); + yield return null; + Assert.IsTrue(NetworkClient.localPlayer is null, "local player should be set to c# null"); + } + + [UnityTest] + public IEnumerator DestroyingOtherObjectDoesntEffectLocalPlayer() + { + // need spawned not-local-player + CreateNetworkedAndSpawn(out _, out NetworkIdentity notPlayer); + + // need spawned local player + CreateNetworkedAndSpawnPlayer(out _, out NetworkIdentity player, NetworkServer.localConnection); + + // need to have localPlayer set for this test + Assert.That(NetworkClient.localPlayer, !Is.Null); + + // destroy, wait one frame, localPlayer should remain + GameObject.Destroy(notPlayer); + yield return null; + Assert.IsTrue(NetworkClient.localPlayer != null, "local player should not be cleared"); + Assert.IsTrue(NetworkClient.localPlayer == player, "local player should still be equal to player"); + } + + [UnityTest] + public IEnumerator LocalPlayerIsSetToNullAfterDestroyMessage() + { + // need spawned local player + CreateNetworkedAndSpawnPlayer(out _, out NetworkIdentity identity, NetworkServer.localConnection); + + // need to have localPlayer set for this test + Assert.That(NetworkClient.localPlayer, !Is.Null); + + // OnObjectDestroy, wait one frame, localPlayer should be cleared + NetworkClient.OnObjectDestroy(new ObjectDestroyMessage{netId = identity.netId}); + yield return null; + Assert.IsTrue(NetworkClient.localPlayer is null, "local player should be set to c# null"); + } + } +} diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer.cs.meta b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer.cs.meta new file mode 100644 index 000000000..b57c8bd6a --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7c087df10f51b674f94f84ba30a303f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer_AsHost.cs b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer_AsHost.cs new file mode 100644 index 000000000..ddc5af61a --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer_AsHost.cs @@ -0,0 +1,48 @@ +using System.Collections; +using NUnit.Framework; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Runtime +{ + public class ClientSceneTest_LocalPlayer_AsHost : MirrorPlayModeTest + { + [UnitySetUp] + public override IEnumerator UnitySetUp() + { + yield return base.UnitySetUp(); + + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + } + + [UnityTest] + public IEnumerator LocalPlayerIsSetToNullAfterNetworkDestroy() + { + // need spawned local player + CreateNetworkedAndSpawnPlayer(out _, out NetworkIdentity identity, NetworkServer.localConnection); + + // need to have localPlayer set for this test + Assert.That(NetworkClient.localPlayer, !Is.Null); + + // unspawn, wait one frame, localPlayer should be cleared + NetworkServer.Destroy(identity.gameObject); + yield return null; + Assert.IsTrue(NetworkClient.localPlayer is null, "local player should be set to c# null"); + } + + [UnityTest] + public IEnumerator LocalPlayerIsSetToNullAfterNetworkUnspawn() + { + // need spawned local player + CreateNetworkedAndSpawnPlayer(out _, out NetworkIdentity identity, NetworkServer.localConnection); + + // need to have localPlayer set for this test + Assert.That(NetworkClient.localPlayer, !Is.Null); + + // unspawn, wait one frame, localPlayer should be cleared + NetworkServer.UnSpawn(identity.gameObject); + yield return null; + Assert.IsTrue(NetworkClient.localPlayer is null, "local player should be set to c# null"); + } + } +} diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer_AsHost.cs.meta b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer_AsHost.cs.meta new file mode 100644 index 000000000..e9837dd30 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_LocalPlayer_AsHost.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6baedb85368e4347876f7b8b7c5110cb +timeCreated: 1622030080 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_Runtime_RegisterPrefab.cs b/Assets/Mirror/Tests/Runtime/ClientSceneTests_Runtime_RegisterPrefab.cs new file mode 100644 index 000000000..4e50852ca --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_Runtime_RegisterPrefab.cs @@ -0,0 +1,72 @@ +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Runtime.ClientSceneTests +{ + public class ClientSceneTests_Runtime_RegisterPrefab : ClientSceneTests_RegisterPrefabBase + { + [Test] + [TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)] + [TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId)] + public void Handler_AddsSpawnHandlerToDictionaryForRuntimeObject(RegisterPrefabOverload overload) + { + // create a scene object + CreateNetworked(out GameObject runtimeObject, out NetworkIdentity networkIdentity); + + Debug.Assert(networkIdentity.sceneId == 0, "SceneId was not set to 0"); + Debug.Assert(runtimeObject.GetComponent().sceneId == 0, "SceneId was not set to 0"); + + //test + CallRegisterPrefab(runtimeObject, overload); + Assert.IsTrue(NetworkClient.spawnHandlers.ContainsKey(anotherGuid)); + } + + [Test] + [TestCase(RegisterPrefabOverload.Prefab)] + [TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate)] + [TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate)] + public void ErrorForEmptyGuid(RegisterPrefabOverload overload) + { + // create a scene object + CreateNetworked(out GameObject runtimeObject, out _); + + //test + string msg = OverloadWithHandler(overload) + ? $"Can not Register handler for '{runtimeObject.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead" + : $"Can not Register '{runtimeObject.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead"; + + LogAssert.Expect(LogType.Error, msg); + CallRegisterPrefab(runtimeObject, overload); + } + + [Test] + [TestCase(RegisterPrefabOverload.Prefab_NewAssetId)] + public void PrefabNewGuid_AddsRuntimeObjectToDictionary(RegisterPrefabOverload overload) + { + // create a scene object + CreateNetworked(out GameObject runtimeObject, out NetworkIdentity networkIdentity); + + //test + CallRegisterPrefab(runtimeObject, overload); + + Assert.IsTrue(NetworkClient.prefabs.ContainsKey(anotherGuid)); + Assert.AreEqual(NetworkClient.prefabs[anotherGuid], runtimeObject); + + Assert.AreEqual(networkIdentity.assetId, anotherGuid); + } + + [Test] + [TestCase(RegisterPrefabOverload.Prefab_SpawnDelegate_NewAssetId)] + [TestCase(RegisterPrefabOverload.Prefab_SpawnHandlerDelegate_NewAssetId)] + public void Handler_AddsUnSpawnHandlerToDictionaryForRuntimeObject(RegisterPrefabOverload overload) + { + // create a scene object + CreateNetworked(out GameObject runtimeObject, out _); + + //test + CallRegisterPrefab(runtimeObject, overload); + Assert.IsTrue(NetworkClient.unspawnHandlers.ContainsKey(anotherGuid)); + } + } +} diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_Runtime_RegisterPrefab.cs.meta b/Assets/Mirror/Tests/Runtime/ClientSceneTests_Runtime_RegisterPrefab.cs.meta new file mode 100644 index 000000000..218c110c4 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_Runtime_RegisterPrefab.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c47e4b8fb9faba04792f9e4764a9c929 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/Mirror.Tests.Runtime.asmdef b/Assets/Mirror/Tests/Runtime/Mirror.Tests.Runtime.asmdef new file mode 100644 index 000000000..f852e83f7 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Mirror.Tests.Runtime.asmdef @@ -0,0 +1,22 @@ +{ + "name": "Mirror.Tests.Runtime", + "references": [ + "Mirror", + "Mirror.Components", + "Mirror.Tests.Common" + ], + "optionalUnityReferences": [ + "TestAssemblies" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "NSubstitute.dll", + "Castle.Core.dll", + "System.Threading.Tasks.Extensions.dll" + ], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Assets/Mirror/Tests/Runtime/Mirror.Tests.Runtime.asmdef.meta b/Assets/Mirror/Tests/Runtime/Mirror.Tests.Runtime.asmdef.meta new file mode 100644 index 000000000..fb6f3e343 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Mirror.Tests.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b8767e8f08282414ea6026200077ad4c +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/NetworkIdentityTests.cs b/Assets/Mirror/Tests/Runtime/NetworkIdentityTests.cs new file mode 100644 index 000000000..e92433b73 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/NetworkIdentityTests.cs @@ -0,0 +1,79 @@ +using System.Collections; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Runtime +{ + public class NetworkIdentityTests : MirrorPlayModeTest + { + GameObject gameObject; + NetworkIdentity identity; + + [UnitySetUp] + public override IEnumerator UnitySetUp() + { + yield return base.UnitySetUp(); + CreateNetworked(out gameObject, out identity); + yield return null; + } + + // prevents https://github.com/vis2k/Mirror/issues/1484 + [UnityTest] + public IEnumerator OnDestroyIsServerTrue() + { + // call OnStartServer so that isServer is true + identity.OnStartServer(); + Assert.That(identity.isServer, Is.True); + + // destroy it + // note: we need runtime .Destroy instead of edit mode .DestroyImmediate + // because we can't check isServer after DestroyImmediate + GameObject.Destroy(gameObject); + + // make sure that isServer is still true so we can save players etc. + Assert.That(identity.isServer, Is.True); + + yield return null; + // Confirm it has been destroyed + Assert.That(identity == null, Is.True); + } + + [UnityTest] + public IEnumerator OnDestroyIsServerTrueWhenNetworkServerDestroyIsCalled() + { + // call OnStartServer so that isServer is true + identity.OnStartServer(); + Assert.That(identity.isServer, Is.True); + + // destroy it + NetworkServer.Destroy(gameObject); + + // make sure that isServer is still true so we can save players etc. + Assert.That(identity.isServer, Is.True); + + yield return null; + // Confirm it has been destroyed + Assert.That(identity == null, Is.True); + } + + // imer: There's currently an issue with dropped/skipped serializations + // once a server has been running for around a week, this test should + // highlight the potential cause + [UnityTest] + public IEnumerator TestSerializationWithLargeTimestamps() + { + // 14 * 24 hours per day * 60 minutes per hour * 60 seconds per minute = 14 days + // NOTE: change this to 'float' to see the tests fail + int tick = 14 * 24 * 60 * 60; + NetworkIdentitySerialization serialization = identity.GetSerializationAtTick(tick); + // advance tick + ++tick; + NetworkIdentitySerialization serializationNew = identity.GetSerializationAtTick(tick); + + // if the serialization has been changed the tickTimeStamp should have moved + Assert.That(serialization.tick == serializationNew.tick, Is.False); + yield break; + } + } +} diff --git a/Assets/Mirror/Tests/Runtime/NetworkIdentityTests.cs.meta b/Assets/Mirror/Tests/Runtime/NetworkIdentityTests.cs.meta new file mode 100644 index 000000000..9f6cfef8d --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/NetworkIdentityTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef23592667dfe46948980606ccbe1860 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/NetworkManagerTests.cs b/Assets/Mirror/Tests/Runtime/NetworkManagerTests.cs new file mode 100644 index 000000000..8186f92d5 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/NetworkManagerTests.cs @@ -0,0 +1,45 @@ +using System.Collections; +using NUnit.Framework; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Runtime +{ + public class NetworkManagerTests + { + Scene activeScene; + + IEnumerator RunIsActiveSceneTest(string sceneToCheck, bool expected) + { + // wait for first frame to make sure scene is loaded + yield return null; + activeScene = SceneManager.GetActiveScene(); + + bool isActive = NetworkManager.IsSceneActive(sceneToCheck); + Assert.That(isActive, Is.EqualTo(expected)); + } + + [UnityTest] + public IEnumerator IsActiveSceneWorksWithSceneName() + { + yield return RunIsActiveSceneTest(activeScene.name, true); + yield return RunIsActiveSceneTest("NotActiveScene", false); + } + [UnityTest] + public IEnumerator IsActiveSceneWorksWithScenePath() + { + yield return RunIsActiveSceneTest(activeScene.path, true); + yield return RunIsActiveSceneTest("Assets/Mirror/Tests/Runtime/Scenes/NotActiveScene.unity", false); + } + [UnityTest] + public IEnumerator IsActiveSceneIsFalseForScenesWithSameNameButDifferentPath() + { + yield return RunIsActiveSceneTest($"Another/Path/To/{activeScene.path}", false); + } + [UnityTest] + public IEnumerator IsActiveSceneIsFalseForEmptyString() + { + yield return RunIsActiveSceneTest("", false); + } + } +} diff --git a/Assets/Mirror/Tests/Runtime/NetworkManagerTests.cs.meta b/Assets/Mirror/Tests/Runtime/NetworkManagerTests.cs.meta new file mode 100644 index 000000000..592ff091c --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/NetworkManagerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4cd917fdc9c513d438db90185784d87d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs b/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs new file mode 100644 index 000000000..03559af62 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs @@ -0,0 +1,112 @@ +using System.Collections; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirror.Tests.Runtime +{ + [TestFixture] + public class NetworkServerRuntimeTest : MirrorPlayModeTest + { + [UnitySetUp] + public override IEnumerator UnitySetUp() + { + yield return base.UnitySetUp(); + + // start server & client and wait 1 frame + NetworkServer.Listen(1); + ConnectHostClientBlockingAuthenticatedAndReady(); + yield return null; + } + + [UnityTest] + public IEnumerator DestroyPlayerForConnectionTest() + { + // create spawned player + CreateNetworkedAndSpawnPlayer(out GameObject player, out _, NetworkServer.localConnection); + + // destroy player for connection, wait 1 frame to unspawn and destroy + NetworkServer.DestroyPlayerForConnection(NetworkServer.localConnection); + yield return null; + + Assert.That(player == null, "Player should be destroyed with DestroyPlayerForConnection"); + } + + [UnityTest] + public IEnumerator RemovePlayerForConnectionTest() + { + // create spawned player + CreateNetworkedAndSpawnPlayer(out GameObject player, out _, NetworkServer.localConnection); + + // remove player for connection, wait 1 frame to unspawn + NetworkServer.RemovePlayerForConnection(NetworkServer.localConnection, false); + yield return null; + + Assert.That(player, Is.Not.Null, "Player should be not be destroyed"); + Assert.That(NetworkServer.localConnection.identity == null, "identity should be null"); + + // respawn player + NetworkServer.AddPlayerForConnection(NetworkServer.localConnection, player); + Assert.That(NetworkServer.localConnection.identity != null, "identity should not be null"); + } + + [UnityTest] + public IEnumerator Shutdown_DestroysAllSpawnedPrefabs() + { + // setup + NetworkServer.Listen(1); + + const string ValidPrefabAssetGuid = "33169286da0313d45ab5bfccc6cf3775"; + GameObject prefab = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(ValidPrefabAssetGuid)); + + NetworkIdentity identity1 = SpawnPrefab(prefab); + NetworkIdentity identity2 = SpawnPrefab(prefab); + + // shutdown, wait 1 frame for unity to destroy objects + NetworkServer.Shutdown(); + yield return null; + + // check that objects were destroyed + // need to use untiy `==` check + Assert.IsTrue(identity1 == null); + Assert.IsTrue(identity2 == null); + + Assert.That(NetworkServer.spawned, Is.Empty); + } + + NetworkIdentity SpawnPrefab(GameObject prefab) + { + GameObject clone1 = GameObject.Instantiate(prefab); + NetworkServer.Spawn(clone1); + NetworkIdentity identity1 = clone1.GetComponent(); + Assert.IsTrue(NetworkServer.spawned.ContainsValue(identity1)); + return identity1; + } + + [Test] + public void Shutdown_DisablesAllSpawnedPrefabs() + { + // setup + NetworkServer.Listen(1); + + // spawn two scene objects + CreateNetworkedAndSpawn(out _, out NetworkIdentity identity1); + CreateNetworkedAndSpawn(out _, out NetworkIdentity identity2); + identity1.sceneId = (ulong)identity1.GetHashCode(); + identity2.sceneId = (ulong)identity2.GetHashCode(); + + // test + NetworkServer.Shutdown(); + + // check that objects were disabled + // need to use untiy `==` check + Assert.IsTrue(identity1 != null); + Assert.IsTrue(identity2 != null); + Assert.IsFalse(identity1.gameObject.activeSelf); + Assert.IsFalse(identity1.gameObject.activeSelf); + + Assert.That(NetworkServer.spawned, Is.Empty); + } + } +} diff --git a/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs.meta b/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs.meta new file mode 100644 index 000000000..431c107b0 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a94e088c9596a284cb2cb960746ef2ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/Scenes.meta b/Assets/Mirror/Tests/Runtime/Scenes.meta new file mode 100644 index 000000000..0e9d6f423 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69531c106ae23bc438f789d213c11fce +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/Scenes/SceneObjectSpawningTestsScene.unity b/Assets/Mirror/Tests/Runtime/Scenes/SceneObjectSpawningTestsScene.unity new file mode 100644 index 000000000..be02d2d12 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Scenes/SceneObjectSpawningTestsScene.unity @@ -0,0 +1,381 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &8790187 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8790189} + - component: {fileID: 8790188} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &8790188 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8790187} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &8790189 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8790187} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &324336641 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 324336643} + - component: {fileID: 324336642} + m_Layer: 0 + m_Name: SceneNetworkIdentity + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &324336642 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 324336641} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 3353204156 + serverOnly: 0 + m_AssetId: + hasSpawned: 0 +--- !u!4 &324336643 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 324336641} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &417599908 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 417599911} + - component: {fileID: 417599910} + - component: {fileID: 417599909} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &417599909 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 417599908} + m_Enabled: 1 +--- !u!20 &417599910 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 417599908} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_GateFitMode: 2 + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &417599911 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 417599908} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &330407922257146537 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4064655512664549480, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_Name + value: TestNetworkManager + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8279465151912833971, guid: b243904f0fa5b04418e0e135533686b0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: b243904f0fa5b04418e0e135533686b0, type: 3} diff --git a/Assets/Mirror/Tests/Runtime/Scenes/SceneObjectSpawningTestsScene.unity.meta b/Assets/Mirror/Tests/Runtime/Scenes/SceneObjectSpawningTestsScene.unity.meta new file mode 100644 index 000000000..65474c733 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Scenes/SceneObjectSpawningTestsScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 11c6caa4a9760bf4a909e72138dfb8e5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/Scenes/TestNetworkManager.prefab b/Assets/Mirror/Tests/Runtime/Scenes/TestNetworkManager.prefab new file mode 100644 index 000000000..1eabd8217 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Scenes/TestNetworkManager.prefab @@ -0,0 +1,114 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4064655512664549480 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8279465151912833971} + - component: {fileID: 1233094718765946942} + - component: {fileID: 2509988586038888270} + m_Layer: 0 + m_Name: TestNetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8279465151912833971 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4064655512664549480} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1233094718765946942 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4064655512664549480} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8aab4c8111b7c411b9b92cf3dbc5bd4e, type: 3} + m_Name: + m_EditorClassIdentifier: + dontDestroyOnLoad: 0 + runInBackground: 0 + startOnHeadless: 0 + showDebugMessages: 0 + serverTickRate: 30 + offlineScene: + onlineScene: + transport: {fileID: 2509988586038888270} + networkAddress: localhost + maxConnections: 4 + disconnectInactiveConnections: 0 + disconnectInactiveTimeout: 60 + authenticator: {fileID: 0} + playerPrefab: {fileID: 796411093575107534, guid: 5d01c4bf42a9b434dac396c2ba1aea10, + type: 3} + autoCreatePlayer: 0 + playerSpawnMethod: 0 + spawnPrefabs: [] +--- !u!114 &2509988586038888270 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4064655512664549480} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6131cf1e8a1c14ef5b5253f86f3fc9c9, type: 3} + m_Name: + m_EditorClassIdentifier: + OnClientConnected: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine.CoreModule, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null + OnClientDataReceived: + m_PersistentCalls: + m_Calls: [] + m_TypeName: Mirror.ClientDataReceivedEvent, Mirror, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + OnClientError: + m_PersistentCalls: + m_Calls: [] + m_TypeName: Mirror.UnityEventException, Mirror, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + OnClientDisconnected: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine.CoreModule, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null + OnServerConnected: + m_PersistentCalls: + m_Calls: [] + m_TypeName: Mirror.UnityEventInt, Mirror, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + OnServerDataReceived: + m_PersistentCalls: + m_Calls: [] + m_TypeName: Mirror.ServerDataReceivedEvent, Mirror, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + OnServerError: + m_PersistentCalls: + m_Calls: [] + m_TypeName: Mirror.UnityEventIntException, Mirror, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + OnServerDisconnected: + m_PersistentCalls: + m_Calls: [] + m_TypeName: Mirror.UnityEventInt, Mirror, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null diff --git a/Assets/Mirror/Tests/Runtime/Scenes/TestNetworkManager.prefab.meta b/Assets/Mirror/Tests/Runtime/Scenes/TestNetworkManager.prefab.meta new file mode 100644 index 000000000..ac5d48478 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Scenes/TestNetworkManager.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b243904f0fa5b04418e0e135533686b0 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Runtime/Scenes/TestPlayer.prefab b/Assets/Mirror/Tests/Runtime/Scenes/TestPlayer.prefab new file mode 100644 index 000000000..77421a8c5 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Scenes/TestPlayer.prefab @@ -0,0 +1,48 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &796411093575107534 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6042682300829753709} + - component: {fileID: 9021640260471491865} + m_Layer: 0 + m_Name: TestPlayer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6042682300829753709 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 796411093575107534} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &9021640260471491865 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 796411093575107534} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 0 + serverOnly: 0 + m_AssetId: 5d01c4bf42a9b434dac396c2ba1aea10 diff --git a/Assets/Mirror/Tests/Runtime/Scenes/TestPlayer.prefab.meta b/Assets/Mirror/Tests/Runtime/Scenes/TestPlayer.prefab.meta new file mode 100644 index 000000000..949e8a566 --- /dev/null +++ b/Assets/Mirror/Tests/Runtime/Scenes/TestPlayer.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5d01c4bf42a9b434dac396c2ba1aea10 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: