rd-net/Test.RdFramework/RdCollectionsTest.cs (606 lines of code) (raw):

using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; using JetBrains.Collections; using JetBrains.Collections.Viewable; using JetBrains.Core; using JetBrains.Lifetimes; using JetBrains.Rd; using JetBrains.Rd.Base; using JetBrains.Rd.Impl; using JetBrains.Threading; using NUnit.Framework; namespace Test.RdFramework; [TestFixture] [Apartment(System.Threading.ApartmentState.STA)] public class RdCollectionsTest : RdFrameworkTestBase { private static readonly int ourKey = 1; protected override ISerializers CreateSerializers(bool isServer) { var serializers = base.CreateSerializers(isServer); Register(RdMap<int, RdList<RdProperty<RdSet<int>>>>.Read, RdMap<int, RdList<RdProperty<RdSet<int>>>>.Write); Register(RdList<RdProperty<RdSet<int>>>.Read, RdList<RdProperty<RdSet<int>>>.Write); Register(RdSet<int>.Read, RdSet<int>.Write); Register(RdProperty<RdSet<int>>.Read, RdProperty<RdSet<int>>.Write); Register(RdProperty<RdProperty<RdSet<int>>>.Read, RdProperty<RdProperty<RdSet<int>>>.Write); Register(RdProperty<RdProperty<RdProperty<RdSet<int>>>>.Read, RdProperty<RdProperty<RdProperty<RdSet<int>>>>.Write); Register(AsyncRdProperty<int>.Read, AsyncRdProperty<int>.Write); Register(AsyncRdMap<int, string>.Read, AsyncRdMap<int, string>.Write); Register(AsyncRdSet<int>.Read, AsyncRdSet<int>.Write); Register(RdList<RdProperty<int>>.Read, RdList<RdProperty<int>>.Write); Register(RdProperty<int>.Read, RdProperty<int>.Write); return serializers; void Register<T>(CtxReadDelegate<T> read, CtxWriteDelegate<T> write) { serializers.Register(read, write, RdId.DefineByFqn(typeof(T)).Value); } } protected override IScheduler CreateScheduler(bool isServer) { return new PumpScheduler(Thread.CurrentThread); } protected override IScheduler TryCreateWireScheduler(bool isServer) { return new MyScheduler(Thread.CurrentThread); } [Flags] enum SchedulerKind { Client = 1, Server = 2, Both = Client | Server } private void SetSchedulerActive(SchedulerKind kind, Action action) => SetSchedulerActive(kind, () => { action(); return Unit.Instance; }); private T SetSchedulerActive<T>(SchedulerKind kind, Func<T> func) { return Lifetime.Using(lifetime => { if ((kind & SchedulerKind.Client) != 0) ((PumpScheduler)ClientProtocol.Scheduler).SetActive(lifetime); if ((kind & SchedulerKind.Server) != 0) ((PumpScheduler)ServerProtocol.Scheduler).SetActive(lifetime); return func(); }); } [Test] public void SetInitializedCollectionsTest() { var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime, NewRdProperty<RdMap<int, RdList<RdProperty<RdSet<int>>>>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime, NewRdProperty<RdMap<int, RdList<RdProperty<RdSet<int>>>>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientMap = NewRdMap<int, RdList<RdProperty<RdSet<int>>>>(); var clientList = NewRdList<RdProperty<RdSet<int>>>(); var clientProperty = NewRdProperty<RdSet<int>>(); var clientSet = NewRdSet<int>(); clientSet.Add(1); clientSet.Add(2); clientSet.Add(3); clientProperty.Value = clientSet; clientList.Add(clientProperty); clientMap[2] = clientList; SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientMap; }); Assert.IsFalse(serverTopLevelProperty.Maybe.HasValue); PumpAllProtocols(true); var serverMap = serverTopLevelProperty.Value; Assert.AreEqual(1, serverMap.Count); Assert.AreEqual(2, serverMap.Keys.Single()); var serverList = serverMap.Values.Single(); Assert.AreEqual(1, serverList.Count); var serverProperty = serverList[0]; Assert.IsTrue(serverProperty.Maybe.HasValue); var serverSet = serverProperty.Value; Assert.AreEqual(3, serverSet.Count); Assert.IsTrue(serverSet.Contains(1)); Assert.IsTrue(serverSet.Contains(2)); Assert.IsTrue(serverSet.Contains(3)); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = null; }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientMap.BindState); Assert.AreEqual(BindState.NotBound,serverMap.BindState); Assert.AreEqual(BindState.NotBound,clientList.BindState); Assert.AreEqual(BindState.NotBound,serverList.BindState); Assert.AreEqual(BindState.NotBound,clientProperty.BindState); Assert.AreEqual(BindState.NotBound,serverProperty.BindState); Assert.AreEqual(BindState.NotBound,clientSet.BindState); Assert.AreEqual(BindState.NotBound,serverSet.BindState); } [Test] public void UnbindTest() { var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime, NewRdProperty<RdMap<int, RdList<RdProperty<RdSet<int>>>>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime, NewRdProperty<RdMap<int, RdList<RdProperty<RdSet<int>>>>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientMap = NewRdMap<int, RdList<RdProperty<RdSet<int>>>>(); clientMap.ValueCanBeNull = true; var clientList = NewRdList<RdProperty<RdSet<int>>>(); clientList.ValueCanBeNull = true; var clientProperty = NewRdProperty<RdSet<int>>(); clientProperty.ValueCanBeNull = true; var clientSet = NewRdSet<int>(); clientSet.Add(1); clientSet.Add(2); clientSet.Add(3); clientProperty.Value = clientSet; clientList.Add(clientProperty); clientMap[2] = clientList; SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientMap; }); Assert.IsFalse(serverTopLevelProperty.Maybe.HasValue); PumpAllProtocols(true); var serverMap = serverTopLevelProperty.Value; Assert.AreEqual(1, serverMap.Count); Assert.AreEqual(2, serverMap.Keys.Single()); var serverList = serverMap.Values.Single(); Assert.AreEqual(1, serverList.Count); var serverProperty = serverList[0]; Assert.IsTrue(serverProperty.Maybe.HasValue); var serverSet = serverProperty.Value; Assert.AreEqual(3, serverSet.Count); Assert.IsTrue(serverSet.Contains(1)); Assert.IsTrue(serverSet.Contains(2)); Assert.IsTrue(serverSet.Contains(3)); SetSchedulerActive(SchedulerKind.Client, () => { clientProperty.Value = NewRdSet<int>(); }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientSet.BindState); Assert.AreEqual(BindState.NotBound,serverSet.BindState); Assert.AreEqual(BindState.Bound,clientProperty.BindState); Assert.AreEqual(BindState.Bound,serverProperty.BindState); clientSet = clientProperty.Value; serverSet = serverProperty.Value; Assert.AreEqual(BindState.Bound,clientSet.BindState); Assert.AreEqual(BindState.Bound,serverSet.BindState); SetSchedulerActive(SchedulerKind.Client, () => { var oldValue = clientProperty.Value; clientProperty.Value = null; clientProperty.Value = oldValue; clientProperty.Value = null; }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientSet.BindState); Assert.AreEqual(BindState.NotBound,serverSet.BindState); Assert.AreEqual(BindState.Bound,clientProperty.BindState); Assert.AreEqual(BindState.Bound,serverProperty.BindState); SetSchedulerActive(SchedulerKind.Client, () => { var oldValue = clientList[0]; clientList[0] = NewRdProperty<RdSet<int>>(); clientList[0] = oldValue; clientList[0] = NewRdProperty<RdSet<int>>(); }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientProperty.BindState); Assert.AreEqual(BindState.NotBound,serverProperty.BindState); Assert.AreEqual(BindState.Bound,clientList.BindState); Assert.AreEqual(BindState.Bound,serverList.BindState); clientProperty = clientList[0]; serverProperty = serverList[0]; Assert.AreEqual(BindState.Bound,clientProperty.BindState); Assert.AreEqual(BindState.Bound,serverProperty.BindState); SetSchedulerActive(SchedulerKind.Client, () => { clientList.Clear(); }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientProperty.BindState); Assert.AreEqual(BindState.NotBound,serverProperty.BindState); Assert.AreEqual(BindState.Bound,clientList.BindState); Assert.AreEqual(BindState.Bound,serverList.BindState); SetSchedulerActive(SchedulerKind.Client, () => { var oldValue = clientMap[clientMap.Keys.Single()]; clientMap[clientMap.Keys.Single()] = NewRdList<RdProperty<RdSet<int>>>(); clientMap[clientMap.Keys.Single()] = oldValue; clientMap[clientMap.Keys.Single()] = NewRdList<RdProperty<RdSet<int>>>(); }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientList.BindState); Assert.AreEqual(BindState.NotBound,serverList.BindState); Assert.AreEqual(BindState.Bound,clientMap.BindState); Assert.AreEqual(BindState.Bound,serverMap.BindState); clientList = clientMap.Values.Single(); serverList = serverMap.Values.Single(); Assert.AreEqual(BindState.Bound,clientList.BindState); Assert.AreEqual(BindState.Bound,serverList.BindState); SetSchedulerActive(SchedulerKind.Client, () => { clientMap.Clear(); }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientList.BindState); Assert.AreEqual(BindState.NotBound,serverList.BindState); Assert.AreEqual(BindState.Bound,clientMap.BindState); Assert.AreEqual(BindState.Bound,serverMap.BindState); SetSchedulerActive(SchedulerKind.Client, () => { var oldValue = clientTopLevelProperty.Value; clientTopLevelProperty.Value = null; clientTopLevelProperty.Value = oldValue; clientTopLevelProperty.Value = null; }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientMap.BindState); Assert.AreEqual(BindState.NotBound,serverMap.BindState); } [Test] public void RdListRemoveAtTest() { var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime, NewRdProperty<RdList<RdProperty<int>>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime, NewRdProperty<RdList<RdProperty<int>>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientList = NewRdList<RdProperty<int>>(); clientList.ValueCanBeNull = true; var clientProperty1 = NewRdProperty<int>(); clientProperty1.ValueCanBeNull = true; var clientProperty2 = NewRdProperty<int>(); clientProperty2.ValueCanBeNull = true; clientList.Add(clientProperty1); clientList.Add(clientProperty2); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientList; }); Assert.IsFalse(serverTopLevelProperty.Maybe.HasValue); PumpAllProtocols(true); var serverList = serverTopLevelProperty.Value; Assert.AreEqual(2, serverList.Count); var serverProperty1 = serverList[0]; var serverProperty2 = serverList[1]; Assert.AreEqual(BindState.Bound, clientProperty1.BindState); Assert.AreEqual(BindState.Bound, clientProperty2.BindState); Assert.AreEqual(BindState.Bound, serverProperty1.BindState); Assert.AreEqual(BindState.Bound, serverProperty2.BindState); SetSchedulerActive(SchedulerKind.Client, () => { clientList.RemoveAt(0); }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound, clientProperty1.BindState); Assert.AreEqual(BindState.NotBound, serverProperty1.BindState); Assert.AreEqual(BindState.Bound, clientProperty2.BindState); Assert.AreEqual(BindState.Bound, serverProperty2.BindState); Assert.AreEqual(BindState.Bound, clientList.BindState); Assert.AreEqual(BindState.Bound, serverList.BindState); SetSchedulerActive(SchedulerKind.Client, () => { clientList.RemoveAt(0); }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound, clientProperty1.BindState); Assert.AreEqual(BindState.NotBound, serverProperty1.BindState); Assert.AreEqual(BindState.NotBound, clientProperty2.BindState); Assert.AreEqual(BindState.NotBound, serverProperty2.BindState); Assert.AreEqual(BindState.Bound, clientList.BindState); Assert.AreEqual(BindState.Bound, serverList.BindState); } [Test] public void ChangeCollectionsTest() { ClientWire.AutoTransmitMode = true; ServerWire.AutoTransmitMode = true; var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime, NewRdProperty<RdMap<int, RdList<RdProperty<RdSet<int>>>>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime, NewRdProperty<RdMap<int, RdList<RdProperty<RdSet<int>>>>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientMap = NewRdMap<int, RdList<RdProperty<RdSet<int>>>>();; var clientList = NewRdList<RdProperty<RdSet<int>>>(); var clientProperty = NewRdProperty<RdSet<int>>(); var clientSet = NewRdSet<int>(); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientMap; clientMap[2] = clientList; clientList.Add(clientProperty); clientProperty.Value = clientSet; clientSet.Add(1); clientSet.Add(2); clientSet.Add(3); }); PumpAllProtocols(true); var serverMap = serverTopLevelProperty.Value; Assert.AreEqual(1, serverMap.Count); Assert.AreEqual(2, serverMap.Keys.Single()); var serverList = serverMap.Values.Single(); Assert.AreEqual(1, serverList.Count); var serverProperty = serverList[0]; Assert.IsTrue(serverProperty.Maybe.HasValue); var serverSet = serverProperty.Value; Assert.AreEqual(3, serverSet.Count); Assert.IsTrue(serverSet.Contains(1)); Assert.IsTrue(serverSet.Contains(2)); Assert.IsTrue(serverSet.Contains(3)); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = null; }); PumpAllProtocols(true); Assert.AreEqual(BindState.NotBound,clientMap.BindState); Assert.AreEqual(BindState.NotBound,serverMap.BindState); Assert.AreEqual(BindState.NotBound,clientList.BindState); Assert.AreEqual(BindState.NotBound,serverList.BindState); Assert.AreEqual(BindState.NotBound,clientProperty.BindState); Assert.AreEqual(BindState.NotBound,serverProperty.BindState); Assert.AreEqual(BindState.NotBound,clientSet.BindState); Assert.AreEqual(BindState.NotBound,serverSet.BindState); } [Test] public void ReassignBindableValue() { ClientWire.AutoTransmitMode = false; ServerWire.AutoTransmitMode = false; var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime, NewRdProperty<RdProperty<RdSet<int>>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime, NewRdProperty<RdProperty<RdSet<int>>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientNested1 = NewRdProperty<RdSet<int>>(); var clientNested2 = NewRdProperty<RdSet<int>>(); var clientSet = NewRdSet<int>(); clientNested1.Value = clientSet; clientNested2.Value = clientSet; SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientNested1; PumpAllProtocols(true); clientTopLevelProperty.Value = clientNested2; PumpAllProtocols(true); }); } [Test] public void PropertyTest() { ClientWire.AutoTransmitMode = true; ServerWire.AutoTransmitMode = true; var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime,NewRdProperty<RdProperty<RdProperty<RdSet<int>>>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime,NewRdProperty<RdProperty<RdProperty<RdSet<int>>>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientNested1 = NewRdProperty<RdProperty<RdSet<int>>>(); var clientNested2 = NewRdProperty<RdSet<int>>(); var clientSet = NewRdSet<int>(); clientNested2.Value = clientSet; clientNested1.Value = clientNested2; RdProperty<RdProperty<RdSet<int>>> serverProperty = null; SetSchedulerActive(SchedulerKind.Server, () => { serverTopLevelProperty.View(LifetimeDefinition.Lifetime, (mapLifetime, property) => { serverProperty = property; }); }); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientNested1; }); PumpAllProtocols(false); Assert.NotNull(serverProperty); } [Test] public void AsyncPropertyTest() { ClientWire.AutoTransmitMode = true; ServerWire.AutoTransmitMode = true; var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime,NewRdProperty<AsyncRdProperty<int>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime,NewRdProperty<AsyncRdProperty<int>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientAsyncProperty = NewAsyncRdProperty<int>(); AsyncRdProperty<int> serverAsyncProperty = null; SetSchedulerActive(SchedulerKind.Server, () => { serverTopLevelProperty.View(LifetimeDefinition.Lifetime, (mapLifetime, property) => { serverAsyncProperty = property; }); }); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientAsyncProperty; }); PumpAllProtocols(true); Assert.NotNull(serverAsyncProperty); Assert.IsFalse(serverAsyncProperty.Maybe.HasValue); clientAsyncProperty.Value = 123; Assert.IsTrue(serverAsyncProperty.Maybe.HasValue); Assert.AreEqual(123, serverAsyncProperty.Maybe.Value); } [Test] public void AsyncMapTest() { ClientWire.AutoTransmitMode = true; ServerWire.AutoTransmitMode = true; var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime,NewRdProperty<AsyncRdMap<int, string>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime,NewRdProperty<AsyncRdMap<int, string>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientAsyncMap = NewAsyncRdMap<int, string>(); AsyncRdMap<int, string> serverAsyncMap = null; SetSchedulerActive(SchedulerKind.Server, () => { serverTopLevelProperty.View(LifetimeDefinition.Lifetime, (mapLifetime, map) => { serverAsyncMap = map; }); }); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientAsyncMap; }); PumpAllProtocols(true); Assert.NotNull(serverAsyncMap); Assert.AreEqual(0, serverAsyncMap.Count); clientAsyncMap[0] = 123.ToString(); Assert.AreEqual(1, serverAsyncMap.Count); Assert.AreEqual("123", serverAsyncMap[0]); } [Test] public void AsyncSetTest() { ClientWire.AutoTransmitMode = true; ServerWire.AutoTransmitMode = true; var serverTopLevelProperty = BindToServer(LifetimeDefinition.Lifetime,NewRdProperty<AsyncRdSet<int>>(), ourKey); serverTopLevelProperty.ValueCanBeNull = true; var clientTopLevelProperty = BindToClient(LifetimeDefinition.Lifetime,NewRdProperty<AsyncRdSet<int>>(), ourKey); clientTopLevelProperty.ValueCanBeNull = true; var clientAsyncSet = NewAsyncRdSet<int>(); AsyncRdSet<int> serverAsyncSet = null; SetSchedulerActive(SchedulerKind.Server, () => { serverTopLevelProperty.View(LifetimeDefinition.Lifetime, (mapLifetime, set) => { serverAsyncSet = set; }); }); SetSchedulerActive(SchedulerKind.Client, () => { clientTopLevelProperty.Value = clientAsyncSet; }); PumpAllProtocols(true); Assert.NotNull(serverAsyncSet); Assert.AreEqual(0, serverAsyncSet.Count); clientAsyncSet.Add(123); Assert.AreEqual(1, serverAsyncSet.Count); Assert.IsTrue(serverAsyncSet.Contains(123)); } private void PumpAllProtocols(bool transmitMessages) { var clientScheduler = (PumpScheduler)ClientProtocol.Scheduler; var serverScheduler = (PumpScheduler)ServerProtocol.Scheduler; do { if (transmitMessages) { if (ClientWire.HasMessages) ClientWire.TransmitAllMessages(); if (ServerWire.HasMessages) ServerWire.TransmitAllMessages(); } } while (clientScheduler.PumpOnce() || serverScheduler.PumpOnce() || ClientWire.HasMessages || ServerWire.HasMessages); } public override void TearDown() { ((PumpScheduler)ClientProtocol.Scheduler).Dispose(); ((PumpScheduler)ServerProtocol.Scheduler).Dispose(); base.TearDown(); } private class MyScheduler : IScheduler { private readonly Thread myThread; private int myCount; public MyScheduler(Thread thread) { myThread = thread; } public void Queue(Action action) { AssertThread(); myCount++; try { action(); } finally { myCount--; } } private void AssertThread() { Assert.AreEqual(myThread, Thread.CurrentThread, $"Wrong thread. Expected: {myThread.ToThreadString()}, Actual: {Thread.CurrentThread.ToThreadString()}"); } public bool IsActive { get { AssertThread(); return myCount > 0; } } public bool OutOfOrderExecution => false; } private class PumpScheduler : IScheduler, IDisposable { private readonly Queue<Action> myActions = new(); private bool myDisposed = false; private readonly Thread myThread; private int myCount; public PumpScheduler(Thread thread) { myThread = thread; } public void Queue(Action action) { AssertThread(); if (myDisposed) { action(); return; } myActions.Enqueue(() => { myCount++; try { action(); } finally { myCount--; } }); } private void AssertThread() { Assert.AreEqual(myThread, Thread.CurrentThread, $"Wrong thread. Expected: {myThread.ToThreadString()}, Actual: {Thread.CurrentThread.ToThreadString()}"); } public void SetActive(Lifetime lifetime) { AssertThread(); lifetime.Bracket(() => { return myCount++; }, () => { AssertThread(); myCount--; }); } public bool PumpOnce() { AssertThread(); if (myActions.TryDequeue(out var action)) { action(); return true; } return false; } public void PumpAll() { while (PumpOnce()) { } } public bool IsActive { get { AssertThread(); return myCount > 0; } } public bool OutOfOrderExecution => false; public void Dispose() { myCount = 1; myDisposed = true; } } }