Click here to Skip to main content
15,895,462 members
Articles / Programming Languages / C#

Long Running Work Flow Activities

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
11 Dec 2008CPOL18 min read 45.3K   385   35  
A generic way to write long running work flow activities
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CommonWFLibrary;
using System.Workflow.Runtime;
using System.Threading;
using System.Workflow.ComponentModel;
using System.IO;
using System.Diagnostics;

namespace CommonWFLibraryTest
{
    /// <summary>
    /// Test the long running WF activity
    /// </summary>
    [TestClass]
    public class t_LongRunningActivityBase
    {
        public t_LongRunningActivityBase()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        class LRSimple : LongRunningActivityBase
        {
            public static int _result = 0;
            [LongRunningMethod]
            public static void Run()
            {
                _result = 10;
            }
        }

        [TestMethod]
        public void TestSimpleWF()
        {
            LRSimple._result = 0;
            RunWFByType(typeof(LRSimple));
            Assert.IsTrue(LRSimple._result == 10, "The static item for the activity method was not set -- was activity actually run!?");
        }

        class LRSimpleInherit : LRSimple
        {
        }

        [TestMethod]
        public void TestSimpleWFInherrited()
        {
            /// Make sure we can pick up trivial inherritance activities
            LRSimpleInherit._result = 0;
            RunWFByType(typeof(LRSimpleInherit));
            Assert.IsTrue(LRSimpleInherit._result == 10, "The static item for the activity wasn't right!");
        }

        /// <summary>
        /// Run a test, throw exceptions back to user.
        /// </summary>
        /// <param name="wfActivity"></param>
        /// <param name="dict"></param>
        private static void RunWFByType(Type wfActivity, Dictionary<string, object> dict)
        {
            using (WorkflowRuntime runtime = new WorkflowRuntime())
            using (AutoResetEvent reset = new AutoResetEvent(false))
            {
                Exception exp = null;
                runtime.WorkflowCompleted += delegate { reset.Set(); };
                runtime.WorkflowTerminated += (o, args) => { exp = args.Exception; reset.Set(); };

                LongRunningActivityBase.RegisterService(runtime);
                runtime.StartRuntime();

                WorkflowInstance instance;
                instance = runtime.CreateWorkflow(wfActivity, dict);
                instance.Start();
                reset.WaitOne();

                if (exp != null)
                {
                    throw exp;
                }
            }
        }

        /// <summary>
        /// Run a WF, with persistance. Once the WF goes into the idle state kill the runtime. Restart it, and then
        /// see if the WF is restored or used correctly.
        /// </summary>
        /// <param name="wfActivity"></param>
        /// <param name="dict"></param>
        /// <param name="timeout"></param>
        /// <param name="cache"></param>
        /// <returns></returns>
        private static bool RunWFByTypeWithPersistance(Type wfActivity, Dictionary<string, object> dict, int timeout, DirectoryInfo cache)
        {
            return RunWFByTypeWithPersistance(wfActivity, dict, timeout, cache, 1);
        }

