Monday, May 2, 2016

Event based throttler test


    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using CommonUtilities.Schedulers;

    [TestClass]
    public sealed class TimeboundThrottlerTest
    {
        [TestMethod]
        public void Test_TimeboundThrottler_ctor_Success()
        {
            TimeboundThrottler validThrottler = new TimeboundThrottler(10);
            Assert.IsNotNull(validThrottler);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Test_TimeboundThrottler_ctor_Failure()
        {
            TimeboundThrottler invalidThrottler = new TimeboundThrottler(0);
        }

        [TestMethod]
        public void Test_TimeboundThrottler_Enqueue()
        {
            TimeboundThrottler throttler = new TimeboundThrottler(3);
            int runningTasks = 0;
            int completedTasks = 0;

            List<Task> tasks = new List<Task>();
            List<CancellationTokenSource> cancellationTokens = new List<CancellationTokenSource>();

            for (int i = 0; i < 15; i++)
            {
                var cts = new CancellationTokenSource();
                ChangeNotifier notifier = new ChangeNotifier();
                notifier.StartStateChanged += (s, e) => { lock (this) { runningTasks++; } };
                notifier.CompletedStateChanged += (s, e) => { lock (this) { completedTasks++; } };
                cancellationTokens.Add(cts);
                tasks.Add(throttler.Enqueue(() => TestFuncWithCancellationAsync(cts, notifier)));
            }

            // Initially verify that only 3 tasks are picked and rest of the tasks are waiting
            Assert.AreEqual(3, runningTasks);
            Assert.AreEqual(0, completedTasks);

            Thread.Sleep(1100);
            // Initially verify that 3 new tasks are picked and rest of the tasks are waiting
            Assert.AreEqual(6, runningTasks);
            Assert.AreEqual(0, completedTasks);

            // Complete one task, still verify that only 3 tasks are picked in next 1sec
            cancellationTokens[0].Cancel();
            Thread.Sleep(1100);
            Assert.AreEqual(9, runningTasks);
            Assert.AreEqual(1, completedTasks);
        }

        [TestMethod]
        public void Test_TimeboundThrottler_Enqueue_TaskwithResult()
        {
            TimeboundThrottler throttler = new TimeboundThrottler(3);
            int runningTasks = 0;
            int completedTasks = 0;

            List<Task<CancellationToken>> tasks = new List<Task<CancellationToken>>();
            List<CancellationTokenSource> cancellationTokens = new List<CancellationTokenSource>();

            for (int i = 0; i < 15; i++)
            {
                var cts = new CancellationTokenSource();
                ChangeNotifier notifier = new ChangeNotifier();
                notifier.StartStateChanged += (s, e) => { lock (this) { runningTasks++; } };
                notifier.CompletedStateChanged += (s, e) => { lock (this) { completedTasks++; } };
                cancellationTokens.Add(cts);
                tasks.Add(throttler.Enqueue(() => TestFuncWithCancellation2Async(cts, notifier)));
            }

            // Initially verify that only 3 tasks are picked and rest of the tasks are waiting
            Assert.AreEqual(3, runningTasks);
            Assert.AreEqual(0, completedTasks);

            Thread.Sleep(1100);
            // Initially verify that 3 new tasks are picked and rest of the tasks are waiting
            Assert.AreEqual(6, runningTasks);
            Assert.AreEqual(0, completedTasks);

            // Complete one task, still verify that only 3 tasks are picked in next 1sec
            cancellationTokens[0].Cancel();
            Thread.Sleep(1100);
            Assert.AreEqual(9, runningTasks);
            Assert.AreEqual(1, completedTasks);
            Assert.AreEqual(tasks[0].Result, cancellationTokens[0].Token);
        }


        private async Task TestFuncWithCancellationAsync(CancellationTokenSource cts, ChangeNotifier notifier)
        {
            notifier.IsStarted = true;
            while (true)
            {
                if (cts.Token.IsCancellationRequested)
                {
                    break;
                }
                await Task.Delay(1000);
            }
            notifier.IsCompleted = true;
        }

        private async Task<CancellationToken> TestFuncWithCancellation2Async(CancellationTokenSource cts, ChangeNotifier notifier)
        {
            notifier.IsStarted = true;
            while (true)
            {
                if (cts.Token.IsCancellationRequested)
                {
                    break;
                }
                await Task.Delay(1000);
            }
            notifier.IsCompleted = true;
            return cts.Token;
        }

        public class ChangeNotifier
        {
            // Local data
            private bool isStarted = false;
            private bool isCompleted = false;

            // Ctor to assign data
            public ChangeNotifier() { this.isStarted = false; this.isCompleted = false; }
           
            // The event that can be subscribed to
            public event EventHandler StartStateChanged;
            public event EventHandler CompletedStateChanged;
           
            public bool IsStarted
            {
                get { return this.isStarted; }
                set
                {
                    // If the value has changed...
                    if (this.isStarted != value)
                    {
                        // Assign the new value to private storage
                        this.isStarted = value;

                        // And raise the event
                        if (this.StartStateChanged != null)
                            this.StartStateChanged(this, EventArgs.Empty);
                    }
                }
            }
           
            public bool IsCompleted
            {
                get { return this.isCompleted; }
                set
                {
                    // If the value has changed...
                    if (this.isCompleted != value)
                    {
                        // Assign the new value to private storage
                        this.isCompleted = value;

                        // And raise the event
                        if (this.CompletedStateChanged != null)
                            this.CompletedStateChanged(this, EventArgs.Empty);
                    }
                }
            }
        }
    }

No comments:

Post a Comment