Skip to content

Commit

Permalink
Add task aware string writer with asynclocal (#940)
Browse files Browse the repository at this point in the history
* Add thread safe string writer with asynclocal
* Use task aware string writer to collect output

Co-authored-by: Medeni Baykal <433724+Haplois@users.noreply.github.com>
  • Loading branch information
nohwnd and Haplois authored Nov 23, 2021
1 parent 76631dc commit 3904a64
Show file tree
Hide file tree
Showing 69 changed files with 6,988 additions and 109 deletions.
31 changes: 29 additions & 2 deletions TestFx.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29728.190
# Visual Studio Version 17
VisualStudioVersion = 17.1.31910.343
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FF8B1B72-55A1-4FFE-809E-7B79323ED8D0}"
EndProject
Expand Down Expand Up @@ -220,6 +220,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReferencedProjectFromDataSo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscoverInternalsProject", "test\E2ETests\TestAssets\DiscoverInternalsProject\DiscoverInternalsProject.csproj", "{44A504D9-A0D6-427D-BFB2-DB144A74F0D5}"
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
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Adapter\PlatformServices.Shared\PlatformServices.Shared.projitems*{2177c273-ae07-43b3-b87a-443e47a23c5a}*SharedItemsImports = 13
Expand Down Expand Up @@ -1322,6 +1324,30 @@ Global
{44A504D9-A0D6-427D-BFB2-DB144A74F0D5}.Release|x64.Build.0 = Release|Any CPU
{44A504D9-A0D6-427D-BFB2-DB144A74F0D5}.Release|x86.ActiveCfg = Release|Any CPU
{44A504D9-A0D6-427D-BFB2-DB144A74F0D5}.Release|x86.Build.0 = Release|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|Any CPU.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|ARM.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|ARM.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|x64.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|x64.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|x86.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Code Analysis Debug|x86.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|ARM.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|ARM.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|x64.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|x64.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|x86.ActiveCfg = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Debug|x86.Build.0 = Debug|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|Any CPU.Build.0 = Release|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|ARM.ActiveCfg = Release|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|ARM.Build.0 = Release|Any CPU
{F64A748C-DDBA-4B57-99F4-D9E55684A7A4}.Release|x64.ActiveCfg = Release|Any CPU
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1393,6 +1419,7 @@ Global
{35D010CC-CDF2-4115-BCFB-E2E3D21C1055} = {CA01DAF5-8D9D-496E-9AD3-94BB7FBB2D34}
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {31E0F4D5-975A-41CC-933E-545B2201FAF9}
Expand Down
2 changes: 1 addition & 1 deletion scripts/test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function Run-Test([string[]] $testContainers, [string[]] $netCoreTestContainers)
{
$vstestPath = Get-VSTestPath

$additionalArguments = ''
$additionalArguments = @('/Blame:CollectHangDump;TestTimeout=5min')
if($TFT_Parallel)
{
$additionalArguments += "/parallel"
Expand Down
33 changes: 33 additions & 0 deletions src/Adapter/Build/Desktop.Legacy/MSTest.TestAdapter.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<TestAdapterContent Include="$(MSBuildThisFileDirectory)..\_common\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll">
<Link>Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</TestAdapterContent>
<TestAdapterContent Include="$(MSBuildThisFileDirectory)..\_common\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll">
<Link>Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</TestAdapterContent>
<TestAdapterContent Include="$(MSBuildThisFileDirectory)..\_common\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll">
<Link>Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</TestAdapterContent>
<TestAdapterContent Include="$(MSBuildThisFileDirectory)\Microsoft.TestPlatform.AdapterUtilities.dll">
<Link>Microsoft.TestPlatform.AdapterUtilities.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</TestAdapterContent>
</ItemGroup>

<ItemGroup>
<!-- Including `@(TestAdapterContent)` in the `None` ItemGroup to get the `CopyToOutputDirectory`
behavior be default, package consumers can opt-out of this behavior
by removing `@(TestAdapterContent)` from the `None` ItemGroup
i.e. `<None Remove="@(TestAdapterContent)" />` -->
<None Include="@(TestAdapterContent)" />
</ItemGroup>
</Project>
35 changes: 35 additions & 0 deletions src/Adapter/Build/Desktop.Legacy/MSTest.TestAdapter.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<EnableMSTestV2CopyResources Condition=" '$(EnableMSTestV2CopyResources)' == '' ">true</EnableMSTestV2CopyResources>
</PropertyGroup>

<Target Name="GetMSTestV2CultureHierarchy">
<!-- Only traversing 5 levels in the culture hierarchy. This is the maximum lenght for all cultures and should be sufficient to get to a culture name that maps to a resource folder we package.
The root culture name for all cultures is invariant whose name is ''(empty) and the parent for invariant culture is invariant itself.(https://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.parent(v=vs.110).aspx.)
So the below code should not break build in any case. -->
<ItemGroup>
<CurrentUICultureHierarchy Include="$([System.Globalization.CultureInfo]::CurrentUICulture.Name)" />
<CurrentUICultureHierarchy Include="$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Name)" Condition=" '$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Name)' != '' "/>
<CurrentUICultureHierarchy Include="$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Parent.Name)" Condition=" '$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Parent.Name)' != '' "/>
<CurrentUICultureHierarchy Include="$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Parent.Parent.Name)" Condition=" '$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Parent.Parent.Name)' != '' "/>
<CurrentUICultureHierarchy Include="$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Parent.Parent.Parent.Name)" Condition=" '$([System.Globalization.CultureInfo]::CurrentUICulture.Parent.Parent.Parent.Parent.Name)' != '' "/>
</ItemGroup>
</Target>

<!-- Copy resources over to $(TargetDir) if this is a localized build. -->
<Target Name="CopyMSTestV2Resources" BeforeTargets="PrepareForBuild" Condition=" '$(EnableMSTestV2CopyResources)' == 'true' " DependsOnTargets="GetMSTestV2CultureHierarchy">
<ItemGroup>
<MSTestV2ResourceFiles Include="$(MSBuildThisFileDirectory)..\_common\%(CurrentUICultureHierarchy.Identity)\*resources.dll">
<CultureString>%(CurrentUICultureHierarchy.Identity)</CultureString>
</MSTestV2ResourceFiles>

<Content Include="@(MSTestV2ResourceFiles)" Condition=" @(MSTestV2ResourceFiles) != '' ">
<Link>%(MSTestV2ResourceFiles.CultureString)\%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</Content>
</ItemGroup>
</Target>

</Project>
2 changes: 1 addition & 1 deletion src/Adapter/Build/Desktop/MSTest.TestAdapter.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</TestAdapterContent>
<TestAdapterContent Include="$(MSBuildThisFileDirectory)..\_common\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll">
<TestAdapterContent Include="$(MSBuildThisFileDirectory)Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll">
<Link>Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;

using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
Expand Down Expand Up @@ -265,7 +266,7 @@ private bool DynamicDataAttached(IDictionary<string, object> sourceLevelParamete
return false;
}

using (var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture))
using (var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "all"))
{
var testMethod = test.TestMethod;
var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, sourceLevelParameters);
Expand Down
46 changes: 40 additions & 6 deletions src/Adapter/MSTest.CoreAdapter/Execution/LogMessageListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
using System;
using System.Globalization;
using System.IO;

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting.Logging;

Expand All @@ -18,8 +18,8 @@ public class LogMessageListener : IDisposable
{
private static LogMessageListener activeRedirector;
private readonly LogMessageListener previousRedirector;
private readonly TextWriter redirectLoggerOut;
private readonly TextWriter redirectStdErr;
private readonly ThreadSafeStringWriter redirectLoggerOut;
private readonly ThreadSafeStringWriter redirectStdErr;
private readonly bool captureDebugTraces;

/// <summary>
Expand All @@ -41,8 +41,8 @@ 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);
this.redirectStdErr = new ThreadSafeStringWriter(CultureInfo.InvariantCulture);
this.redirectLoggerOut = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "out");
this.redirectStdErr = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "err");

