public static LiveListState <T> CreateState(ObservableType executionType, IBaseListCollection <T> list, int startingIndex, TimeSpan timeout)
        {
            var tracker = new ListCollectionIndexOffsetTracker <T>();

            tracker.SetOriginalIndexAndResetCurrent(startingIndex);

            list.CollectionChangedEvent += tracker.HandleEvent;
            list.CollectionChangedEvent += (_, e) =>
            {
                if (e != null)
                {
                    lock (tracker)
                    {
                        Monitor.Pulse(tracker);
                    }
                }
            };
            return(new LiveListState <T>()
            {
                List = list,
                Tracker = tracker,
                Timeout = timeout,
                ExecutionType = executionType
            });
        }
        public void IndexTrackerLockThreadPotentiallyAfter()
        {
            var tracker = new ListCollectionIndexOffsetTracker <int>();

            tracker.SetOriginalIndexAndResetCurrent(1);

            Assert.That(tracker.CurrentIndex, Is.EqualTo(1));
            Assert.That(tracker.OriginalIndex, Is.EqualTo(1));

            tracker.ApplyContextLock(t =>
            {
                t.SetOriginalIndexAndResetCurrent(t.CurrentIndex + 1);
            });

            // We'll skip mid-tests to ensure there's a chance a lock contention would occur

            var state = Task.Run(() =>
            {
                tracker.HandleEvent(null, ListCollectionEventArgs <int> .CreateInsertEvent(0, 5)); // Should happen after or as close to the set operation as possible (high chance it will happen after)
                return(0);
            }).ContinueWith(execState =>
            {
                var eState = execState.Result;
                Assert.That(eState, Is.Zero);

                Assert.That(tracker.CurrentIndex, Is.EqualTo(3));
                Assert.That(tracker.OriginalIndex, Is.EqualTo(2));

                return(eState + 1);
            }).Result;

            Assert.That(state, Is.EqualTo(1)); // We do this test to ensure that the ContinueWith is always executed. There may be task continuation options to enforce this, but it's probably easier to understand "the state will only be correct if ContinueWith is executed"
        }
        public void IndexTrackerSetIndices()
        {
            var index = new ListCollectionIndexOffsetTracker <int>();

            index.SetOriginalIndexAndResetCurrent(20);

            Assert.That(index.OriginalIndex, Is.EqualTo(20));
            Assert.That(index.CurrentIndex, Is.EqualTo(20));
            Assert.That(index.Offset, Is.Zero);
        }
        public void IndexTrackerResetCurrentIndex()
        {
            var index = new ListCollectionIndexOffsetTracker <int>
            {
                OriginalIndex = 2
            };

            index.ResetCurrentIndex();

            Assert.That(index.OriginalIndex, Is.EqualTo(2));
            Assert.That(index.CurrentIndex, Is.EqualTo(2));
            Assert.That(index.Offset, Is.Zero);
        }
        public void IndexTrackerConfidenceTest()
        {
            var index = new ListCollectionIndexOffsetTracker <int>();

            Assert.That(index.OriginalIndex, Is.Zero);
            Assert.That(index.CurrentIndex, Is.Zero);
            Assert.That(index.Offset, Is.Zero);

            index.OriginalIndex = 2;

            Assert.That(index.OriginalIndex, Is.EqualTo(2));
            Assert.That(index.CurrentIndex, Is.Zero);
            Assert.That(index.Offset, Is.EqualTo(-2));
        }
        public void IndexTrackerInsertAfter()
        {
            var list = new List <int>()
            {
                10,
                20,
                30
            };

            var collection = list.AsListCollection();

            var capture = new EventCapture <ListCollectionEventArgs <int> >();

            collection.CollectionChangedEvent += capture.CaptureEventHandler;
            Assert.That(capture.CapturedEvents, Is.Empty);

            var tracker = new ListCollectionIndexOffsetTracker <int>();

            tracker.SetOriginalIndexAndResetCurrent(1);
            collection.CollectionChangedEvent += tracker.HandleEvent;

            Assert.That(tracker.OriginalIndex, Is.EqualTo(1));
            Assert.That(tracker.CurrentIndex, Is.EqualTo(1));
            Assert.That(tracker.Offset, Is.Zero);

            Assert.That(collection[0], Is.EqualTo(10));
            Assert.That(collection[1], Is.EqualTo(20));
            Assert.That(collection[2], Is.EqualTo(30));

            collection.Insert(2, 40);

            Assert.That(collection[0], Is.EqualTo(10));
            Assert.That(collection[1], Is.EqualTo(20));
            Assert.That(collection[2], Is.EqualTo(40));

            Assert.That(capture.CapturedEvents, Is.Not.Empty.And.Count.EqualTo(1));

            var eventObject = capture.CapturedEvents[0].eventObject;

            Assert.That(eventObject.Type, Is.EqualTo(ListCollectionEventType.Insert));
            Assert.That(eventObject.Index, Is.EqualTo(2));
            Assert.That(eventObject.Value, Is.EqualTo(40));

            Assert.That(tracker.OriginalIndex, Is.EqualTo(1));
            Assert.That(tracker.CurrentIndex, Is.EqualTo(1));
            Assert.That(tracker.Offset, Is.Zero);
        }
        public void IndexTrackerLockThreadEnforceBefore()
        {
            var tracker = new ListCollectionIndexOffsetTracker <int>();

            tracker.SetOriginalIndexAndResetCurrent(1);

            Assert.That(tracker.CurrentIndex, Is.EqualTo(1));
            Assert.That(tracker.OriginalIndex, Is.EqualTo(1));

            tracker.HandleEvent(null, ListCollectionEventArgs <int> .CreateInsertEvent(0, 5));

            Assert.That(tracker.CurrentIndex, Is.EqualTo(2));
            Assert.That(tracker.OriginalIndex, Is.EqualTo(1));

            tracker.ApplyContextLock(t =>
            {
                t.SetOriginalIndexAndResetCurrent(t.CurrentIndex + 1);
            });
            Assert.That(tracker.CurrentIndex, Is.EqualTo(3));
            Assert.That(tracker.OriginalIndex, Is.EqualTo(3));
        }