/// <summary> /// Runs a single test. /// </summary> /// <param name="testMethod"> The test Method. </param> /// <param name="testContextProperties"> The test context properties. </param> /// <returns> The <see cref="UnitTestResult"/>. </returns> internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary <string, object> testContextProperties) { if (testMethod == null) { throw new ArgumentNullException(nameof(testMethod)); } try { using (var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "context")) { var properties = new Dictionary <string, object>(testContextProperties); var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties); testContext.SetOutcome(TestTools.UnitTesting.UnitTestOutcome.InProgress); // Get the testMethod var testMethodInfo = this.typeCache.GetTestMethodInfo( testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces); if (this.classCleanupManager == null && testMethodInfo != null && testMethodInfo.Parent.HasExecutableCleanupMethod) { PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.OlderTFMVersionFoundClassCleanup); } if (!this.IsTestMethodRunnable(testMethod, testMethodInfo, out var notRunnableResult)) { bool shouldRunClassCleanup = false; this.classCleanupManager?.MarkTestComplete(testMethodInfo, testMethod, out shouldRunClassCleanup); if (shouldRunClassCleanup) { testMethodInfo.Parent.RunClassCleanup(ClassCleanupBehavior.EndOfClass); } return(notRunnableResult); } var result = new TestMethodRunner(testMethodInfo, testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces, this.reflectHelper).Execute(); this.RunClassCleanupIfEndOfClass(testMethodInfo, testMethod, result); return(result); } } catch (TypeInspectionException ex) { // Catch any exception thrown while inspecting the test method and return failure. return(new UnitTestResult[] { new UnitTestResult(ObjectModel.UnitTestOutcome.Failed, ex.Message) }); } }
/// <summary> /// Runs a single test. /// </summary> /// <param name="testMethod"> The test Method. </param> /// <param name="testExecutionRecorder">A instance of TestExecutionRecorderWrapper used to log test execution.</param> /// <param name="testContextProperties"> The test context properties. </param> /// <returns> The <see cref="UnitTestResult"/>. </returns> internal UnitTestResult[] RunSingleTest(TestMethod testMethod, TestExecutionRecorderWrapper testExecutionRecorder, IDictionary <string, object> testContextProperties) { if (testMethod == null) { throw new ArgumentNullException("testMethod"); } if (testExecutionRecorder == null) { throw new ArgumentNullException(nameof(testExecutionRecorder)); } try { using (var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture)) { var properties = new Dictionary <string, object>(testContextProperties); var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties); testContext.SetOutcome(TestTools.UnitTesting.UnitTestOutcome.InProgress); // Get the testMethod var testMethodInfo = this.typeCache.GetTestMethodInfo(testMethod, testContext, testExecutionRecorder, MSTestSettings.CurrentSettings.CaptureDebugTraces); // If the specified TestMethod could not be found, return a NotFound result. if (testMethodInfo == null) { return(new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.NotFound, string.Format(CultureInfo.CurrentCulture, Resource.TestNotFound, testMethod.Name)) }); } // If test cannot be executed, then bail out. if (!testMethodInfo.IsRunnable) { return(new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.NotRunnable, testMethodInfo.NotRunnableReason) }); } return(new TestMethodRunner(testMethodInfo, testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces).Execute()); } } catch (TypeInspectionException ex) { // Catch any exception thrown while inspecting the test method and return failure. return(new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.Failed, ex.Message) }); } }
/// <summary> /// Initializes a new instance of the <see cref="LogMessageListener"/> class. /// </summary> /// <param name="captureDebugTraces">Captures debug traces if true.</param> public LogMessageListener(bool captureDebugTraces) { this.captureDebugTraces = captureDebugTraces; // Cache the original output/error streams and replace it with the own stream. this.redirectedStandardOutput = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "out"); this.redirectedStandardError = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "err"); Logger.OnLogMessage += this.redirectedStandardOutput.WriteLine; if (this.captureDebugTraces) { // This is awkward, it has a side-effect of setting up Console output redirection, but the naming is suggesting that we are // just getting TraceListener manager. this.traceListenerManager = PlatformServiceProvider.Instance.GetTraceListenerManager(this.redirectedStandardOutput, this.redirectedStandardError); // The Debug listener uses Debug.WriteLine and Debug.Write to write the messages, which end up written into Trace.Listeners. // These listeners are static and hence shared across the whole process. We need to capture Debug output only for the current // test, which was historically done by registering a listener in constructor of this class, and by removing the listener on Dispose. // The newly created listener replaced previously registered listener, which was remembered, and put back on dispose. // // This works well as long as there are no tests running in parallel. But as soon as there are tests running in parallel. Then all the // debug output of all tests will be output into the test that was most recently created (because it registered the listener most recently). // // To prevent mixing of outputs, the ThreadSafeStringWriter was re-implemented for net46 and newer to leverage AsyncLocal, which allows the writer to // write only to the output of the current test. This leaves the LogMessageListener with only one task. Make sure that a trace listener is registered // as long as there is any active test. This is still done by constructor and Dispose, but instead of replacing the listener every time, we use listenerCount // to only add the listerner when there is none, and remove it when we are the last one to dispose. // // This would break the behavior for net451, but that functionality was moved further into ThreadSafeStringWriter. lock (traceLock) { if (listenerCount == 0) { redirectedDebugTrace = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "trace"); traceListener = PlatformServiceProvider.Instance.GetTraceListener(redirectedDebugTrace); this.traceListenerManager.Add(traceListener); } listenerCount++; } } }