chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
I am running into a NullReferenceException when trying to validate a method in an abstract class that aborts a background thread. I want to verify that, in addition to other things that happen in the method, the background thread is aborted. This sample code is a simplified version of the real code that focuses on aborting the thread.

    public abstract class Foo
    {
        public static void KillTime()
        {
            try
            {
                for (int i = 0; i < 10000000; i++)
                {
                    Thread.Sleep(1);
                }
            }
            catch
            {
                Thread.Sleep(100);
            }
        }

        public Thread MyThread { get; set; }

        public void StartThread()
        {
            MyThread = new Thread(new ThreadStart(RunStuff));
            MyThread.Name = "Thread RunStuff";
            MyThread.Start();
        }

        public void AbortThread()
        {
            MyThread.Abort();
        }

        public void RunStuff()
        {
            KillTime();
        }
    }


Here is the unit test that is a simplified form of my actual unit test.

        [TestMethod]
        public void AbortThreadTest()
        {
            Foo fakeFoo = Isolate.Fake.Instance<Foo>(Members.CallOriginal);

            //Isolate.WhenCalled(() => fakeFoo.RunStuff()).DoInstead(context => Foo.KillTime());

            fakeFoo.StartThread();
            Thread.Sleep(50);
            fakeFoo.AbortThread();

            Assert.AreEqual<ThreadState>(ThreadState.AbortRequested | ThreadState.WaitSleepJoin, fakeFoo.MyThread.ThreadState,
                "The thread has not been aborted");

            Assert.IsTrue(fakeFoo.MyThread.Join(500), "The thread did not finish aborting in time");
        }


As listed above, the unit test above passes. This indicates that the KillTime method is not a problem. However, if the commented line is uncommented (as in the real unit test), then the test is aborted and the test run reports the following error:

One of the background threads threw exception: System.NullReferenceException: Object reference not set to an instance of an object.
at du.Invoke(Object[] A_0, Object A_1)
at ad.a(DynamicReturnValue A_0, Object A_1, Object[] A_2, Object A_3)
at ad.a(DynamicReturnValue A_0, Object A_1, Object[] A_2, Type A_3, Object A_4)
at ad.a(Object A_0, Object[] A_1, Type A_2, Scope A_3, Int32 A_4, Object A_5, Type A_6)
at bi.a(String A_0, Object[] A_1, Object A_2, Object A_3, String A_4, Type A_5)
at b2.a(String A_0, Object A_1, MethodBase A_2, Object[] A_3, Object A_4, String A_5, bi A_6)
at b2.b(Object A_0, String A_1, String A_2, MethodBase A_3, Object[] A_4, Object A_5)
   at TypeMock.MockManager.a(String A_0, String A_1, Object A_2, Object A_3, Boolean A_4, Object[] A_5)
   at TypeMock.InternalMockManager.getReturn(Object that, String typeName, String methodName, Object methodParameters, Boolean isInjected)
   at TestProject1.Foo.RunStuff() in ...TestProject1UnitTest1.cs:line 115
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Test host process exited unexpectedly.


If I comment the last Assert out that does the Join, then the test passes, but I still get a test run error that only reports the last line in the error above: "Test host process exited unexpectedly."

The actual RunStuff method is private in my abstract class; making it public (like in the code above) did not make a difference.

Also, I tried making the Foo class above a concrete class (unlike my actual class which is abstract - hence, the need to create a fake instance) and still encountered the same problems. However, if I made Foo a concrete class and created a real instance (avoiding Isolator altogether), then the test passed and the test run did not report any errors.

I am looking for any ideas as to what is wrong with the test or suggestions on alternate approaches to testing that the thread gets aborted.

Thanks,
Brian
asked by bhunter (3.4k points)

2 Answers

0 votes
Brian,

According to the stack trace it seems like somewhere between threads the dynamic behavior delegate supplied to DoInstead() lost its calling context, which causes it to throw a null reference exception from its inner invocation. I will need to work a bit further to get a local repro of this issue and debug it to find out the root cause. I will post my findings here then.

Thanks,
Doron
Typemock Support
answered by doron (17.2k points)
0 votes
I was just wondering if any progress had been made on this issue.

I actually just ran into it again on another unit test where I am validating the actions taking when the background thread is aborted. Within the DoInstead, I am aborting the current thread to cause the ThreadAbortException to be thrown. I would have used WillThrow, but ThreadAbortException does not have any public constructors. However, I did find a workaround by aborting the unit test thread, catching the ThreadAbortException, getting a reference to the exception object, calling Thread.ResetAbort to stop the abort, and finally passing the reference to WillThrow.

Brian
answered by bhunter (3.4k points)
...