        /// <summary>
        /// Run a WF, with persistance. Once the WF goes into the idle state kill the runtime. We do this "n" times, and
        /// then see if the WF is restored and used correctly.
        /// </summary>
        /// <param name="wfActivity"></param>
        /// <param name="dict"></param>
        /// <param name="timeout"></param>
        /// <param name="cache"></param>
        /// <param name="numberCrashToIdles"></param>
        /// <returns></returns>
        private static bool RunWFByTypeWithPersistance(Type wfActivity, Dictionary<string, object> dict, int timeout, DirectoryInfo cache, int numberCrashToIdles)
        {
            ///
            /// Crash the runtime n times.
            /// 

            bool start_wf = true;
            bool has_entered_idle = false;
            while (numberCrashToIdles >= 0)
            {
                Trace.WriteLine("Starting WF runtime iteration");
                numberCrashToIdles = numberCrashToIdles - 1;
                bool waitForComplete = numberCrashToIdles < 0;

                ///
                /// Run until the WF enters the idle state. If it crashes, then, of course, we are outta here.
                /// 

                using (AutoResetEvent reset = new AutoResetEvent(false))
                using (WorkflowRuntime runtime = new WorkflowRuntime())
                {
                    try
                    {
                        Exception e = null;
                        bool completed = false;
                        if (!waitForComplete)
                        {
                            runtime.WorkflowIdled += (o, args) => { reset.Set(); has_entered_idle = true; };
                        }
                        runtime.WorkflowCompleted += (o, args) => { completed = true; reset.Set(); };
                        runtime.WorkflowTerminated += (o, args) => { e = args.Exception; reset.Set(); };

                        runtime.AddService(new FilePersistenceService(true, cache));
                        LongRunningActivityBinaryPersister lrp = new LongRunningActivityBinaryPersister(new FileInfo(cache.FullName + "\\longrunning"));
                        LongRunningActivityBase.RegisterService(runtime, obj => lrp.Save(obj), () => lrp.Restore());
                        runtime.StartRuntime();

                        ///
                        /// If this is the first time through, then we should start the WF. Otherwise, it
                        /// should have been picked up by the persitancy service from the last run.
                        /// 

                        if (start_wf)
                        {
                            WorkflowInstance instance;
                            instance = runtime.CreateWorkflow(wfActivity, dict);
                            instance.Start();
                            start_wf = false;
                        }

                        ///
                        /// Great. Now wait for it to finish. We expect the WF to enter
                        /// an idled state within 1 second. On the last one, waiting for something
                        /// real to happen, we expect that to happen in the timeout parameter passed
                        /// to us.
                        /// 
                        /// We shut down the runtime here so that we can step through the other statements in the
                        /// debugger!
                        /// 

                        int waittime = waitForComplete ? timeout : 1000;
                        bool result = reset.WaitOne(waittime);
                        runtime.StopRuntime();
                        runtime.Dispose();

                        ///
                        /// First, if the WF ended due to an exception, we are outta here no matter what
                        /// 

                        if (e != null)
                        {
                            throw e;
                        }

                        ///
                        /// Next, if we are waiting for a complete, then we just return the result
                        /// 

                        if (waitForComplete)
                        {
                            return result;
                        }

                        ///
                        /// Ok -- we were waiting for things to go into the idle state. Bomb if they didn't,
                        /// otherwise we go around again!
                        /// 

                        if (completed)
                        {
                            throw new Exception("The WF unexpectedly completed before going idle!");
                        }
                        if (!result && !has_entered_idle)
                        {
                            throw new Exception("The workflow was never idled");
                        }
                    }
                    finally
                    {
                        if (runtime.IsStarted)
                        {
                            runtime.StopRuntime();
                        }
                    }
                }
            }
            return false;
        }

        private static void RunWFByType(Type wfActivity)
        {
            RunWFByType(wfActivity, null);
        }

        /// <summary>
        /// WF that will wait for some period of time before actually causing the thing
        /// to fail.
        /// </summary>
        class LWFTime : LongRunningActivityBase
        {
            public int Ticks { get; set; }

            public LWFTime()
            {
            }
            [Serializable]
            public class cArgs
            {
                public int _args;
                public cArgs(int a)
                {
                    _args = a;
                }
            }
            [LongRunningMethod]
            public static void Run(cArgs a)
            {
                Thread.Sleep(a._args);
            }
            [LongRunningGatherArguments]
            public cArgs Args()
            {
                return new cArgs(Ticks);
            }
        }

        [TestMethod]
        public void TestPrematureShutdown()
        {
            ///
            /// If the workflow engine shuts down, make sure we don't cause a crash by trying to reference
            /// something that is in there...
            /// 

            using (WorkflowRuntime runtime = new WorkflowRuntime())
            using (AutoResetEvent reset = new AutoResetEvent(false))
            {
                runtime.WorkflowIdled += delegate { reset.Set(); };

                LongRunningActivityBase.RegisterService(runtime);
                runtime.StartRuntime();

                WorkflowInstance instance;
                instance = runtime.CreateWorkflow(typeof(LWFTime), new Dictionary<string,object> {{"Ticks", 3000}});
                instance.Start();
                reset.WaitOne();

                runtime.StopRuntime();
            }

            ///
            /// Now wait for the ticks guy to time out. While this guy may finish, it will cause a crash
            /// in the vshost dude.
            /// 

            Thread.Sleep(4000);
        }

