From 9eaca6c5a37905f8cf3faaa1cc8e7f8ad356b82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20R=C3=B8rbech?= Date: Tue, 9 Apr 2024 13:57:58 +0200 Subject: [PATCH] [RNET-992] Add sync support for collections in mixed (#3556) --- .../Database/RealmValueWithCollections.cs | 171 ++++++- .../Sync/DataTypeSynchronizationTests.cs | 461 +++++++++++++++++- Tools/DeployApps/BaasClient.cs | 1 - wrappers/src/dictionary_cs.cpp | 12 +- wrappers/src/list_cs.cpp | 12 +- wrappers/src/object_cs.cpp | 12 +- 6 files changed, 636 insertions(+), 33 deletions(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 21f438e724..738387d1cf 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -253,6 +253,87 @@ public void List_AfterCreation_CanBeAssigned([Values(true, false)] bool isManage Assert.That(rvo.RealmValueProperty == newStringVal); } + [Test] + public void List_AfterCreation_CanBeReassigned([Values(true, false)] bool isManaged) + { + var initialList = (RealmValue)new List { 1, 2, 3 }; + var rvo = new RealmValueObject { RealmValueProperty = initialList }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + var actualList = rvo.RealmValueProperty; + Assert.That(initialList, Is.EqualTo(actualList).Using(_rvComparer)); + + var updatedList = (RealmValue)new List { 4, 5 }; + _realm.Write(() => + { + rvo.RealmValueProperty = updatedList; + }); + + actualList = rvo.RealmValueProperty; + Assert.That(updatedList, Is.EqualTo(actualList).Using(_rvComparer)); + } + + [Test] + public void List_AfterCreation_EmbeddedListCanBeReassigned([Values(true, false)] bool isManaged) + { + var initialList = (RealmValue)new List { new List { 1, 2, 3 } }; + var rvo = new RealmValueObject { RealmValueProperty = new List { initialList } }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + var actualEmbeddedList = rvo.RealmValueProperty.AsList()[0]; + Assert.That(initialList, Is.EqualTo(actualEmbeddedList).Using(_rvComparer)); + + var updatedList = (RealmValue)new List { 4, 5, 6 }; + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[0] = updatedList; + }); + + actualEmbeddedList = rvo.RealmValueProperty.AsList()[0]; + Assert.That(updatedList, Is.EqualTo(actualEmbeddedList).Using(_rvComparer)); + } + + [Test] + public void List_AfterCreation_EmbeddedDictionaryCanBeReassigned([Values(true, false)] bool isManaged) + { + var initialDictionary = (RealmValue)new Dictionary { { "key1", 1 } }; + var rvo = new RealmValueObject { RealmValueProperty = new List { initialDictionary } }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + var actualDictionary = rvo.RealmValueProperty.AsList()[0]; + Assert.That(initialDictionary, Is.EqualTo(actualDictionary).Using(_rvComparer)); + + var updatedDictionary = (RealmValue)new Dictionary { { "key2", 2 } }; + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[0] = updatedDictionary; + }); + + actualDictionary = rvo.RealmValueProperty.AsList()[0]; + Assert.That(updatedDictionary, Is.EqualTo(actualDictionary).Using(_rvComparer)); + } + [Test] public void List_WhenManaged_CanBeModified() { @@ -451,7 +532,6 @@ public void List_WhenManaged_WorksWithNotifications() callbacks.Clear(); } - #endregion #region Dictionary @@ -651,6 +731,93 @@ public void Dictionary_AfterCreation_CanBeAssigned([Values(true, false)] bool is Assert.That(rvo.RealmValueProperty == newStringVal); } + [Test] + public void Dictionary_AfterCreation_CanBeReassigned([Values(true, false)] bool isManaged) + { + var initialDictionary = (RealmValue)new Dictionary { { "key1", 1 } }; + var rvo = new RealmValueObject { RealmValueProperty = initialDictionary }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + var actualDictionary = rvo.RealmValueProperty; + Assert.That(initialDictionary, Is.EqualTo(actualDictionary).Using(_rvComparer)); + + var updatedDictionary = (RealmValue)new Dictionary { { "key2", 2 } }; + _realm.Write(() => + { + rvo.RealmValueProperty = updatedDictionary; + }); + + actualDictionary = rvo.RealmValueProperty; + Assert.That(updatedDictionary, Is.EqualTo(actualDictionary).Using(_rvComparer)); + } + + [Test] + public void Dictionary_AfterCreation_EmbeddedListCanBeReassigned([Values(true, false)] bool isManaged) + { + var initialList = new List { new List { 1, 2, 3 } }; + var rvo = new RealmValueObject + { + RealmValueProperty = new Dictionary { { "key", initialList } } + }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + var actualEmbeddedList = rvo.RealmValueProperty.AsDictionary()["key"].AsList(); + Assert.That(initialList, Is.EqualTo(actualEmbeddedList).Using(_rvComparer)); + + var updatedList = (RealmValue)new List { 4, 5, 6 }; + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary()["key"] = updatedList; + }); + + actualEmbeddedList = rvo.RealmValueProperty.AsDictionary()["key"].AsList(); + Assert.AreEqual(updatedList.AsList().Count, actualEmbeddedList.Count); + } + + [Test] + public void Dict_AfterCreation_EmbeddedDictionaryCanBeReassigned([Values(true, false)] bool isManaged) + { + var embeddedDictionary = new Dictionary { { "key1", 1 } }; + var rvo = new RealmValueObject + { + RealmValueProperty = new Dictionary { { "key", embeddedDictionary } } + }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + var actualEmbedded = rvo.RealmValueProperty.AsDictionary()["key"].AsDictionary(); + Assert.That(embeddedDictionary, Is.EqualTo(actualEmbedded).Using(_rvComparer)); + + var updatedDictionary = new Dictionary { { "key2", 2 } }; + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary()["key"] = updatedDictionary; + }); + + actualEmbedded = rvo.RealmValueProperty.AsDictionary()["key"].AsDictionary(); + Assert.That(updatedDictionary, Is.EqualTo(actualEmbedded).Using(_rvComparer)); + } + [Test] public void Dictionary_WhenManaged_CanBeModified() { @@ -958,7 +1125,7 @@ public void IndexedRealmValue_WithCollection_BasicTest() #endregion - private class RealmValueComparer : IEqualityComparer + internal class RealmValueComparer : IEqualityComparer { public bool Equals(RealmValue x, RealmValue y) { diff --git a/Tests/Realm.Tests/Sync/DataTypeSynchronizationTests.cs b/Tests/Realm.Tests/Sync/DataTypeSynchronizationTests.cs index d5b3056e0b..4d55c43431 100644 --- a/Tests/Realm.Tests/Sync/DataTypeSynchronizationTests.cs +++ b/Tests/Realm.Tests/Sync/DataTypeSynchronizationTests.cs @@ -24,14 +24,18 @@ using System.Threading.Tasks; using MongoDB.Bson; using NUnit.Framework; +using NUnit.Framework.Constraints; using Realms.Extensions; using Realms.Helpers; +using Realms.Tests.Database; namespace Realms.Tests.Sync { [TestFixture, Preserve(AllMembers = true)] public class DataTypeSynchronizationTests : SyncTestBase { + private readonly RealmValueWithCollections.RealmValueComparer _rvComparer = new(); + #region Boolean [Test] @@ -236,7 +240,7 @@ public class DataTypeSynchronizationTests : SyncTestBase public void Dict_Binary() => TestDictionaryCore(o => o.ByteArrayDict, TestHelpers.GetBytes(10), TestHelpers.GetBytes(15), (a, b) => a.SequenceEqual(b)); [Test] - public void Property_Binary() => TestPropertyCore(o => o.ByteArrayProperty, (o, rv) => o.ByteArrayProperty = rv, TestHelpers.GetBytes(5), TestHelpers.GetBytes(10), (a, b) => a!.SequenceEqual(b!)); + public void Property_Binary() => TestPropertyCore(o => o.ByteArrayProperty, (o, rv) => o.ByteArrayProperty = rv, TestHelpers.GetBytes(5), TestHelpers.GetBytes(10)); #endregion @@ -265,7 +269,7 @@ public class DataTypeSynchronizationTests : SyncTestBase #region RealmValue - public static readonly object[] RealmTestValues = new[] + public static readonly object[] RealmTestPrimitiveValues = { new object[] { (RealmValue)"abc", (RealmValue)10 }, new object[] { (RealmValue)new ObjectId("5f63e882536de46d71877979"), (RealmValue)new Guid("{F2952191-A847-41C3-8362-497F92CB7D24}") }, @@ -275,21 +279,85 @@ public class DataTypeSynchronizationTests : SyncTestBase new object[] { (RealmValue)12.5f, (RealmValue)15d }, }; - [TestCaseSource(nameof(RealmTestValues))] - public void List_RealmValue(RealmValue first, RealmValue second) => TestListCore(o => o.RealmValueList, Clone(first), Clone(second), equalsOverride: RealmValueEquals); - - [TestCaseSource(nameof(RealmTestValues))] - public void Set_RealmValue(RealmValue first, RealmValue second) => TestSetCore(o => o.RealmValueSet, Clone(first), Clone(second), equalsOverride: RealmValueEquals); - - [TestCaseSource(nameof(RealmTestValues))] - public void Dict_RealmValue(RealmValue first, RealmValue second) => TestDictionaryCore(o => o.RealmValueDict, Clone(first), Clone(second), equalsOverride: RealmValueEquals); - - [TestCaseSource(nameof(RealmTestValues))] - public void Property_RealmValue(RealmValue first, RealmValue second) => TestPropertyCore(o => o.RealmValueProperty, (o, rv) => o.RealmValueProperty = rv, Clone(first), Clone(second), equalsOverride: RealmValueEquals); + public static readonly object[] RealmTestValuesWithCollections = RealmTestPrimitiveValues.Concat(new[] + { + new object[] { (RealmValue)12.5f, (RealmValue)15d }, + new object[] + { + (RealmValue)new List + { + RealmValue.Null, + 1, + true, + "string", + new byte[] { 0, 1, 2 }, + new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero), + 1f, + 2d, + 3m, + new ObjectId("5f63e882536de46d71877979"), + Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), + new List { 1, true, "string" }, + new Dictionary + { + { "key1", RealmValue.Null }, + { "key2", 1 }, + { "key3", true }, + { "key4", "string" }, + } + }, + (RealmValue)15d + }, + new object[] + { + (RealmValue)new Dictionary + { + { "key1", RealmValue.Null }, + { "key2", 1 }, + { "key3", true }, + { "key4", "string" }, + { "key5", new byte[] { 0, 1, 2, 3 } }, + { "key6", new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero) }, + { "key7", 1f }, + { "key8", 2d }, + { "key9", 3m }, + { "key10", new ObjectId("5f63e882536de46d71877979") }, + { "key11", Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31") }, + { "key12", new List { 1, true, "string" } }, + { + "key13", new Dictionary + { + { "key1", RealmValue.Null }, + { "key2", 1 }, + { "key3", true }, + { "key4", "string" }, + } + }, + }, + (RealmValue)15d + }, + }).ToArray(); + + [TestCaseSource(nameof(RealmTestValuesWithCollections))] + public void List_RealmValue(RealmValue first, RealmValue second) => TestListCore(o => o.RealmValueList, + Clone(first), Clone(second), equalsOverride: RealmValueEquals); + + [TestCaseSource(nameof(RealmTestPrimitiveValues))] + public void Set_RealmValue(RealmValue first, RealmValue second) => TestSetCore(o => o.RealmValueSet, + Clone(first), Clone(second), equalsOverride: RealmValueEquals); + + [TestCaseSource(nameof(RealmTestValuesWithCollections))] + public void Dict_RealmValue(RealmValue first, RealmValue second) => TestDictionaryCore(o => o.RealmValueDict, + Clone(first), Clone(second), equalsOverride: RealmValueEquals); + + [TestCaseSource(nameof(RealmTestValuesWithCollections))] + public void Property_RealmValue(RealmValue first, RealmValue second) => TestPropertyCore( + o => o.RealmValueProperty, (o, rv) => o.RealmValueProperty = rv, Clone(first), Clone(second)); #endregion - private void TestListCore(Func> getter, T item1, T item2, Func? equalsOverride = null) + private void TestListCore(Func> getter, T item1, T item2, + Func? equalsOverride = null) { equalsOverride ??= (a, b) => a?.Equals(b) == true; @@ -474,15 +542,25 @@ private void TestDictionaryCore(Func(Func getter, Action setter, T item1, T item2, Func? equalsOverride = null) + private void TestPropertyCore(Func getter, Action setter, + T item1, T item2) { - equalsOverride ??= (a, b) => a?.Equals(b) == true; - SyncTestHelpers.RunBaasTestAsync(async () => { - var partition = Guid.NewGuid().ToString(); - var realm1 = await GetIntegrationRealmAsync(partition); - var realm2 = await GetIntegrationRealmAsync(partition); + var realm1 = await GetFLXIntegrationRealmAsync(); + + realm1.Subscriptions.Update(() => + { + realm1.Subscriptions.Add(realm1.All()); + realm1.Subscriptions.Add(realm1.All()); + }); + + var realm2 = await GetFLXIntegrationRealmAsync(); + realm2.Subscriptions.Update(() => + { + realm2.Subscriptions.Add(realm2.All()); + realm2.Subscriptions.Add(realm2.All()); + }); var obj1 = realm1.Write(() => { @@ -501,8 +579,8 @@ private void TestPropertyCore(Func getter, Action { @@ -514,8 +592,343 @@ private void TestPropertyCore(Func getter, Action + { + var realm1 = await GetFLXIntegrationRealmAsync(); + realm1.Subscriptions.Update(() => + { + realm1.Subscriptions.Add(realm1.All()); + realm1.Subscriptions.Add(realm1.All()); + }); + + var parent = new SyncAllTypesObject(); + var child = new IntPropertyObject(); + + var valuesValue = new List + { + 1, + "Realm", + child, + new List { 1, "Realm", child }, + new Dictionary + { + { "key1", 1 }, { "key2", "Realm" }, { "key3", child }, + } + }; + + realm1.Write(() => + { + realm1.Add(parent); + parent.StringProperty = "PARENT"; + parent.ObjectProperty = child; + parent.RealmValueProperty = valuesValue; + }); + + var parentId = parent.Id; + var childId = child.Id; + + await realm1.SyncSession.WaitForUploadAsync(); + realm1.Dispose(); + + var realm2 = await GetFLXIntegrationRealmAsync(); + realm2.Subscriptions.Update(() => + { + realm2.Subscriptions.Add(realm2.All()); + realm2.Subscriptions.Add(realm2.All()); + }); + await realm2.SyncSession.WaitForDownloadAsync(); + + var syncedParent = + await TestHelpers.WaitForConditionAsync(() => realm2.FindCore(parentId), + o => o != null); + var syncedChild = + await TestHelpers.WaitForConditionAsync(() => realm2.FindCore(childId), + o => o != null); + var syncedValues = syncedParent!.RealmValueProperty.AsList(); + Assert.AreEqual(valuesValue[0], syncedValues[0]); + Assert.AreEqual(valuesValue[1], syncedValues[1]); + Assert.AreEqual(childId, syncedValues[2].AsRealmObject().Id); + var nestedExpectedList = valuesValue[3].AsList(); + var nestedSyncedList = syncedValues[3].AsList(); + Assert.AreEqual(nestedExpectedList[0], nestedSyncedList[0]); + Assert.AreEqual(nestedExpectedList[1], nestedSyncedList[1]); + Assert.AreEqual(childId, nestedSyncedList[2].AsRealmObject().Id); + + var nestedExpectedDictionary = valuesValue[4].AsDictionary(); + var nestedSyncedDictionary = syncedValues[4].AsDictionary(); + Assert.AreEqual(nestedExpectedDictionary["key1"], nestedSyncedDictionary["key1"]); + Assert.AreEqual(nestedExpectedDictionary["key2"], nestedSyncedDictionary["key2"]); + Assert.AreEqual(childId, nestedSyncedDictionary["key3"].AsRealmObject().Id); + }); + } + + public static readonly IList RealmValueCollectionTestValues = new List() + { + "abc", + new ObjectId("5f63e882536de46d71877979"), + new byte[] { 0, 1, 2 }, + DateTimeOffset.FromUnixTimeSeconds(1616137641), + true, + RealmValue.Null, + 5m, + 12.5f, + 15d, + new List + { + RealmValue.Null, + 1, + true, + "string", + new byte[] { 0, 1, 2 }, + new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero), + 1f, + 2d, + 3m, + new ObjectId("5f63e882536de46d71877979"), + Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), + new List { 1, true, "string" }, + new Dictionary + { + { "key1", RealmValue.Null }, + { "key2", 1 }, + { "key3", true }, + { "key4", "string" }, + } + }, + new Dictionary + { + { "key1", RealmValue.Null }, + { "key2", 1 }, + { "key3", true }, + { "key4", "string" }, + { "key5", new byte[] { 0, 1, 2, 3 } }, + { "key6", new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero) }, + { "key7", 1f }, + { "key8", 2d }, + { "key9", 3m }, + { "key10", new ObjectId("5f63e882536de46d71877979") }, + { "key11", Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31") }, + { "key12", new List { 1, true, "string" } }, + { + "key13", new Dictionary + { + { "key1", RealmValue.Null }, + { "key2", 1 }, + { "key3", true }, + { "key4", "string" }, + } + }, + }, + }; + + [Test] + public void ListManipulations() + { + SyncTestHelpers.RunBaasTestAsync(async () => + { + var realm1 = await GetFLXIntegrationRealmAsync(); + var realm2 = await GetFLXIntegrationRealmAsync(); + + realm1.Subscriptions.Update(() => { realm1.Subscriptions.Add(realm1.All()); }); + realm2.Subscriptions.Update(() => { realm2.Subscriptions.Add(realm2.All()); }); + + var obj1 = realm1.Write(() => + { + var o = realm1.Add(new SyncAllTypesObject()); + o.RealmValueProperty = new List(); + return o; + }); + + var obj2 = await WaitForObjectAsync(obj1, realm2); + + // Append elements one by one and verify that they are synced + foreach (var realmTestValue in RealmValueCollectionTestValues) + { + realm1.Write(() => + { + obj1.RealmValueProperty.AsList().Add(realmTestValue); + }); + await realm1.SyncSession.WaitForUploadAsync(); + + await realm2.SyncSession.WaitForDownloadAsync(); + var expectedValues = obj1.RealmValueProperty.AsList(); + var actualValues = obj2.RealmValueProperty.AsList(); + Assert.That(expectedValues, Is.EqualTo(actualValues).Using(_rvComparer)); + } + + // Remove elements one by one and verify that changes are synced + for (int index = 0; index < RealmValueCollectionTestValues.Count; index++) + { + realm1.Write(() => + { + obj1.RealmValueProperty.AsList().RemoveAt(0); + }); + await realm1.SyncSession.WaitForUploadAsync(); + + await realm2.SyncSession.WaitForDownloadAsync(); + var expectedValues = obj1.RealmValueProperty.AsList(); + var actualValues = obj2.RealmValueProperty.AsList(); + Assert.That(expectedValues, Is.EqualTo(actualValues).Using(_rvComparer)); + } + + // Insert/override elements at index 0 and verify that changes are synced + foreach (var realmTestValue in RealmValueCollectionTestValues) + { + realm1.Write(() => + { + if (obj1.RealmValueProperty.AsList().Count == 0) + { + obj1.RealmValueProperty.AsList().Insert(0, realmTestValue); + } + else + { + obj1.RealmValueProperty.AsList()[0] = realmTestValue; + } + }); + await realm1.SyncSession.WaitForUploadAsync(); + + await realm2.SyncSession.WaitForDownloadAsync(); + var expectedValues = obj1.RealmValueProperty.AsList(); + var actualValues = obj2.RealmValueProperty.AsList(); + Assert.That(expectedValues, Is.EqualTo(actualValues).Using(_rvComparer)); + } + }); + } + + [Test] + public void DictionaryManipulations() + { + SyncTestHelpers.RunBaasTestAsync(async () => + { + var realm1 = await GetFLXIntegrationRealmAsync(); + realm1.Subscriptions.Update(() => + { + realm1.Subscriptions.Add(realm1.All()); + }); + + var realm2 = await GetFLXIntegrationRealmAsync(); + realm2.Subscriptions.Update(() => + { + realm2.Subscriptions.Add(realm2.All()); + }); + + var obj1 = realm1.Write(() => + { + var o = realm1.Add(new SyncAllTypesObject()); + o.RealmValueProperty = new Dictionary(); + return o; + }); + + var obj2 = await WaitForObjectAsync(obj1, realm2); + + for (int index = 0; index < RealmTestValuesWithCollections.Length; index++) + { + var realmTestValue = RealmValueCollectionTestValues[index]; + realm1.Write(() => { obj1.RealmValueProperty.AsDictionary()[$"{index}"] = realmTestValue; }); + + await realm1.SyncSession.WaitForUploadAsync(); + await realm2.SyncSession.WaitForDownloadAsync(); + var expectedValues = obj1.RealmValueProperty.AsDictionary(); + var actualValues = obj2.RealmValueProperty.AsDictionary(); + Assert.That(expectedValues, Is.EqualTo(actualValues).Using(_rvComparer)); + } + + for (int index = 0; index < RealmTestValuesWithCollections.Length; index++) + { + realm1.Write(() => { obj1.RealmValueProperty.AsDictionary().Remove($"{index}"); }); + await realm1.SyncSession.WaitForUploadAsync(); + await realm2.SyncSession.WaitForDownloadAsync(); + var expectedValues = obj1.RealmValueProperty.AsDictionary(); + var actualValues = obj2.RealmValueProperty.AsDictionary(); + Assert.That(expectedValues, Is.EqualTo(actualValues).Using(_rvComparer)); + } + }); + } + + [Test] + [Ignore("Crashes until https://github.com/realm/realm-core/issues/7488 is fixed")] + public void CollectionMerge() + { + SyncTestHelpers.RunBaasTestAsync(async () => + { + var realm1 = await GetFLXIntegrationRealmAsync(); + realm1.Subscriptions.Update(() => + { + realm1.Subscriptions.Add(realm1.All()); + }); + var realm2 = await GetFLXIntegrationRealmAsync(); + realm2.Subscriptions.Update(() => + { + realm2.Subscriptions.Add(realm2.All()); + }); + + var obj1 = realm1.Write(() => + { + var o = realm1.Add(new SyncAllTypesObject()); + o.RealmValueProperty = new Dictionary + { + { "list", new List { 1, 2, 3 } }, + { "dictionary", new Dictionary() { { "key1", 1 } } }, + }; + return o; + }); + + var obj2 = await WaitForObjectAsync(obj1, realm2); + + realm1.SyncSession.Stop(); + realm2.SyncSession.Stop(); + + realm1.Write(() => + { + var list = obj1.RealmValueProperty.AsDictionary()["list"].AsList(); + list.RemoveAt(0); + list.Add(4); + var dictionary = obj1.RealmValueProperty.AsDictionary()["dictionary"].AsDictionary(); + dictionary.Remove("key1"); + dictionary["key2"] = 2; + }); + realm2.Write(() => + { + var list = obj2.RealmValueProperty.AsDictionary()["list"].AsList(); + list.RemoveAt(0); + list.Add(5); + var dictionary = obj2.RealmValueProperty.AsDictionary()["dictionary"].AsDictionary(); + dictionary.Remove("key1"); + dictionary["key3"] = 3; + }); + + realm1.SyncSession.Start(); + realm2.SyncSession.Start(); + + await realm1.SyncSession.WaitForUploadAsync(); + await realm2.SyncSession.WaitForUploadAsync(); + await realm1.SyncSession.WaitForDownloadAsync(); + await realm2.SyncSession.WaitForDownloadAsync(); + + var list1 = obj1.RealmValueProperty.AsDictionary()["list"].AsList(); + var dictionary1 = obj1.RealmValueProperty.AsDictionary()["dictionary"].AsDictionary(); + var list2 = obj1.RealmValueProperty.AsDictionary()["list"].AsList(); + var dictionary2 = obj1.RealmValueProperty.AsDictionary()["dictionary"].AsDictionary(); + + Assert.That(list1, new NotConstraint(Contains.Item((RealmValue)1))); + Assert.That(list1, Contains.Item((RealmValue)2)); + Assert.That(list1, Contains.Item((RealmValue)3)); + Assert.That(list1, Contains.Item((RealmValue)4)); + Assert.That(list1, Contains.Item((RealmValue)5)); + Assert.That(list1, Is.EqualTo(list2).Using(_rvComparer)); + + Assert.That(dictionary1, new NotConstraint(Contains.Key("key1"))); + Assert.That(dictionary1, Contains.Key("key2")); + Assert.That(dictionary1, Contains.Key("key3")); + Assert.That(dictionary1, Is.EqualTo(dictionary2).Using(_rvComparer)); }); } diff --git a/Tools/DeployApps/BaasClient.cs b/Tools/DeployApps/BaasClient.cs index ab1664ec06..e300f0359a 100644 --- a/Tools/DeployApps/BaasClient.cs +++ b/Tools/DeployApps/BaasClient.cs @@ -555,7 +555,6 @@ public async Task SetAutomaticRecoveryEnabled(BaasApp app, bool enabled) { development_mode_enabled = true, }); - return (app, mongoServiceId); } diff --git a/wrappers/src/dictionary_cs.cpp b/wrappers/src/dictionary_cs.cpp index cec6395d17..c9bc2b94e7 100644 --- a/wrappers/src/dictionary_cs.cpp +++ b/wrappers/src/dictionary_cs.cpp @@ -84,11 +84,19 @@ extern "C" { switch (type) { case realm::binding::realm_value_type::RLM_TYPE_LIST: + { dictionary.insert_collection(dict_key, CollectionType::List); - return new List(dictionary.get_list(dict_key)); + auto innerList = new List(dictionary.get_list(dict_key)); + innerList->remove_all(); + return innerList; + } case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + { dictionary.insert_collection(dict_key, CollectionType::Dictionary); - return new object_store::Dictionary(dictionary.get_dictionary(dict_key)); + auto innerDict = new object_store::Dictionary(dictionary.get_dictionary(dict_key)); + innerDict->remove_all(); + return innerDict; + } default: REALM_TERMINATE("Invalid collection type"); } diff --git a/wrappers/src/list_cs.cpp b/wrappers/src/list_cs.cpp index c296b7544f..591954ed10 100644 --- a/wrappers/src/list_cs.cpp +++ b/wrappers/src/list_cs.cpp @@ -94,11 +94,19 @@ REALM_EXPORT void* list_set_collection(List& list, size_t list_ndx, realm_value_ switch (type) { case realm::binding::realm_value_type::RLM_TYPE_LIST: + { list.set_collection(list_ndx, CollectionType::List); - return new List(list.get_list(list_ndx)); + auto innerList = new List(list.get_list(list_ndx)); + innerList->remove_all(); + return innerList; + } case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + { list.set_collection(list_ndx, CollectionType::Dictionary); - return new object_store::Dictionary(list.get_dictionary(list_ndx)); + auto innerDict = new object_store::Dictionary(list.get_dictionary(list_ndx)); + innerDict->remove_all(); + return innerDict; + } default: REALM_TERMINATE("Invalid collection type"); } diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index 4ab900189f..59db3e8a6a 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -184,11 +184,19 @@ extern "C" { switch (type) { case realm::binding::realm_value_type::RLM_TYPE_LIST: + { object.get_obj().set_collection(prop.column_key, CollectionType::List); - return new List(object.realm(), object.get_obj(), prop.column_key); + auto innerList = new List(object.realm(), object.get_obj(), prop.column_key); + innerList->remove_all(); + return innerList; + } case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + { object.get_obj().set_collection(prop.column_key, CollectionType::Dictionary); - return new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key); + auto innerDict = new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key); + innerDict->remove_all(); + return innerDict; + } default: REALM_TERMINATE("Invalid collection type"); }