Logger.OnLogMessage += this.redirectLoggerOut.WriteLine;

Expand All @@ -51,7 +51,7 @@ public LogMessageListener(bool captureDebugTraces)

if (this.captureDebugTraces)
{
this.traceListener = PlatformServiceProvider.Instance.GetTraceListener(new ThreadSafeStringWriter(CultureInfo.InvariantCulture));
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
Expand Down Expand Up @@ -94,6 +94,40 @@ public string DebugTrace
}
}

public string GetAndClearStandardOutput()
{
var output = this.redirectLoggerOut.ToString();
this.redirectLoggerOut.Clear();
return output;
}

public string GetAndClearStandardError()
{
var output = this.redirectStdErr.ToString();
this.redirectStdErr.Clear();
return output;
}

public string GetAndClearDebugTrace()
{
var writer = this.traceListener?.GetWriter();
if (writer == 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();
}

public void Dispose()
{
this.Dispose(true);
Expand Down
6 changes: 3 additions & 3 deletions src/Adapter/MSTest.CoreAdapter/Execution/TestMethodInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ public virtual TestResult Invoke(object[] arguments)
if (result != null)
{
result.Duration = watch.Elapsed;
result.DebugTrace = listener.DebugTrace;
result.LogOutput = listener.StandardOutput;
result.LogError = listener.StandardError;
result.DebugTrace = listener.GetAndClearDebugTrace();
result.LogOutput = listener.GetAndClearStandardOutput();
result.LogError = listener.GetAndClearStandardError();
result.TestContextMessages = this.TestMethodOptions.TestContext.GetAndClearDiagnosticMessages();
result.ResultFiles = this.TestMethodOptions.TestContext.GetResultFiles();
}
Expand Down
6 changes: 3 additions & 3 deletions src/Adapter/MSTest.CoreAdapter/Execution/TestMethodRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ internal UnitTestResult[] Execute()
}
finally
{
initLogs = logListener.StandardOutput;
initTrace = logListener.DebugTrace;
initErrorLogs = logListener.StandardError;
initLogs = logListener.GetAndClearStandardOutput();
initTrace = logListener.GetAndClearDebugTrace();
initErrorLogs = logListener.GetAndClearStandardError();
inittestContextMessages = this.testContext.GetAndClearDiagnosticMessages();
}
}
Expand Down
9 changes: 5 additions & 4 deletions src/Adapter/MSTest.CoreAdapter/Execution/UnitTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TPOM = Microsoft.VisualStudio.TestPlatform.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
Expand Down Expand Up @@ -112,7 +113,7 @@ internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary<strin