        [TestMethod]
        public void TestLostService()
        {
            /// Test the fact that the service was not added in

            using (WorkflowRuntime runtime = new WorkflowRuntime())
            using (AutoResetEvent reset = new AutoResetEvent(false))
            {
                Exception exp = null;
                runtime.WorkflowCompleted += delegate { reset.Set(); };
                runtime.WorkflowTerminated += (o, args) => { exp = args.Exception; reset.Set(); };
                runtime.StartRuntime();

                WorkflowInstance instance;
                instance = runtime.CreateWorkflow(typeof(LRSimple));
                instance.Start();
                reset.WaitOne();

                Assert.IsTrue(exp != null, "No exception was thrown by the wf!");
                Assert.IsTrue(exp.Message.Contains("Service"), "Exception thrown is wrong exception for missing WF serice!");
            }
        }

        [TestMethod]
        public void TestThrow()
        {
            /// Make sure we throw an exception because we don't have a "run" method.
            try
            {
                RunWFByType(typeof(LongRunningActivityBase));
            }
            catch (Exception e)
            {
                Assert.IsTrue(e.Message.Contains("LongRunningMethod"), "Wrong exception came back for missing Run method!");
                return;
            }
            Assert.Fail("An exception should have been thrown!");
        }

        [TestMethod]
        public void Test05secWF()
        {
            TimeSpan sp = RunTimeTest(500);
            Assert.IsTrue(sp.TotalMilliseconds > 500, "Run took less than half a second - pause didn't work");
        }

        /// <summary>
        /// Run a timer test...
        /// </summary>
        /// <returns></returns>
        private static TimeSpan RunTimeTest(int ticks)
        {
            /// Wait for 0.5 second and make sure that it actually takes that long to complete.
            DateTime now = DateTime.Now;
            RunWFByType(typeof(LWFTime), new Dictionary<string,object> {{"Ticks", ticks}});
            DateTime then = DateTime.Now;
            TimeSpan sp = then - now;
            return sp;
        }

        [TestMethod]
        public void TestFastWF()
        {
            TimeSpan sp = RunTimeTest(0);
            Assert.IsTrue(sp.TotalMilliseconds < 300, "Run took more than half a second - pause didn't work");
        }

        /// <summary>
        /// Activity that will set some information on the way back down.
        /// </summary>
        public class LWASetResult : LongRunningActivityBase
        {
            public class cArgs
            {
                public int _arg;
            }

            public int TheArg { get; set; }

            [LongRunningMethod]
            public static cArgs Run(cArgs arg)
            {
                cArgs newarg = new cArgs();
                newarg._arg = arg._arg * 2;
                return newarg;
            }

            [LongRunningGatherArguments]
            public cArgs GatherArgs()
            {
                cArgs result = new cArgs();
                result._arg = TheArg;
                return result;
            }

            public static int _result;

            [LongRunningDistributeArguments]
            public void DoneWithResult(cArgs arg)
            {
                _result = arg._arg;
            }
        }

        [TestMethod]
        public void TestArgDistribution()
        {
            LWASetResult._result = 0;
            RunWFByType(typeof(LWASetResult), new Dictionary<string, object> { { "TheArg", 10 } });
            Assert.IsTrue(LWASetResult._result == 20, "The callback didn't set the result to the right value!");
        }

        [TestMethod]
        public void TestPersistance()
        {
            try
            {
                Assert.IsTrue(RunWFByTypeWithPersistance(typeof(LWFTime), new Dictionary<string, object> { { "Ticks", 3000 } }, 5000, new DirectoryInfo(".\\testPers")), "The task did not complete");
                Assert.Fail("The persistance should have thrown a crash - because it didn't restart, but that didn't happen");
            }
            catch (LongRunningException e)
            {
                Assert.IsTrue(e.Message.Contains("Activity was waiting for response"), "Incorrect crash came back from aborted WF.");
            }
        }

        [TestMethod]
        public void TestAbortedWF()
        {
            DirectoryInfo cache = new DirectoryInfo (".\\TestAbortedWF");

            ///
            /// Get the runtime going, go into idle state, and then abort the workflow. There should
            /// be no files sitting in the cache directory at that point.
            /// 

            using (WorkflowRuntime runtime = new WorkflowRuntime())
            using (AutoResetEvent reset = new AutoResetEvent(false))
            {
                Exception exp = null;
                runtime.WorkflowCompleted += delegate { reset.Set(); };
                runtime.WorkflowTerminated += (o, args) => { exp = args.Exception; reset.Set(); };
                runtime.WorkflowIdled += (o, args) => reset.Set();

                runtime.AddService(new FilePersistenceService(true, cache));
                LongRunningActivityBinaryPersister lrp = new LongRunningActivityBinaryPersister(new FileInfo(cache.FullName + "\\longrunning"));
                LongRunningActivityBase.RegisterService(runtime, obj => lrp.Save(obj), () => lrp.Restore());
                runtime.StartRuntime();

                WorkflowInstance instance;
                instance = runtime.CreateWorkflow(typeof(LWFTime), new Dictionary<string, object> { { "Ticks", 5000 } });
                instance.Start();
                reset.WaitOne();

                if (exp != null)
                {
                    throw exp;
                }

                ///
                /// Ok -- now check out the cache. Make sure there are no files there!
                /// 

                instance.Terminate("Forcing terminate for testing");

                Assert.IsTrue(cache.GetFiles().Length == 0, "There were files in the cache after aborted WF!");
            }
        }

