/src/NUnit/core/TestThread.cs
C# | 194 lines | 127 code | 28 blank | 39 comment | 7 complexity | 3eebceeded363f40564833cc9cb4b466 MD5 | raw file
1// **************************************************************** 2// Copyright 2008, Charlie Poole 3// This is free software licensed under the NUnit license. You may 4// obtain a copy of the license at http://nunit.org. 5// **************************************************************** 6using System; 7using System.Runtime.Remoting.Messaging; 8using System.Threading; 9 10namespace NUnit.Core 11{ 12 /// <summary> 13 /// Represents a thread of test execution and runs a test 14 /// on a thread, implementing timeout and setting the 15 /// apartment state appropriately. 16 /// </summary> 17 public abstract class TestThread 18 { 19 static Logger log = InternalTrace.GetLogger(typeof(TestThread)); 20 21 private Test test; 22 23 #region Protected Fields 24 /// <summary> 25 /// The Thread object used to run tests 26 /// </summary> 27 protected Thread thread; 28 29 /// <summary> 30 /// The result of running the test, which must be kept 31 /// separate from the returned TestResult while the thread 32 /// is running to avoid race conditions. 33 /// </summary> 34 protected TestResult threadResult; 35 36 protected EventListener listener; 37 38 protected ITestFilter filter; 39 40 protected TestMethod.ContextDictionary contextDictionary; 41 42 /// <summary> 43 /// Unexpected exception thrown by test thread 44 /// </summary> 45 protected Exception thrownException; 46 #endregion 47 48 #region Constructor 49 protected TestThread(Test test) 50 { 51 this.test = test; 52 53 this.thread = new Thread(new ThreadStart(RunTestProc)); 54 thread.CurrentCulture = Thread.CurrentThread.CurrentCulture; 55 thread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture; 56 57 // Setting to Unknown causes an error under the Mono 1.0 profile 58 if ( test.ApartmentState != ApartmentState.Unknown ) 59 this.ApartmentState = test.ApartmentState; 60 } 61 #endregion 62 63 #region Properties 64 public ApartmentState ApartmentState 65 { 66#if NET_2_0 67 get { return thread.GetApartmentState(); } 68 set { thread.SetApartmentState(value); } 69#else 70 get { return thread.ApartmentState; } 71 set { thread.ApartmentState = value; } 72#endif 73 } 74 #endregion 75 76 /// <summary> 77 /// Run the test, honoring any timeout value provided. If the 78 /// timeout is exceeded, set the testresult as a failure. As 79 /// currently implemented, the thread proc calls test.doRun, 80 /// which handles all exceptions itself. However, for safety, 81 /// any exception thrown is rethrown upwards. 82 /// 83 /// TODO: It would be cleaner to call test.Run, since that's 84 /// part of the pubic interface, but it would require some 85 /// restructuring of the Test hierarchy. 86 /// </summary> 87 public TestResult Run(EventListener listener, ITestFilter filter) 88 { 89 TestResult testResult = new TestResult(test); 90 91 this.thrownException = null; 92 this.listener = listener; 93 this.filter = filter; 94 this.contextDictionary = (TestMethod.ContextDictionary)CallContext.GetData("NUnit.Framework.TestContext"); 95 96 log.Debug("Starting test in separate thread"); 97 thread.Start(); 98 thread.Join(this.Timeout); 99 100 // Timeout? 101 if (thread.IsAlive) 102 { 103 log.Debug("Test timed out - aborting thread"); 104 thread.Abort(); 105 //thread.Join(); 106 testResult.Failure(string.Format("Test exceeded Timeout value of {0}ms", Timeout), null); 107 } 108 else if (thrownException != null) 109 { 110 log.Debug("Test threw " + thrownException.GetType().Name); 111 throw thrownException; 112 } 113 else 114 { 115 log.Debug("Test completed normally"); 116 testResult = threadResult; 117 } 118 119 return testResult; 120 } 121 122 /// <summary> 123 /// This is the engine of this class; the actual call to test.doRun! 124 /// Note that any thrown exception is saved for later use! 125 /// </summary> 126 private void RunTestProc() 127 { 128 CallContext.SetData("NUnit.Framework.TestContext", contextDictionary); 129 130 try 131 { 132 RunTest(); 133 } 134 catch (Exception e) 135 { 136 thrownException = e; 137 } 138 finally 139 { 140 CallContext.FreeNamedDataSlot("NUnit.Framework.TestContext"); 141 } 142 } 143 144 protected abstract int Timeout { get; } 145 protected abstract void RunTest(); 146 } 147 148 public class TestMethodThread : TestThread 149 { 150 private TestMethod testMethod; 151 152 public TestMethodThread(TestMethod testMethod) 153 : base(testMethod) 154 { 155 this.testMethod = testMethod; 156 } 157 158 protected override int Timeout 159 { 160 get 161 { 162 return testMethod.Timeout == 0 //|| System.Diagnostics.Debugger.IsAttached 163 ? System.Threading.Timeout.Infinite 164 : testMethod.Timeout; 165 } 166 } 167 168 protected override void RunTest() 169 { 170 this.threadResult = testMethod.RunTest(); 171 } 172 } 173 174 public class TestSuiteThread : TestThread 175 { 176 private TestSuite suite; 177 178 public TestSuiteThread(TestSuite suite) 179 : base(suite) 180 { 181 this.suite = suite; 182 } 183 184 protected override int Timeout 185 { 186 get { return System.Threading.Timeout.Infinite; } 187 } 188 189 protected override void RunTest() 190 { 191 this.threadResult = suite.RunSuite(listener, filter); 192 } 193 } 194}