try
{
using (var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture))
using (var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "context"))
{
var properties = new Dictionary<string, object>(testContextProperties);
var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties);
Expand Down Expand Up @@ -181,9 +182,9 @@ internal RunCleanupResult RunCleanup()
{
// Replacing the null character with a string.replace should work.
// If this does not work for a specific dotnet version a custom function doing the same needs to be put in place.
result.StandardOut = redirector.StandardOutput?.Replace("\0", "\\0");
result.StandardError = redirector.StandardError?.Replace("\0", "\\0");
result.DebugTrace = redirector.DebugTrace?.Replace("\0", "\\0");
result.StandardOut = redirector.GetAndClearStandardOutput()?.Replace("\0", "\\0");
result.StandardError = redirector.GetAndClearStandardError()?.Replace("\0", "\\0");
result.DebugTrace = redirector.GetAndClearDebugTrace()?.Replace("\0", "\\0");
}
}

Expand Down
1 change: 0 additions & 1 deletion src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
<Compile Include="ObjectModel\StackTraceInformation.cs" />
<Compile Include="ObjectModel\TestMethod.cs" />
<Compile Include="Execution\TestMethodInfo.cs" />
<Compile Include="Execution\ThreadSafeStringWriter.cs" />
<Compile Include="ObjectModel\TestMethodOptions.cs" />
<Compile Include="ObjectModel\TypeInspectionException.cs" />
<Compile Include="Helpers\UnitTestOutcomeHelper.cs" />
Expand Down
Loading

0 comments on commit 3904a64

Please # to comment.