        [TestMethod]
        public void TestAutoRestart()
        {
            Assert.IsTrue(RunWFByTypeWithPersistance(typeof(LWFTime), new Dictionary<string, object> { { "Ticks", 3000 }, { "TimesToRetry", 1 } }, 5000, new DirectoryInfo(".\\testAutoRestart")), "The task did not complete");
        }

        /// <summary>
        /// A simple activity that will throw...
        /// </summary>
        class LRCrashNTimesActivity : LongRunningActivityBase
        {
            public static int _numberOfTimes = 0;

            public int TimesToCrash { get; set; }

            [Serializable]
            public class cArgs
            {
                public int _times;
            }

            [LongRunningMethod]
            public static void Run(cArgs args)
            {
                _numberOfTimes = _numberOfTimes + 1;

                if (_numberOfTimes < args._times)
                {
                    Thread.Sleep(2000);
                }
                else
                {
                    throw new Exception("Testing the throw 12345669");
                }
            }

            [LongRunningGatherArguments]
            public cArgs Gather()
            {
                cArgs c = new cArgs();
                c._times = TimesToCrash;
                return c;
            }
        }

        [TestMethod]
        public void TestNumberCalls()
        {
            /// Make sure that the WF above is called the number of times we expect it to be called.
            /// This is more of a test of the test harness than it is of the long running activity code. :-)

            LRCrashNTimesActivity._numberOfTimes = 0;
            RunWFByType(typeof(LRCrashNTimesActivity),
                new Dictionary<string, object> { { "TimesToCrash", 10 } });
            Assert.IsTrue(LRCrashNTimesActivity._numberOfTimes == 1, "Was expecting to have run only once!");

            LRCrashNTimesActivity._numberOfTimes = 0;
            bool result = RunWFByTypeWithPersistance(typeof(LRCrashNTimesActivity),
                new Dictionary<string, object> { { "TimesToCrash", 10 } },
                5000,
                new DirectoryInfo(".\\TestNumberCallsCache"),
                0);
            Assert.IsTrue(result, "Expected complex run with 0 retries to come back just fine!");
            Assert.IsTrue(LRCrashNTimesActivity._numberOfTimes == 1, "Expected the activity to be called only one time!");

            LRCrashNTimesActivity._numberOfTimes = 0;
            bool result2 = RunWFByTypeWithPersistance(typeof(LRCrashNTimesActivity),
                new Dictionary<string, object> { { "TimesToCrash", 10 }, {"TimesToRetry", 10} },
                5000,
                new DirectoryInfo(".\\TestNumberCallsCache"),
                1);
            Assert.IsTrue(result2, "Expected complex run with 1 retries to come back just fine!");
            Assert.IsTrue(LRCrashNTimesActivity._numberOfTimes == 2, "Expected the activity to be called exactly 2 times.");
        }

        [TestMethod]
        public void TestForPersFileToBeGone()
        {
            DirectoryInfo di = new DirectoryInfo(".\\TestNumberCallsCache");
            LRCrashNTimesActivity._numberOfTimes = 0;
            bool result = RunWFByTypeWithPersistance(typeof(LRCrashNTimesActivity),
                new Dictionary<string, object> { { "TimesToCrash", 10 } },
                5000,
                di,
                0);
            Assert.IsTrue(result, "Expected complex run with 0 retries to come back just fine!");
            Assert.IsTrue(LRCrashNTimesActivity._numberOfTimes == 1, "Expected the activity to be called only one time!");

            FileInfo fcache = new FileInfo(di.FullName + "\\longrunning");
            Assert.IsFalse(fcache.Exists, "The cache file should not be around after a single successful run!");
        }

