PageRenderTime 39ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/NUnit/core/TestThread.cs

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