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(); } } } } }