        [TestMethod]
        public void TestAutoRerunExactNumberOfTimes()
        {
            ///
            /// First, make sure that if we retry several times it gets a chance to complete.
            /// 

            LRCrashNTimesActivity._numberOfTimes = 0;
            bool result2 = RunWFByTypeWithPersistance(typeof(LRCrashNTimesActivity),
                new Dictionary<string, object> { { "TimesToCrash", 10 }, { "TimesToRetry", 3 } }, 5000,
                new DirectoryInfo(".\\TestAutoRerunExactNumberOfTimes"), 3);
            Assert.IsTrue(result2, "Was expecting things to complete normally");
            Assert.IsTrue(LRCrashNTimesActivity._numberOfTimes == 4, "Was expecting to have called the activity exactly 4 times!");
        }

        [TestMethod]
        public void TestExcessiveAutoRestart()
        {
            ///
            /// This looks to make sure we are safe against the thing that the task we are doing
            /// causes the whole host to crash. So we specify the max number of times to re-try a
            /// particular WF item.
            /// 

            try
            {
                LRCrashNTimesActivity._numberOfTimes = 0;
                bool result = RunWFByTypeWithPersistance(typeof(LRCrashNTimesActivity),
                    new Dictionary<string, object> { { "TimesToCrash", 10 }, { "TimesToRetry", 4 } }, 5000,
                    new DirectoryInfo(".\\TestExcessiveAutoRestart"),
                    10);
                Assert.Fail("We should have thrown an exception and terminated after the 2nd iteration and note made it to the third!");
            }
            catch (Exception e)
            {
                Assert.IsTrue(e.Message.Contains("waiting for response"), "Exception that came back was incorrect");
                Assert.IsTrue(LRCrashNTimesActivity._numberOfTimes == 5, "Was expecting to have retried only 5 times before a crash!");
            }
        }

        /// <summary>
        /// A simple activity that will throw...
        /// </summary>
        class LRCrashActivity : LongRunningActivityBase
        {
            [LongRunningMethod]
            public static void Run()
            {
                throw new Exception("Testing the throw 123456789");
            }
        }

        [TestMethod]
        public void TestWFThrows()
        {
            try
            {
                RunWFByType(typeof(LRCrashActivity));
                Assert.Fail("WF completed without an error - it should have thrown!");
            }
            catch (Exception e)
            {
                Assert.IsNotNull(e.InnerException, "Inner expcetion is not null!");
                Assert.IsTrue(e.InnerException.Message.Contains("123456789"), "Wrong exception came back from crashed WF");
            }
        }

        class LRAsyncActivity : LongRunningActivityBase
        {
            /// <summary>
            /// How many ticks to wait for!
            /// </summary>
            public int Ticks { get; set; }

            public class cArgs
            {
                public int _ticks;
            }

            [LongRunningGatherArguments]
            public cArgs Gather()
            {
                var result = new cArgs();
                result._ticks = Ticks;
                return result;
            }

            [LongRunningMethodStarter]
            public static void StartTimer(LongRunningContext c, cArgs args)
            {
                Timer t = new Timer(new TimerCallback(timerdone), c, args._ticks, Timeout.Infinite);
            }

            public static void timerdone(object state)
            {
                LongRunningContext c = state as LongRunningContext;
                c.Complete();
            }
        }

        [TestMethod]
        public void TestAsyncCallbackPattern()
        {
            /// Use a guy that doesn't run synchronosly - rather runs async.
            RunWFByType(typeof(LRAsyncActivity), new Dictionary<string, object> { { "Ticks", 1000 } });
        }

        class LRAsyncNoArgActivity : LongRunningActivityBase
        {
            [LongRunningMethodStarter]
            public static void StartTimer(LongRunningContext c)
            {
                Timer t = new Timer(new TimerCallback(timerdone), c, 1000, Timeout.Infinite);
            }

            public static void timerdone(object state)
            {
                LongRunningContext c = state as LongRunningContext;
                c.Complete();
            }
        }

        [TestMethod]
        public void TestAsyncCallbackPatternNoArgs()
        {
            /// WF with a run method that takes no parameters
            RunWFByType(typeof(LRAsyncNoArgActivity));
        }

        class LRAsyncActivityWithArgs : LongRunningActivityBase
        {
            /// <summary>
            /// How many ticks to wait for!
            /// </summary>
            public int Ticks { get; set; }

