view Implab.Test/RunnableComponentTests.cs @ 187:dd4a3590f9c6 ref20160224

Reworked cancelation handling, if the cancel handler isn't specified the OperationCanceledException will be handled by the error handler Any unhandled OperationCanceledException will cause the promise cancelation
author cin
date Tue, 19 Apr 2016 17:35:20 +0300
parents 822aab37b107
children 3071220371f8
line wrap: on
line source

using System;
using System.Reflection;
using System.Threading;
using Implab.Parallels;
using Implab.Components;

#if MONO

using NUnit.Framework;
using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
using TestMethodAttribute = NUnit.Framework.TestAttribute;

#else

using Microsoft.VisualStudio.TestTools.UnitTesting;

#endif

namespace Implab.Test {
    [TestClass]
    public class RunnableComponentTests {

        static void ShouldThrow(Action action) {
            try {
                action();
                Assert.Fail();
            } catch(AssertionException) {
                throw;
            } catch {
            }
        }

        class Runnable : RunnableComponent {
            public Runnable(bool initialized) : base(initialized) {
            }
                
            public Action MockInit {
                get;
                set;
            }

            public Func<IPromise> MockStart {
                get;
                set;
            }

            public Func<IPromise> MockStop {
                get;
                set;
            }

            protected override IPromise OnStart() {
                return MockStart != null ? MockStart() : base.OnStart();
            }

            protected override IPromise OnStop() {
                return MockStop != null ? MockStop() : base.OnStart();
            }

            protected override void OnInitialize() {
                if (MockInit != null)
                    MockInit();
            }
        }

        [TestMethod]
        public void NormalFlowTest() {
            var comp = new Runnable(false);

            Assert.AreEqual(ExecutionState.Created, comp.State);

            comp.Init();

            Assert.AreEqual(ExecutionState.Ready, comp.State);

            comp.Start().Join(1000);

            Assert.AreEqual(ExecutionState.Running, comp.State);

            comp.Stop().Join(1000);

            Assert.AreEqual(ExecutionState.Disposed, comp.State);

        }

        [TestMethod]
        public void InitFailTest() {
            var comp = new Runnable(false) {
                MockInit = () => {
                    throw new Exception("BAD");
                }
            };

            ShouldThrow(() => comp.Start());
            ShouldThrow(() => comp.Stop());
            Assert.AreEqual(ExecutionState.Created, comp.State);

            ShouldThrow(comp.Init);

            Assert.AreEqual(ExecutionState.Failed, comp.State);

            ShouldThrow(() => comp.Start());
            ShouldThrow(() => comp.Stop());
            Assert.AreEqual(ExecutionState.Failed, comp.State);

            comp.Dispose();
            Assert.AreEqual(ExecutionState.Disposed, comp.State);
        }

        [TestMethod]
        public void DisposedTest() {

            var comp = new Runnable(false);
            comp.Dispose();

            ShouldThrow(() => comp.Start());
            ShouldThrow(() => comp.Stop());
            ShouldThrow(comp.Init);

            Assert.AreEqual(ExecutionState.Disposed, comp.State);
        }

        [TestMethod]
        public void StartCancelTest() {
            var comp = new Runnable(true) {
                MockStart = () => PromiseHelper.Sleep(100000, 0)
            };

            var p = comp.Start();
            Assert.AreEqual(ExecutionState.Starting, comp.State);
            p.Cancel();
            ShouldThrow(() => p.Join(1000));
            Assert.AreEqual(ExecutionState.Failed, comp.State);
            Assert.IsInstanceOfType(typeof(OperationCanceledException), comp.LastError);

            comp.Dispose();
        }

        [TestMethod]
        public void StartStopTest() {
            var stop = new Signal();
            var comp = new Runnable(true) {
                MockStart = () => PromiseHelper.Sleep(100000, 0),
                MockStop = () => AsyncPool.RunThread(stop.Wait)
            };

            var p1 = comp.Start();
            var p2 = comp.Stop();
            // should enter stopping state

            ShouldThrow(p1.Join);
            Assert.IsTrue(p1.IsCancelled);
            Assert.AreEqual(ExecutionState.Stopping, comp.State);

            stop.Set();
            p2.Join(1000);
            Assert.AreEqual(ExecutionState.Disposed, comp.State);
        }

        [TestMethod]
        public void StartStopFailTest() {
            var comp = new Runnable(true) {
                MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); })
            };

            comp.Start();
            var p = comp.Stop();
            // if Start fails to cancel, should fail to stop
            ShouldThrow(() => p.Join(1000));
            Assert.AreEqual(ExecutionState.Failed, comp.State);
            Assert.IsNotNull(comp.LastError);
            Assert.AreEqual("I'm dead", comp.LastError.Message);
        }

        [TestMethod]
        public void StopCancelTest() {
            var comp = new Runnable(true) {
                MockStop = () => PromiseHelper.Sleep(100000, 0)
            };

            comp.Start();
            var p = comp.Stop();
            Assert.AreEqual(ExecutionState.Stopping, comp.State);
            p.Cancel();
            ShouldThrow(() => p.Join(1000));
            Assert.AreEqual(ExecutionState.Failed, comp.State);
            Assert.IsInstanceOfType(typeof(OperationCanceledException), comp.LastError);

            comp.Dispose();
        }

    }
}