diff --git a/TestFx.sln b/TestFx.sln index 7c9375407c..bb2fbaed5b 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -222,6 +222,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscoverInternalsProject", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlatformServices.Desktop.Legacy", "src\Adapter\PlatformServices.Desktop.Legacy\PlatformServices.Desktop.Legacy.csproj", "{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutputTestProject", "test\E2ETests\TestAssets\OutputTestProject\OutputTestProject.csproj", "{66608D86-416A-49AF-A937-C47F7E4586AE}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Adapter\PlatformServices.Shared\PlatformServices.Shared.projitems*{2177c273-ae07-43b3-b87a-443e47a23c5a}*SharedItemsImports = 13 @@ -1348,6 +1350,30 @@ Global {F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|x64.Build.0 = Release|Any CPU {F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|x86.ActiveCfg = Release|Any CPU {F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|x86.Build.0 = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|Any CPU.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|ARM.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|ARM.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|x64.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|x64.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|x86.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Code Analysis Debug|x86.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|ARM.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|ARM.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|x64.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|x64.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|x86.ActiveCfg = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Debug|x86.Build.0 = Debug|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|Any CPU.Build.0 = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|ARM.ActiveCfg = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|ARM.Build.0 = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|x64.ActiveCfg = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|x64.Build.0 = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|x86.ActiveCfg = Release|Any CPU + {66608D86-416A-49AF-A937-C47F7E4586AE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1420,6 +1446,7 @@ Global {6B4DE65C-4162-4C52-836A-8F9FA901814A} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} {44A504D9-A0D6-427D-BFB2-DB144A74F0D5} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} {F64A748C-DDBA-4B57-99F4-D9E55684A7A4} = {24088844-2107-4DB2-8F3F-CBCA94FC4B28} + {66608D86-416A-49AF-A937-C47F7E4586AE} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31E0F4D5-975A-41CC-933E-545B2201FAF9} diff --git a/src/Adapter/MSTest.CoreAdapter/Execution/LogMessageListener.cs b/src/Adapter/MSTest.CoreAdapter/Execution/LogMessageListener.cs index f012d8c802..88af97962d 100644 --- a/src/Adapter/MSTest.CoreAdapter/Execution/LogMessageListener.cs +++ b/src/Adapter/MSTest.CoreAdapter/Execution/LogMessageListener.cs @@ -6,6 +6,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution using System; using System.Globalization; using System.IO; + using System.Threading; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; @@ -16,21 +17,23 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution /// public class LogMessageListener : IDisposable { - private static LogMessageListener activeRedirector; - private readonly LogMessageListener previousRedirector; - private readonly ThreadSafeStringWriter redirectLoggerOut; - private readonly ThreadSafeStringWriter redirectStdErr; - private readonly bool captureDebugTraces; + private static object traceLock = new object(); + private static int listenerCount; + private static ThreadSafeStringWriter redirectedDebugTrace; /// /// Trace listener to capture Trace.WriteLines in the test cases /// - private ITraceListener traceListener; + private static ITraceListener traceListener; + private readonly ThreadSafeStringWriter redirectedStandardOutput; + private readonly ThreadSafeStringWriter redirectedStandardError; + private readonly bool captureDebugTraces; /// /// Trace listener Manager to perform operation on tracelistener objects. /// private ITraceListenerManager traceListenerManager; + private bool isDisposed; /// /// Initializes a new instance of the class. @@ -41,30 +44,43 @@ public LogMessageListener(bool captureDebugTraces) this.captureDebugTraces = captureDebugTraces; // Cache the original output/error streams and replace it with the own stream. - this.redirectLoggerOut = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "out"); - this.redirectStdErr = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "err"); - - Logger.OnLogMessage += this.redirectLoggerOut.WriteLine; + this.redirectedStandardOutput = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "out"); + this.redirectedStandardError = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "err"); - // Cache the previous redirector if any and replace the trace listener. - this.previousRedirector = activeRedirector; + Logger.OnLogMessage += this.redirectedStandardOutput.WriteLine; if (this.captureDebugTraces) { - this.traceListener = PlatformServiceProvider.Instance.GetTraceListener(new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "trace")); - this.traceListenerManager = PlatformServiceProvider.Instance.GetTraceListenerManager(this.redirectLoggerOut, this.redirectStdErr); - - // If there was a previous LogMessageListener active, remove its - // TraceListener (it will be restored when this one is disposed). - if (this.previousRedirector != null && this.previousRedirector.traceListener != null) + // 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) { - this.traceListenerManager.Remove(this.previousRedirector.traceListener); - } + if (listenerCount == 0) + { + redirectedDebugTrace = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "trace"); + traceListener = PlatformServiceProvider.Instance.GetTraceListener(redirectedDebugTrace); + this.traceListenerManager.Add(traceListener); + } - this.traceListenerManager.Add(this.traceListener); + listenerCount++; + } } - - activeRedirector = this; } ~LogMessageListener() @@ -75,12 +91,12 @@ public LogMessageListener(bool captureDebugTraces) /// /// Gets logger output /// - public string StandardOutput => this.redirectLoggerOut.ToString(); + public string StandardOutput => this.redirectedStandardOutput.ToString(); /// /// Gets 'Error' Output from the redirected stream /// - public string StandardError => this.redirectStdErr.ToString(); + public string StandardError => this.redirectedStandardError.ToString(); /// /// Gets 'Trace' Output from the redirected stream @@ -89,43 +105,31 @@ public string DebugTrace { get { - return (this.traceListener == null || this.traceListener.GetWriter() == null) ? - string.Empty : this.traceListener.GetWriter().ToString(); + return redirectedDebugTrace?.ToString(); } } public string GetAndClearStandardOutput() { - var output = this.redirectLoggerOut.ToString(); - this.redirectLoggerOut.Clear(); + var output = this.redirectedStandardOutput.ToStringAndClear(); return output; } public string GetAndClearStandardError() { - var output = this.redirectStdErr.ToString(); - this.redirectStdErr.Clear(); + var output = this.redirectedStandardError.ToStringAndClear(); return output; } public string GetAndClearDebugTrace() { - var writer = this.traceListener?.GetWriter(); - if (writer == null) + if (redirectedDebugTrace == null) { return null; } - if (writer is StringWriter sw) - { - var sb = sw.GetStringBuilder(); - var output = sb?.ToString(); - sb?.Clear(); - return output; - } - - // we cannot clear it because it is just a text writer - return writer.ToString(); + var output = redirectedDebugTrace.ToStringAndClear(); + return output; } public void Dispose() @@ -136,47 +140,46 @@ public void Dispose() private void Dispose(bool disposing) { - if (disposing) + if (disposing && !this.isDisposed) { - Logger.OnLogMessage -= this.redirectLoggerOut.WriteLine; - Logger.OnLogMessage -= this.redirectStdErr.WriteLine; + this.isDisposed = true; + Logger.OnLogMessage -= this.redirectedStandardOutput.WriteLine; + Logger.OnLogMessage -= this.redirectedStandardError.WriteLine; - this.redirectLoggerOut.Dispose(); - this.redirectStdErr.Dispose(); + this.redirectedStandardOutput.Dispose(); + this.redirectedStandardError.Dispose(); if (this.captureDebugTraces) { - try + lock (traceLock) { - if (this.traceListener != null) - { - this.traceListenerManager.Remove(this.traceListener); - } - - // Restore the previous LogMessageListener's TraceListener (if there was one) - if (this.previousRedirector != null && this.previousRedirector.traceListener != null) + if (listenerCount == 1) { - this.traceListenerManager.Add(this.previousRedirector.traceListener); + try + { + if (traceListener != null) + { + this.traceListenerManager.Remove(traceListener); + } + } + catch (Exception e) + { + // Catch all exceptions since Dispose should not throw. + PlatformServiceProvider.Instance.AdapterTraceLogger.LogError("ConsoleOutputRedirector.Dispose threw exception: {0}", e); + } + + if (traceListener != null) + { + // Dispose trace manager and listeners + this.traceListenerManager.Dispose(traceListener); + this.traceListenerManager = null; + traceListener = null; + } } - } - catch (Exception e) - { - // Catch all exceptions since Dispose should not throw. - PlatformServiceProvider.Instance.AdapterTraceLogger.LogError( - "ConsoleOutputRedirector.Dispose threw exception: {0}", - e); - } - if (this.traceListener != null) - { - // Dispose trace manager and listeners - this.traceListenerManager.Dispose(this.traceListener); - this.traceListenerManager = null; - this.traceListener = null; + listenerCount--; } } - - activeRedirector = this.previousRedirector; } } } diff --git a/src/Adapter/PlatformServices.Desktop/Services/DesktopTestContextImplementation.cs b/src/Adapter/PlatformServices.Desktop/Services/DesktopTestContextImplementation.cs index 8a9481968f..a9527fd188 100644 --- a/src/Adapter/PlatformServices.Desktop/Services/DesktopTestContextImplementation.cs +++ b/src/Adapter/PlatformServices.Desktop/Services/DesktopTestContextImplementation.cs @@ -45,7 +45,7 @@ public class TestContextImplementation : UTF.TestContext, ITestContext /// /// Writer on which the messages given by the user should be written /// - private StringWriter stringWriter; + private ThreadSafeStringWriter threadSafeStringWriter; /// /// Specifies whether the writer is disposed or not @@ -80,7 +80,9 @@ public TestContextImplementation(ITestMethod testMethod, StringWriter stringWrit Debug.Assert(properties != null, "properties is not null"); this.testMethod = testMethod; - this.stringWriter = stringWriter; + + // Cannot get this type in constructor directly, because all sigantures for all platforms need to be the same. + this.threadSafeStringWriter = (ThreadSafeStringWriter)stringWriter; this.properties = new Dictionary(properties); this.CancellationTokenSource = new CancellationTokenSource(); this.InitializeProperties(); @@ -283,7 +285,7 @@ public override void Write(string message) try { var msg = message?.Replace("\0", "\\0"); - this.stringWriter.Write(msg); + this.threadSafeStringWriter.Write(msg); } catch (ObjectDisposedException) { @@ -307,7 +309,7 @@ public override void Write(string format, params object[] args) try { string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args); - this.stringWriter.Write(message); + this.threadSafeStringWriter.Write(message); } catch (ObjectDisposedException) { @@ -330,7 +332,7 @@ public override void WriteLine(string message) try { var msg = message?.Replace("\0", "\\0"); - this.stringWriter.WriteLine(msg); + this.threadSafeStringWriter.WriteLine(msg); } catch (ObjectDisposedException) { @@ -354,7 +356,7 @@ public override void WriteLine(string format, params object[] args) try { string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args); - this.stringWriter.WriteLine(message); + this.threadSafeStringWriter.WriteLine(message); } catch (ObjectDisposedException) { @@ -446,7 +448,7 @@ public IList GetResultFiles() /// The test context messages added so far. public string GetDiagnosticMessages() { - return this.stringWriter.ToString(); + return this.threadSafeStringWriter.ToString(); } /// @@ -454,8 +456,7 @@ public string GetDiagnosticMessages() /// public void ClearDiagnosticMessages() { - var sb = this.stringWriter.GetStringBuilder(); - sb?.Remove(0, sb.Length); + this.threadSafeStringWriter.ToStringAndClear(); } #endregion diff --git a/src/Adapter/PlatformServices.NetCore/Services/NetCoreTestContextImplementation.cs b/src/Adapter/PlatformServices.NetCore/Services/NetCoreTestContextImplementation.cs index bcf6eca384..851fd6cd37 100644 --- a/src/Adapter/PlatformServices.NetCore/Services/NetCoreTestContextImplementation.cs +++ b/src/Adapter/PlatformServices.NetCore/Services/NetCoreTestContextImplementation.cs @@ -11,6 +11,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices using System.Globalization; using System.IO; using System.Linq; + using System.Net.Sockets; using System.Threading; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel; @@ -51,7 +52,7 @@ public class TestContextImplementation : UTF.TestContext, ITestContext /// private ITestMethod testMethod; - private StringWriter stringWriter; + private ThreadSafeStringWriter threadSafeStringWriter; private bool stringWriterDisposed; /// @@ -67,7 +68,9 @@ public TestContextImplementation(ITestMethod testMethod, StringWriter writer, ID this.testMethod = testMethod; this.properties = new Dictionary(properties); - this.stringWriter = writer; + + // Cannot get this type in constructor directly, because all sigantures for all platforms need to be the same. + this.threadSafeStringWriter = (ThreadSafeStringWriter)writer; this.InitializeProperties(); this.testResultFiles = new List(); this.CancellationTokenSource = new CancellationTokenSource(); @@ -306,7 +309,7 @@ public override void Write(string message) try { var msg = message?.Replace("\0", "\\0"); - this.stringWriter.Write(msg); + this.threadSafeStringWriter.Write(msg); } catch (ObjectDisposedException) { @@ -330,7 +333,7 @@ public override void Write(string format, params object[] args) try { string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args); - this.stringWriter.Write(message); + this.threadSafeStringWriter.Write(message); } catch (ObjectDisposedException) { @@ -353,7 +356,7 @@ public override void WriteLine(string message) try { var msg = message?.Replace("\0", "\\0"); - this.stringWriter.WriteLine(msg); + this.threadSafeStringWriter.WriteLine(msg); } catch (ObjectDisposedException) { @@ -377,7 +380,7 @@ public override void WriteLine(string format, params object[] args) try { string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args); - this.stringWriter.WriteLine(message); + this.threadSafeStringWriter.WriteLine(message); } catch (ObjectDisposedException) { @@ -391,7 +394,7 @@ public override void WriteLine(string format, params object[] args) /// The test context messages added so far. public string GetDiagnosticMessages() { - return this.stringWriter.ToString(); + return this.threadSafeStringWriter.ToString(); } /// @@ -399,8 +402,7 @@ public string GetDiagnosticMessages() /// public void ClearDiagnosticMessages() { - var sb = this.stringWriter.GetStringBuilder(); - sb?.Remove(0, sb.Length); + this.threadSafeStringWriter.ToStringAndClear(); } public void SetDataRow(object dataRow) diff --git a/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10ThreadSafeStringWriter.cs b/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10ThreadSafeStringWriter.cs index 8c8576a94d..4315896ec6 100644 --- a/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10ThreadSafeStringWriter.cs +++ b/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10ThreadSafeStringWriter.cs @@ -43,11 +43,21 @@ public override string ToString() } } - public void Clear() + public string ToStringAndClear() { lock (this.lockObject) { - InvokeBaseClass(() => this.GetStringBuilder().Clear()); + try + { + var sb = this.GetStringBuilder(); + var output = sb.ToString(); + sb.Clear(); + return output; + } + catch (ObjectDisposedException) + { + return default(string); + } } } diff --git a/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13ThreadSafeStringWriter.cs b/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13ThreadSafeStringWriter.cs index cc0e17354d..a9ec8daf90 100644 --- a/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13ThreadSafeStringWriter.cs +++ b/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13ThreadSafeStringWriter.cs @@ -48,6 +48,11 @@ public ThreadSafeStringWriter(IFormatProvider formatProvider, string outputType) } } + public override StringBuilder GetStringBuilder() + { + throw new NotSupportedException("GetStringBuilder is not supported, because it does not allow us to clean the string builder in thread safe way."); + } + /// public override string ToString() { @@ -64,11 +69,27 @@ public override string ToString() } } - public void Clear() + public string ToStringAndClear() { lock (this.lockObject) { - this.GetStringBuilderOrNull()?.Clear(); + try + { + var sb = this.GetStringBuilderOrNull(); + + if (sb == null) + { + return default(string); + } + + var output = sb.ToString(); + sb.Clear(); + return output; + } + catch (ObjectDisposedException) + { + return default(string); + } } } @@ -119,12 +140,6 @@ public override void Write(char[] buffer, int index, int count) } } - // - public override StringBuilder GetStringBuilder() - { - return this.GetStringBuilderOrNull(); - } - /// protected override void Dispose(bool disposing) { diff --git a/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13TraceListenerManager.cs b/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13TraceListenerManager.cs index eeecb8d7b3..d167be9a85 100644 --- a/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13TraceListenerManager.cs +++ b/src/Adapter/PlatformServices.Shared/netstandard1.3/Services/ns13TraceListenerManager.cs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices { using System; + using System.Collections.Generic; using System.Diagnostics; using System.IO; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; diff --git a/test/E2ETests/DiscoveryAndExecutionTests/OutputTests.cs b/test/E2ETests/DiscoveryAndExecutionTests/OutputTests.cs new file mode 100644 index 0000000000..ee182938f1 --- /dev/null +++ b/test/E2ETests/DiscoveryAndExecutionTests/OutputTests.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Linq; + +namespace Microsoft.MSTestV2.Smoke.DiscoveryAndExecutionTests +{ + using System; + using System.Collections.ObjectModel; + using System.IO; + using System.Text.RegularExpressions; + using Microsoft.MSTestV2.CLIAutomation; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using OM = Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class OutputTests : CLITestBase + { + private const string TestAssembly = "OutputTestProject.dll"; + + [TestMethod] + public void OutputIsNotMixedWhenTestsRunInParallel() + { + ValidateOutputForClass(TestAssembly, "UnitTest1"); + } + + [TestMethod] + public void OutputIsNotMixedWhenAsyncTestsRunInParallel() + { + ValidateOutputForClass(TestAssembly, "UnitTest2"); + } + + private void ValidateOutputForClass(string testAssembly, string className) + { + // LogMessageListener uses an implementation of a string writer that captures output per async context. + // This allows us to capture output from tasks even when they are running in parallel. + + // Arrange + var assemblyPath = Path.IsPathRooted(testAssembly) ? testAssembly : this.GetAssetFullPath(testAssembly); + + // Act + var testCases = DiscoverTests(assemblyPath).Where(tc => tc.FullyQualifiedName.Contains(className)).ToList(); + var testResults = RunTests(assemblyPath, testCases); + + // Assert + Assert.AreEqual(3, testResults.Count); + + // Ensure that some tests are running in parallel, because otherwise the output just works correctly. + var firstEnd = testResults.Min(t => t.EndTime); + var someStartedBeforeFirstEnded = testResults.Where(t => t.EndTime != firstEnd).Any(t => firstEnd > t.StartTime); + Assert.IsTrue(someStartedBeforeFirstEnded, "Tests must run in parallel, but there were no other tests that started, before the first one ended."); + + ValidateOutputsAreNotMixed(testResults, "TestMethod1", new[] { "TestMethod2", "TestMethod3" }); + ValidateOutputsAreNotMixed(testResults, "TestMethod2", new[] { "TestMethod1", "TestMethod3" }); + ValidateOutputsAreNotMixed(testResults, "TestMethod3", new[] { "TestMethod1", "TestMethod2" }); + + ValidateInitializationsAndCleanups(testResults); + } + + private static Func IsDebugMessage = m => m.Category == "StdOutMsgs" && m.Text.StartsWith("\n\nDebug Trace:\n"); + private static Func IsStandardOutputMessage = m => m.Category == "StdOutMsgs" && !m.Text.StartsWith("\n\nDebug Trace:\n"); + private static Func IsStandardErrorMessage = m => m.Category == "StdErrMsgs"; + + private static void ValidateOutputsAreNotMixed(ReadOnlyCollection testResults, string methodName, string[] shouldNotContain) + { + ValidateOutputIsNotMixed(testResults, methodName, shouldNotContain, IsStandardOutputMessage); + ValidateOutputIsNotMixed(testResults, methodName, shouldNotContain, IsStandardErrorMessage); + ValidateOutputIsNotMixed(testResults, methodName, shouldNotContain, IsDebugMessage); + } + + private static void ValidateInitializationsAndCleanups(ReadOnlyCollection testResults) + { + ValidateInitializeAndCleanup(testResults, IsStandardOutputMessage); + ValidateInitializeAndCleanup(testResults, IsStandardErrorMessage); + ValidateInitializeAndCleanup(testResults, IsDebugMessage); + } + + private static void ValidateOutputIsNotMixed(ReadOnlyCollection testResults, string methodName, string[] shouldNotContain, Func messageFilter) + { + // Make sure that the output between methods is not mixed. And that every method has test initialize and cleanup. + var testMethod = testResults.Single(t => t.DisplayName == methodName); + Assert.IsNotNull(testMethod, $"Test method {methodName} was not found."); + var message = testMethod.Messages.SingleOrDefault(messageFilter); + Assert.IsNotNull(message, $"Message for {testMethod.DisplayName} was not found. All messages: { string.Join(Environment.NewLine, testMethod.Messages.Select(m=> $"{m.Category} - {m.Text}")) }"); + StringAssert.Matches(message.Text, new Regex(methodName), testMethod.DisplayName); + StringAssert.Matches(message.Text, new Regex("TestInitialize"), testMethod.DisplayName); + StringAssert.Matches(message.Text, new Regex("TestCleanup"), testMethod.DisplayName); + StringAssert.DoesNotMatch(message.Text, new Regex(string.Join("|", shouldNotContain)), testMethod.DisplayName); + } + + private static void ValidateInitializeAndCleanup(ReadOnlyCollection testResults, Func messageFilter) + { + // It is not deterministic where the class initialize and class cleanup will run, so we look at all tests, to make sure it is includes somewhere. + var output = string.Join(Environment.NewLine, testResults.SelectMany(r=>r.Messages).Where(messageFilter).Select(m=>m.Text)); + Assert.IsNotNull(output); + StringAssert.Matches(output, new Regex("ClassInitialize")); + StringAssert.Matches(output, new Regex("ClassCleanup")); + } + } +} diff --git a/test/E2ETests/DiscoveryAndExecutionTests/Utilities/CLITestBase.discovery.cs b/test/E2ETests/DiscoveryAndExecutionTests/Utilities/CLITestBase.discovery.cs index b14d2ca58d..54ed935153 100644 Binary files a/test/E2ETests/DiscoveryAndExecutionTests/Utilities/CLITestBase.discovery.cs and b/test/E2ETests/DiscoveryAndExecutionTests/Utilities/CLITestBase.discovery.cs differ diff --git a/test/E2ETests/TestAssets/OutputTestProject/Assembly.cs b/test/E2ETests/TestAssets/OutputTestProject/Assembly.cs new file mode 100644 index 0000000000..8578dee8d4 --- /dev/null +++ b/test/E2ETests/TestAssets/OutputTestProject/Assembly.cs @@ -0,0 +1,4 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] \ No newline at end of file diff --git a/test/E2ETests/TestAssets/OutputTestProject/OutputTestProject.csproj b/test/E2ETests/TestAssets/OutputTestProject/OutputTestProject.csproj new file mode 100644 index 0000000000..bef06d3947 --- /dev/null +++ b/test/E2ETests/TestAssets/OutputTestProject/OutputTestProject.csproj @@ -0,0 +1,13 @@ + + + net452 + false + false + $(RepoRoot)artifacts\TestAssets\ + + + + + + + diff --git a/test/E2ETests/TestAssets/OutputTestProject/UnitTest1.cs b/test/E2ETests/TestAssets/OutputTestProject/UnitTest1.cs new file mode 100644 index 0000000000..eb2690117c --- /dev/null +++ b/test/E2ETests/TestAssets/OutputTestProject/UnitTest1.cs @@ -0,0 +1,84 @@ +using System; +using System.Diagnostics; +using System.Threading; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace OutputTestProject +{ + + [TestClass] + public class UnitTest1 + { + private static readonly Random rng = new Random(); + + public TestContext TestContext { get; set; } + + [ClassInitialize()] + public static void ClassInitialize(TestContext _) + { + WriteLines("UnitTest1 - ClassInitialize"); + } + + [TestInitialize] + public void TestInitialize() + { + WriteLines("UnitTest1 - TestInitialize"); + } + + [TestCleanup] + public void TestCleanup() + { + WriteLines("UnitTest1 - TestCleanup"); + } + + + [ClassCleanup()] + public static void ClassCleanup() + { + WriteLines($"UnitTest1 - ClassCleanup"); + } + + [TestMethod] + public void TestMethod1() + { + WriteLines("UnitTest1 - TestMethod1"); + // This makes the outputs more likely to run into each other + // when running in parallel. + // It also makes the test longer, because we check in the test + // that all tests started before any test finished (to make sure + // they actually run in parallel), and this gives us more leeway + // on slower machines. + Thread.Sleep(rng.Next(20, 50)); + WriteLines("UnitTest1 - TestMethod1"); + Thread.Sleep(rng.Next(20, 50)); + WriteLines("UnitTest1 - TestMethod1"); + } + + [TestMethod] + public void TestMethod2() + { + WriteLines("UnitTest1 - TestMethod2"); + Thread.Sleep(rng.Next(20, 50)); + WriteLines("UnitTest1 - TestMethod2"); + Thread.Sleep(rng.Next(20, 50)); + WriteLines("UnitTest1 - TestMethod2"); + } + + [TestMethod] + public void TestMethod3() + { + WriteLines("UnitTest1 - TestMethod3"); + Thread.Sleep(rng.Next(20, 50)); + WriteLines("UnitTest1 - TestMethod3"); + Thread.Sleep(rng.Next(20, 50)); + WriteLines("UnitTest1 - TestMethod3"); + } + + private static void WriteLines(string message) + { + Trace.WriteLine(message); + Console.WriteLine(message); + Console.Error.WriteLine(message); + } + } +} diff --git a/test/E2ETests/TestAssets/OutputTestProject/UnitTest2.cs b/test/E2ETests/TestAssets/OutputTestProject/UnitTest2.cs new file mode 100644 index 0000000000..d830c7abec --- /dev/null +++ b/test/E2ETests/TestAssets/OutputTestProject/UnitTest2.cs @@ -0,0 +1,84 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace OutputTestProject +{ + + [TestClass] + public class UnitTest2 + { + private static readonly Random rng = new Random(); + + public TestContext TestContext { get; set; } + + [ClassInitialize()] + public static void ClassInitialize(TestContext _) + { + WriteLines("UnitTest2 - ClassInitialize"); + } + + [TestInitialize] + public void TestInitialize() + { + WriteLines("UnitTest2 - TestInitialize"); + } + + [TestCleanup] + public void TestCleanup() + { + WriteLines("UnitTest2 - TestCleanup"); + } + + + [ClassCleanup()] + public static void ClassCleanup() + { + WriteLines($"UnitTest2 - ClassCleanup"); + } + + [TestMethod] + public async Task TestMethod1() + { + WriteLines("UnitTest2 - TestMethod1"); + // This makes the outputs more likely to run into each other + // when running in parallel. + // It also makes the test longer, because we check in the test + // that all tests started before any test finished (to make sure + // they actually run in parallel), and this gives us more leeway + // on slower machines. + await Task.Delay(rng.Next(20, 50)); + WriteLines("UnitTest2 - TestMethod1"); + await Task.Delay(rng.Next(20, 50)); + WriteLines("UnitTest2 - TestMethod1"); + } + + [TestMethod] + public async Task TestMethod2() + { + WriteLines("UnitTest2 - TestMethod2"); + await Task.Delay(rng.Next(20, 50)); + WriteLines("UnitTest2 - TestMethod2"); + await Task.Delay(rng.Next(20, 50)); + WriteLines("UnitTest2 - TestMethod2"); + } + + [TestMethod] + public async Task TestMethod3() + { + WriteLines("UnitTest2 - TestMethod3"); + await Task.Delay(rng.Next(20, 50)); + WriteLines("UnitTest2 - TestMethod3"); + await Task.Delay(rng.Next(20, 50)); + WriteLines("UnitTest2 - TestMethod3"); + } + + private static void WriteLines(string message) + { + Trace.WriteLine(message); + Console.WriteLine(message); + Console.Error.WriteLine(message); + } + } +} diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/LogMessageListenerTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/LogMessageListenerTests.cs index e5130db64c..929811a608 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/LogMessageListenerTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/LogMessageListenerTests.cs @@ -53,33 +53,30 @@ public void LogMessageListenerShouldCaptureTestFrameworkLogMessages() [TestMethod] public void NoTraceListenerOperationShouldBePerformedIfDebugTraceIsNotEnabled() { - var logMessageListener = new LogMessageListener(false); - this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Add(It.IsAny()), Times.Never); + using (var logMessageListener = new LogMessageListener(false)) + { + this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Add(It.IsAny()), Times.Never); + } } [TestMethod] public void AddTraceListenerOperationShouldBePerformedIfDebugTraceIsEnabled() { - var logMessageListener = new LogMessageListener(true); - this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Add(this.testablePlatformServiceProvider.MockTraceListener.Object), Times.Once); - } - - [TestMethod] - public void DebugTraceShouldReturnTraceOutput() - { - var logMessageListener = new LogMessageListener(true); - StringWriter writer = new StringWriter(new StringBuilder("DummyTrace")); - this.testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - Assert.AreEqual("DummyTrace", logMessageListener.DebugTrace); + using (var logMessageListener = new LogMessageListener(true)) + { + this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Add(this.testablePlatformServiceProvider.MockTraceListener.Object), Times.Once); + } } #region Dispose Tests [TestMethod] public void DisposeShouldNotRemoveTraceListenerIfDebugTracesIsNotEnabled() { - var logMessageListener = new LogMessageListener(false); - logMessageListener.Dispose(); - this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Remove(It.IsAny()), Times.Never); + using (var logMessageListener = new LogMessageListener(false)) + { + logMessageListener.Dispose(); + this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Remove(It.IsAny()), Times.Never); + } } [TestMethod] @@ -91,15 +88,17 @@ public void DisposeShouldRemoveTraceListenerIfDebugTracesIsEnabled() } // Once when Dispose() is called and second time when destructor is called - this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Remove(It.IsAny()), Times.Exactly(2)); + this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Remove(It.IsAny()), Times.Exactly(1)); } [TestMethod] public void DisposeShouldDisposeTraceListener() { - var logMessageListener = new LogMessageListener(true); - logMessageListener.Dispose(); - this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Dispose(this.testablePlatformServiceProvider.MockTraceListener.Object), Times.Once); + using (var logMessageListener = new LogMessageListener(true)) + { + logMessageListener.Dispose(); + this.testablePlatformServiceProvider.MockTraceListenerManager.Verify(mtlm => mtlm.Dispose(this.testablePlatformServiceProvider.MockTraceListener.Object), Times.Once); + } } #endregion diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodInfoTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodInfoTests.cs index 25fa22dc53..688f5bd81d 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodInfoTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodInfoTests.cs @@ -197,56 +197,6 @@ public void TestMethodInfoInvokeShouldHandleAssertInconclusive() Assert.AreEqual(UTF.UnitTestOutcome.Inconclusive, result.Outcome); } - [TestMethodV1] - public void TestMethodInfoInvokeShouldListenForDebugAndTraceLogsWhenEnabled() - { - this.testMethodOptions.CaptureDebugTraces = true; - - StringWriter writer = new StringWriter(new StringBuilder()); - DummyTestClass.TestMethodBody = o => { writer.Write("Trace logs"); }; - - var method = new TestMethodInfo( - this.methodInfo, - this.testClassInfo, - this.testMethodOptions); - - var testablePlatformServiceProvider = new TestablePlatformServiceProvider(); - this.RunWithTestablePlatformService(testablePlatformServiceProvider, () => - { - testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - - PlatformServiceProvider.Instance = testablePlatformServiceProvider; - var result = method.Invoke(null); - - Assert.AreEqual("Trace logs", result.DebugTrace); - }); - } - - [TestMethodV1] - public void TestMethodInfoInvokeShouldNotListenForDebugAndTraceLogsWhenDisabled() - { - this.testMethodOptions.CaptureDebugTraces = false; - - var method = new TestMethodInfo( - this.methodInfo, - this.testClassInfo, - this.testMethodOptions); - StringWriter writer = new StringWriter(new StringBuilder()); - - DummyTestClass.TestMethodBody = o => { writer.Write("Trace logs"); }; - - var testablePlatformServiceProvider = new TestablePlatformServiceProvider(); - this.RunWithTestablePlatformService(testablePlatformServiceProvider, () => - { - testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - - PlatformServiceProvider.Instance = testablePlatformServiceProvider; - var result = method.Invoke(null); - - Assert.IsNull(result.DebugTrace); - }); - } - [TestMethodV1] public void TestMethodInfoInvokeShouldReportTestContextMessages() { diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodRunnerTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodRunnerTests.cs index 9f9a5c7910..dd082732cd 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodRunnerTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodRunnerTests.cs @@ -210,42 +210,6 @@ public void ExecuteForPassingTestShouldReturnUnitTestResultWithPassedOutcome() Assert.AreEqual(AdapterTestOutcome.Passed, results[0].Outcome); } - [TestMethodV1] - public void ExecuteShouldFillInDebugAndTraceLogsFromClassInitialize() - { - StringWriter writer = new StringWriter(new StringBuilder()); - DummyTestClass.ClassInitializeMethodBody = (UTFExtension.TestContext tc) => - { - writer.Write("ClassInit trace"); - }; - this.testClassInfo.ClassInitializeMethod = typeof(DummyTestClass).GetMethod("DummyClassInit"); - var testMethodInfo = new TestableTestmethodInfo(this.methodInfo, this.testClassInfo, this.testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); - var testMethodRunner = new TestMethodRunner(testMethodInfo, this.testMethod, this.testContextImplementation, true); - - this.testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - - var results = testMethodRunner.Execute(); - Assert.AreEqual("ClassInit trace", results[0].DebugTrace); - } - - [TestMethodV1] - public void ExecuteShouldFillInDebugAndTraceLogsFromAssemblyInitialize() - { - StringWriter writer = new StringWriter(new StringBuilder()); - DummyTestClass.AssemblyInitializeMethodBody = (UTFExtension.TestContext tc) => - { - writer.Write("AssemblyInit trace"); - }; - this.testClassInfo.Parent.AssemblyInitializeMethod = typeof(DummyTestClass).GetMethod("DummyAssemblyInit"); - var testMethodInfo = new TestableTestmethodInfo(this.methodInfo, this.testClassInfo, this.testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); - var testMethodRunner = new TestMethodRunner(testMethodInfo, this.testMethod, this.testContextImplementation, true); - - this.testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - - var results = testMethodRunner.Execute(); - Assert.AreEqual("AssemblyInit trace", results[0].DebugTrace); - } - [TestMethodV1] public void ExecuteShouldNotFillInDebugAndTraceLogsIfDebugTraceDisabled() { @@ -283,28 +247,6 @@ public void ExecuteShouldNotFillInDebugAndTraceLogsFromRunningTestMethod() Assert.AreEqual(string.Empty, results[0].DebugTrace); } - [TestMethodV1] - public void ExecuteShouldFillInLogsIfAssemblyInitializeThrows() - { - StringWriter writer = new StringWriter(new StringBuilder()); - DummyTestClass.AssemblyInitializeMethodBody = (UTFExtension.TestContext tc) => - { - writer.Write("Hills"); - tc.WriteLine("Valleys"); - throw new ArgumentException(); - }; - this.testClassInfo.Parent.AssemblyInitializeMethod = typeof(DummyTestClass).GetMethod("DummyAssemblyInit"); - var testMethodInfo = new TestableTestmethodInfo(this.methodInfo, this.testClassInfo, this.testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); - var testMethodRunner = new TestMethodRunner(testMethodInfo, this.testMethod, this.testContextImplementation, true); - - this.testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - - var results = testMethodRunner.Execute(); - - Assert.AreEqual("Hills", results[0].DebugTrace); - StringAssert.Contains(results[0].TestContextMessages, "Valleys"); - } - [TestMethodV1] public void RunTestMethodForTestThrowingExceptionShouldReturnUnitTestResultWithFailedOutcome() { diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/UnitTestRunnerTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/UnitTestRunnerTests.cs index 79e5130a2a..fb7ce88259 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/UnitTestRunnerTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/UnitTestRunnerTests.cs @@ -386,59 +386,6 @@ public void RunCleanupShouldReturnCleanupResultsForAssemblyAndClassCleanupMethod Assert.IsTrue(cleanupresult.Warnings.All(w => w.Contains("NotImplemented"))); } - [TestMethodV1] - public void RunCleanupShouldReturnCleanupResultsWithDebugTraceLogsSetIfDebugTraceEnabled() - { - this.unitTestRunner = new UnitTestRunner(this.GetSettingsWithDebugTrace(true)); - try - { - var type = typeof(DummyTestClassWithCleanupMethods); - var testMethod = new TestMethod(nameof(DummyTestClassWithCleanupMethods.TestMethod), type.FullName, "A", isAsync: false); - - this.testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) - .Returns(Assembly.GetExecutingAssembly()); - - StringWriter writer = new StringWriter(new StringBuilder("DummyTrace")); - - DummyTestClassWithCleanupMethods.ClassCleanupMethodBody = () => - { - writer.Write("ClassCleanup"); - }; - - this.testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - - var testResult = this.unitTestRunner.RunSingleTest(testMethod, this.testRunParameters).FirstOrDefault(); - Assert.IsNotNull(testResult); - Assert.AreEqual("DummyTrace", testResult.DebugTrace); - - var cleanupresult = this.unitTestRunner.RunCleanup(); - Assert.AreEqual("ClassCleanup", cleanupresult.DebugTrace); - } - finally - { - DummyTestClassWithCleanupMethods.ClassCleanupMethodBody = null; - } - } - - [TestMethodV1] - public void RunCleanupShouldReturnCleanupResultsWithNoDebugAndTraceLogsSetIfDebugTraceDisabled() - { - var type = typeof(DummyTestClassWithCleanupMethods); - var methodInfo = type.GetMethod("TestMethod"); - var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - - this.testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) - .Returns(Assembly.GetExecutingAssembly()); - - StringWriter writer = new StringWriter(new StringBuilder("DummyTrace")); - this.testablePlatformServiceProvider.MockTraceListener.Setup(tl => tl.GetWriter()).Returns(writer); - - this.unitTestRunner.RunSingleTest(testMethod, this.testRunParameters); - - var cleanupresult = this.unitTestRunner.RunCleanup(); - Assert.AreEqual(null, cleanupresult.DebugTrace); - } - #endregion #region private helpers @@ -510,28 +457,34 @@ public void TestMethod() [DummyTestClass] private class DummyTestClassWithCleanupMethods { + public DummyTestClassWithCleanupMethods() + { + } + public static Action AssemblyCleanupMethodBody { get; set; } public static Action ClassCleanupMethodBody { get; set; } + public static Action TestMethodBody { get; set; } + + public UTFExtension.TestContext TestContext { get; set; } + [UTF.AssemblyCleanup] public static void AssemblyCleanup() { - if (AssemblyCleanupMethodBody != null) - { - AssemblyCleanupMethodBody.Invoke(); - } + AssemblyCleanupMethodBody?.Invoke(); } [UTF.ClassCleanup] public static void ClassCleanup() { - ClassCleanupMethodBody.Invoke(); + ClassCleanupMethodBody?.Invoke(); } [UTF.TestMethod] public void TestMethod() { + TestMethodBody?.Invoke(this.TestContext); } } diff --git a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestContextImplTests.cs b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestContextImplTests.cs index 6d3faa6e4a..7f8c59e833 100644 --- a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestContextImplTests.cs +++ b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestContextImplTests.cs @@ -43,7 +43,7 @@ public void TestInit() [TestMethod] public void TestContextConstructorShouldInitializeProperties() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsNotNull(this.testContextImplementation.Properties); } @@ -54,7 +54,7 @@ public void TestContextConstructorShouldInitializeDefaultProperties() this.testMethod.Setup(tm => tm.FullClassName).Returns("A.C.M"); this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsNotNull(this.testContextImplementation.Properties); @@ -69,7 +69,7 @@ public void TestContextConstructorShouldInitializeDefaultProperties() [TestMethod] public void CurrentTestOutcomeShouldReturnDefaultOutcome() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.AreEqual(UnitTestOutcome.Failed, this.testContextImplementation.CurrentTestOutcome); } @@ -77,7 +77,7 @@ public void CurrentTestOutcomeShouldReturnDefaultOutcome() [TestMethod] public void CurrentTestOutcomeShouldReturnOutcomeSet() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.SetOutcome(UnitTestOutcome.InProgress); @@ -89,7 +89,7 @@ public void FullyQualifiedTestClassNameShouldReturnTestMethodsFullClassName() { this.testMethod.Setup(tm => tm.FullClassName).Returns("A.C.M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.AreEqual("A.C.M", this.testContextImplementation.FullyQualifiedTestClassName); } @@ -99,7 +99,7 @@ public void TestNameShouldReturnTestMethodsName() { this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.AreEqual("M", this.testContextImplementation.TestName); } @@ -113,7 +113,7 @@ public void PropertiesShouldReturnPropertiesPassedToTestContext() this.properties.Add(property1); this.properties.Add(property2); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); CollectionAssert.Contains(this.testContextImplementation.Properties, property1); CollectionAssert.Contains(this.testContextImplementation.Properties, property2); @@ -124,7 +124,7 @@ public void ContextShouldReturnTestContextObject() { this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsNotNull(this.testContextImplementation.Context); Assert.AreEqual("M", this.testContextImplementation.Context.TestName); @@ -135,7 +135,7 @@ public void TryGetPropertyValueShouldReturnTrueIfPropertyIsPresent() { this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsTrue(this.testContextImplementation.TryGetPropertyValue("TestName", out var propValue)); Assert.AreEqual("M", propValue); @@ -144,7 +144,7 @@ public void TryGetPropertyValueShouldReturnTrueIfPropertyIsPresent() [TestMethod] public void TryGetPropertyValueShouldReturnFalseIfPropertyIsNotPresent() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsFalse(this.testContextImplementation.TryGetPropertyValue("Random", out var propValue)); Assert.IsNull(propValue); @@ -153,7 +153,7 @@ public void TryGetPropertyValueShouldReturnFalseIfPropertyIsNotPresent() [TestMethod] public void AddPropertyShouldAddPropertiesToThePropertyBag() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.AddProperty("SomeNewProperty", "SomeValue"); CollectionAssert.Contains( @@ -164,7 +164,7 @@ public void AddPropertyShouldAddPropertiesToThePropertyBag() [TestMethod] public void AddResultFileShouldThrowIfFileNameIsNull() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); var exception = ActionUtility.PerformActionAndReturnException(() => this.testContextImplementation.AddResultFile(null)); @@ -176,7 +176,7 @@ public void AddResultFileShouldThrowIfFileNameIsNull() [TestMethod] public void AddResultFileShouldThrowIfFileNameIsEmpty() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); var exception = ActionUtility.PerformActionAndReturnException(() => this.testContextImplementation.AddResultFile(string.Empty)); @@ -188,7 +188,7 @@ public void AddResultFileShouldThrowIfFileNameIsEmpty() [TestMethod] public void AddResultFileShouldAddFiletoResultsFiles() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.AddResultFile("C:\\temp.txt"); @@ -200,7 +200,7 @@ public void AddResultFileShouldAddFiletoResultsFiles() [TestMethod] public void AddResultFileShouldAddMultipleFilestoResultsFiles() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new System.IO.StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.AddResultFile("C:\\temp.txt"); this.testContextImplementation.AddResultFile("C:\\temp2.txt"); @@ -214,7 +214,7 @@ public void AddResultFileShouldAddMultipleFilestoResultsFiles() [TestMethod] public void WriteShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("{0} Testing write", 1); StringAssert.Contains(stringWriter.ToString(), "1 Testing write"); @@ -223,7 +223,7 @@ public void WriteShouldWriteToStringWriter() [TestMethod] public void WriteShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("{0} Testing \0 write \0", 1); StringAssert.Contains(stringWriter.ToString(), "1 Testing \\0 write \\0"); @@ -232,7 +232,7 @@ public void WriteShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); this.testContextImplementation.Write("{0} Testing write", 1); @@ -244,7 +244,7 @@ public void WriteShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void WriteWithMessageShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("1 Testing write"); StringAssert.Contains(stringWriter.ToString(), "1 Testing write"); @@ -253,7 +253,7 @@ public void WriteWithMessageShouldWriteToStringWriter() [TestMethod] public void WriteWithMessageShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("1 Testing \0 write \0"); StringAssert.Contains(stringWriter.ToString(), "1 Testing \\0 write \\0"); @@ -262,7 +262,7 @@ public void WriteWithMessageShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteWithMessageShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); this.testContextImplementation.Write("1 Testing write"); @@ -274,7 +274,7 @@ public void WriteWithMessageShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void WriteWithMessageShouldWriteToStringWriterForReturnCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("2 Testing write \n\r"); this.testContextImplementation.Write("3 Testing write\n\r"); @@ -284,7 +284,7 @@ public void WriteWithMessageShouldWriteToStringWriterForReturnCharacters() [TestMethod] public void WriteLineShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("{0} Testing write", 1); @@ -295,7 +295,7 @@ public void WriteLineShouldWriteToStringWriter() [TestMethod] public void WriteLineShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("{0} Testing \0 write \0", 1); @@ -306,7 +306,7 @@ public void WriteLineShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteLineShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); @@ -320,7 +320,7 @@ public void WriteLineShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void WriteLineWithMessageShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("1 Testing write"); @@ -331,7 +331,7 @@ public void WriteLineWithMessageShouldWriteToStringWriter() [TestMethod] public void WriteLineWithMessageShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("1 Testing \0 write \0"); @@ -342,7 +342,7 @@ public void WriteLineWithMessageShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteLineWithMessageShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); @@ -356,7 +356,7 @@ public void WriteLineWithMessageShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void GetDiagnosticMessagesShouldReturnMessagesFromWriteLine() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.WriteLine("1 Testing write"); this.testContextImplementation.WriteLine("2 Its a happy day"); @@ -368,7 +368,7 @@ public void GetDiagnosticMessagesShouldReturnMessagesFromWriteLine() [TestMethod] public void ClearDiagnosticMessagesShouldClearMessagesFromWriteLine() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("1 Testing write"); @@ -382,7 +382,7 @@ public void ClearDiagnosticMessagesShouldClearMessagesFromWriteLine() [TestMethod] public void SetDataRowShouldSetDataRowObjectForCurrentRun() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); DataTable dataTable = new DataTable(); @@ -402,7 +402,7 @@ public void SetDataRowShouldSetDataRowObjectForCurrentRun() [TestMethod] public void SetDataConnectionShouldSetDbConnectionForFetchingData() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.Odbc"); diff --git a/test/UnitTests/PlatformServices.NetCore.Unit.Tests/Services/NetCoreTestContextImplementationTests.cs b/test/UnitTests/PlatformServices.NetCore.Unit.Tests/Services/NetCoreTestContextImplementationTests.cs index 19e01619db..f0e1a7cc6a 100644 --- a/test/UnitTests/PlatformServices.NetCore.Unit.Tests/Services/NetCoreTestContextImplementationTests.cs +++ b/test/UnitTests/PlatformServices.NetCore.Unit.Tests/Services/NetCoreTestContextImplementationTests.cs @@ -47,7 +47,7 @@ public void TestInit() [TestMethod] public void TestContextConstructorShouldInitializeProperties() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsNotNull(this.testContextImplementation.Properties); } @@ -58,7 +58,7 @@ public void TestContextConstructorShouldInitializeDefaultProperties() this.testMethod.Setup(tm => tm.FullClassName).Returns("A.C.M"); this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsNotNull(this.testContextImplementation.Properties); @@ -73,7 +73,7 @@ public void TestContextConstructorShouldInitializeDefaultProperties() [TestMethod] public void CurrentTestOutcomeShouldReturnDefaultOutcome() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.AreEqual(UnitTestOutcome.Failed, this.testContextImplementation.CurrentTestOutcome); } @@ -81,7 +81,7 @@ public void CurrentTestOutcomeShouldReturnDefaultOutcome() [TestMethod] public void CurrentTestOutcomeShouldReturnOutcomeSet() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.SetOutcome(UnitTestOutcome.InProgress); @@ -93,7 +93,7 @@ public void FullyQualifiedTestClassNameShouldReturnTestMethodsFullClassName() { this.testMethod.Setup(tm => tm.FullClassName).Returns("A.C.M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.AreEqual("A.C.M", this.testContextImplementation.FullyQualifiedTestClassName); } @@ -103,7 +103,7 @@ public void TestNameShouldReturnTestMethodsName() { this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.AreEqual("M", this.testContextImplementation.TestName); } @@ -117,7 +117,7 @@ public void PropertiesShouldReturnPropertiesPassedToTestContext() this.properties.Add(property1); this.properties.Add(property2); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); CollectionAssert.Contains(this.testContextImplementation.Properties, property1); CollectionAssert.Contains(this.testContextImplementation.Properties, property2); @@ -128,7 +128,7 @@ public void ContextShouldReturnTestContextObject() { this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsNotNull(this.testContextImplementation.Context); Assert.AreEqual("M", this.testContextImplementation.Context.TestName); @@ -139,7 +139,7 @@ public void TryGetPropertyValueShouldReturnTrueIfPropertyIsPresent() { this.testMethod.Setup(tm => tm.Name).Returns("M"); - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsTrue(this.testContextImplementation.TryGetPropertyValue("TestName", out object propValue)); Assert.AreEqual("M", propValue); } @@ -147,7 +147,7 @@ public void TryGetPropertyValueShouldReturnTrueIfPropertyIsPresent() [TestMethod] public void TryGetPropertyValueShouldReturnFalseIfPropertyIsNotPresent() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); Assert.IsFalse(this.testContextImplementation.TryGetPropertyValue("Random", out object propValue)); Assert.IsNull(propValue); } @@ -155,7 +155,7 @@ public void TryGetPropertyValueShouldReturnFalseIfPropertyIsNotPresent() [TestMethod] public void AddPropertyShouldAddPropertiesToThePropertyBag() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.AddProperty("SomeNewProperty", "SomeValue"); @@ -167,7 +167,7 @@ public void AddPropertyShouldAddPropertiesToThePropertyBag() [TestMethod] public void WriteShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("{0} Testing write", 1); StringAssert.Contains(stringWriter.ToString(), "1 Testing write"); @@ -176,7 +176,7 @@ public void WriteShouldWriteToStringWriter() [TestMethod] public void WriteShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("{0} Testing \0 write \0", 1); StringAssert.Contains(stringWriter.ToString(), "1 Testing \\0 write \\0"); @@ -185,7 +185,7 @@ public void WriteShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); this.testContextImplementation.Write("{0} Testing write", 1); @@ -197,7 +197,7 @@ public void WriteShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void WriteWithMessageShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("1 Testing write"); StringAssert.Contains(stringWriter.ToString(), "1 Testing write"); @@ -206,7 +206,7 @@ public void WriteWithMessageShouldWriteToStringWriter() [TestMethod] public void WriteWithMessageShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("1 Testing \0 write \0"); StringAssert.Contains(stringWriter.ToString(), "1 Testing \\0 write \\0"); @@ -215,7 +215,7 @@ public void WriteWithMessageShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteWithMessageShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); this.testContextImplementation.Write("1 Testing write"); @@ -227,7 +227,7 @@ public void WriteWithMessageShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void WriteWithMessageShouldWriteToStringWriterForReturnCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.Write("2 Testing write \n\r"); this.testContextImplementation.Write("3 Testing write\n\r"); @@ -237,7 +237,7 @@ public void WriteWithMessageShouldWriteToStringWriterForReturnCharacters() [TestMethod] public void WriteLineShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("{0} Testing write", 1); @@ -248,7 +248,7 @@ public void WriteLineShouldWriteToStringWriter() [TestMethod] public void WriteLineShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("{0} Testing \0 write \0", 1); @@ -259,7 +259,7 @@ public void WriteLineShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteLineShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); @@ -273,7 +273,7 @@ public void WriteLineShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void WriteLineWithMessageShouldWriteToStringWriter() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("1 Testing write"); @@ -284,7 +284,7 @@ public void WriteLineWithMessageShouldWriteToStringWriter() [TestMethod] public void WriteLineWithMessageShouldWriteToStringWriterForNullCharacters() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("1 Testing \0 write \0"); @@ -295,7 +295,7 @@ public void WriteLineWithMessageShouldWriteToStringWriterForNullCharacters() [TestMethod] public void WriteLineWithMessageShouldNotThrowIfStringWriterIsDisposed() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); stringWriter.Dispose(); @@ -309,7 +309,7 @@ public void WriteLineWithMessageShouldNotThrowIfStringWriterIsDisposed() [TestMethod] public void GetDiagnosticMessagesShouldReturnMessagesFromWriteLine() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.WriteLine("1 Testing write"); this.testContextImplementation.WriteLine("2 Its a happy day"); @@ -321,7 +321,7 @@ public void GetDiagnosticMessagesShouldReturnMessagesFromWriteLine() [TestMethod] public void ClearDiagnosticMessagesShouldClearMessagesFromWriteLine() { - var stringWriter = new StringWriter(); + var stringWriter = new ThreadSafeStringWriter(null, "test"); this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, stringWriter, this.properties); this.testContextImplementation.WriteLine("1 Testing write"); @@ -335,7 +335,7 @@ public void ClearDiagnosticMessagesShouldClearMessagesFromWriteLine() [TestMethod] public void AddResultFileShouldAddFiletoResultsFiles() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.AddResultFile("C:\\files\\myfile.txt"); var resultFile = this.testContextImplementation.GetResultFiles(); @@ -346,7 +346,7 @@ public void AddResultFileShouldAddFiletoResultsFiles() [TestMethod] public void AddResultFileShouldThrowIfFileNameIsNull() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); var exception = ActionUtility.PerformActionAndReturnException(() => this.testContextImplementation.AddResultFile(null)); @@ -357,7 +357,7 @@ public void AddResultFileShouldThrowIfFileNameIsNull() [TestMethod] public void AddResultFileShouldThrowIfFileNameIsEmpty() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); var exception = ActionUtility.PerformActionAndReturnException(() => this.testContextImplementation.AddResultFile(string.Empty)); @@ -368,7 +368,7 @@ public void AddResultFileShouldThrowIfFileNameIsEmpty() [TestMethod] public void AddResultFileShouldAddMultipleFilestoResultsFiles() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.AddResultFile("C:\\files\\file1.txt"); this.testContextImplementation.AddResultFile("C:\\files\\files2.html"); @@ -382,7 +382,7 @@ public void AddResultFileShouldAddMultipleFilestoResultsFiles() [TestMethod] public void GetResultFilesShouldReturnNullIfNoAddedResultFiles() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); var resultFile = this.testContextImplementation.GetResultFiles(); @@ -392,7 +392,7 @@ public void GetResultFilesShouldReturnNullIfNoAddedResultFiles() [TestMethod] public void GetResultFilesShouldReturnListOfAddedResultFiles() { - this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new StringWriter(), this.properties); + this.testContextImplementation = new TestContextImplementation(this.testMethod.Object, new ThreadSafeStringWriter(null, "test"), this.properties); this.testContextImplementation.AddResultFile("C:\\files\\myfile.txt"); this.testContextImplementation.AddResultFile("C:\\files\\myfile2.txt");