            public class cArgs
            {
                public int _ticks;
            }

            [LongRunningGatherArguments]
            public cArgs Gather()
            {
                var result = new cArgs();
                result._ticks = Ticks;
                return result;
            }

            [LongRunningMethodStarter]
            public static void StartTimer(LongRunningContext c, cArgs args)
            {
                Timer t = new Timer(new TimerCallback(timerdone), c, args._ticks, Timeout.Infinite);
            }

            public static void timerdone(object state)
            {
                LongRunningContext c = state as LongRunningContext;
                Result r = new Result();
                r._result = 15;
                c.Complete(r);
            }

            public class Result
            {
                public int _result;
            }

            public static int _result = 0;

            [LongRunningDistributeArguments]
            public void Save(Result r)
            {
                _result = r._result;
            }

        }

        [TestMethod]
        public void TestAsyncWFWithResult()
        {
            LRAsyncActivityWithArgs._result = 0;
            RunWFByType(typeof(LRAsyncActivityWithArgs), new Dictionary<string, object> { { "Ticks", 500 } });
            Assert.IsTrue(LRAsyncActivityWithArgs._result == 15, "The saved result was not correct");
        }

        class LRAsyncActivityWithContext : LongRunningActivityBase
        {
            /// <summary>
            /// How many ticks to wait for!
            /// </summary>
            public int Ticks { get; set; }

            [Serializable]
            public class cArgs
            {
                public int _ticks;
            }

            [Serializable]
            public class MyContext
            {
                public int _mycounter = 0;
            }

            [LongRunningGatherArguments]
            public cArgs Gather()
            {
                var result = new cArgs();
                result._ticks = Ticks;
                return result;
            }

            [LongRunningMethodStarter]
            public static MyContext StartTimer(LongRunningContext c, cArgs args, MyContext me)
            {
                MyContext newme = new MyContext();
                newme._mycounter = 1;
                if (me != null)
                {
                    newme._mycounter = newme._mycounter + me._mycounter;
                }
                Trace.WriteLine("Starting Timer -- counter is " + newme._mycounter.ToString());

                Timer t = new Timer(new TimerCallback(timerdone), new object[] { c, newme }, args._ticks, Timeout.Infinite);

                return newme;
            }

            public static void timerdone(object state)
            {
                object[] arr = state as object[];
                LongRunningContext c = arr[0] as LongRunningContext;
                MyContext me = arr[1] as MyContext;

                Result r = new Result();
                r._result = 15;
                r._counter = me._mycounter;
                c.Complete(r);
            }

            [Serializable]
            public class Result
            {
                public int _result;
                public int _counter;
            }

            public static int _result = 0;
            public static int _counter = 0;

            [LongRunningDistributeArguments]
            public void Save(Result r)
            {
                _result = r._result;
                _counter = r._counter;
            }
        }

        [TestMethod]
        public void TestAsyncWithContext()
        {
            LRAsyncActivityWithContext._counter = 0;
            LRAsyncActivityWithContext._result = 0;
            RunWFByType(typeof(LRAsyncActivityWithContext));

            Assert.IsTrue(LRAsyncActivityWithContext._result == 15, "The result is not correct");
            Assert.IsTrue(LRAsyncActivityWithContext._counter == 1, "The number of times it was called is not four!");
        }

        [TestMethod]
        public void TestAsyncWithContextAndCrash()
        {
            /// Make sure the updated context is correctly written out to the cache.
            /// It doesn't appear to be in one of the long running program tests.
            LRAsyncActivityWithContext._counter = 0;
            LRAsyncActivityWithContext._result = 0;
            RunWFByTypeWithPersistance(typeof(LRAsyncActivityWithContext), new Dictionary<string, object> { { "Ticks", 8000 }, {"TimesToRetry", 5} }, 10000,
                new DirectoryInfo(".\\TestAsyncWithContextAndCrash"),
                4);

            Assert.IsTrue(LRAsyncActivityWithContext._result == 15, "The result is not correct");
            Assert.IsTrue(LRAsyncActivityWithContext._counter == 5, "The number of times it was called is not one!");
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Other University of Washington
United States United States
I'm a professor of physics at the University of Washington - my field of research is particle physics. I went into this because of the intersection of physics, hardware, and computers. I've written large experiment data aquisition systems (I've done a lot of multi-thread programming). My hobby is writing tools and other things that tend to be off-shoots of work-related projects.

Comments and Discussions