Skip to content

Commit

Permalink
Move to xunit.v3.runner.utility
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Jun 14, 2024
1 parent b60599b commit e546e93
Show file tree
Hide file tree
Showing 100 changed files with 1,024 additions and 15,906 deletions.
10 changes: 5 additions & 5 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ as the only supported IDE environment (others like Resharper should work, though

You will need the following software installed:

* .NET Framework 4.6.2 or later (part of the Windows OS)
* [.NET SDK 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
* .NET Framework 4.7.2 or later (part of the Windows OS)
* [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet/8.0)
* [.NET 6.0 Runtime](https://dotnet.microsoft.com/download/dotnet/6.0)
* [git](https://git-scm.com/downloads)
* PowerShell (or [PowerShell Core](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6))
Expand Down Expand Up @@ -43,17 +43,17 @@ Ensure that you have configured PowerShell to be able to run local unsigned scri

## Debugging

Debugging the VS Adapter is tricky. There are two ways to do it depending on whether you want to do it under `net462` or `net6.0`. In all cases, you'll currently need to build your own test adapter NuGet package using `./build.ps1 Build` first to ensure you have local symbols. The symbols are not in the public package. It's helpful to add it to a local `\packages` directory and then use an entry like `<add key="Local Packages" value=".\packages" />` in your `NuGet.config` file to point to it. Don't forget to eventually delete it from your global profile `.nuget\packages\xunit...` when you're done.
Debugging the VS Adapter is tricky. There are two ways to do it depending on whether you want to do it under `net472` or `net6.0`. In all cases, you'll currently need to build your own test adapter NuGet package using `./build.ps1 Build` first to ensure you have local symbols. The symbols are not in the public package. It's helpful to add it to a local `\packages` directory and then use an entry like `<add key="Local Packages" value=".\packages" />` in your `NuGet.config` file to point to it. Don't forget to eventually delete it from your global profile `.nuget\packages\xunit...` when you're done.

### `net462`
### `net472`
Easiest thing to do is add a `launchSettings.json` file that adds the `vstest.console.exe` as a startup project and point it to an xunit dll. Something like the following (use `/listtests` if you just want to debug the discovery portion):

```json
{
"profiles": {
"vstest console": {
"executablePath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\IDE\\CommonExtensions\\Microsoft\\TestWindow\\vstest.console.exe",
"commandLineArgs": ".\\bin\\Debug\\net462\\Tests.System.Reactive.dll /TestAdapterPath:.\\bin\\Debug\\net462 /listtests",
"commandLineArgs": ".\\bin\\Debug\\net472\\Tests.System.Reactive.dll /TestAdapterPath:.\\bin\\Debug\\net472 /listtests",
"workingDirectory": "C:\\dev\\RxNET\\Rx.NET\\Source\\Tests.System.Reactive\\"
}
}
Expand Down
27 changes: 1 addition & 26 deletions Directory.build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,11 @@
<Import Project="Versions.props"/>

<PropertyGroup>
<Authors>.NET Foundation and Contributors</Authors>
<Company>.NET Foundation</Company>
<Copyright>Copyright (c) .NET Foundation and Contributors.</Copyright>
<DebugType>embedded</DebugType>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<LangVersion>12.0</LangVersion>
<MSBuildCopyContentTransitively>false</MSBuildCopyContentTransitively>
<PackageIcon>logo-512-transparent.png</PackageIcon>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/xunit/visualstudio.xunit</PackageProjectUrl>
<Product>xUnit.net Testing Framework</Product>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="$(NerdbankGitVersioningVersion)" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="$(MicrosoftSourceLinkGitHubVersion)" PrivateAssets="All"/>
</ItemGroup>

<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<Target Name="AddCommitHashToAssemblyAttributes" BeforeTargets="GetAssemblyAttributes">
<ItemGroup>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition=" '$(SourceRevisionId)' != '' ">
<_Parameter1>CommitHash</_Parameter1>
<_Parameter2>$(SourceRevisionId)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
</Target>

</Project>
6 changes: 4 additions & 2 deletions Versions.props
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<Project>

<PropertyGroup>
<ILRepackVersion>2.0.33</ILRepackVersion>
<MicrosoftNetCoreAppRefVersion>6.0.11</MicrosoftNetCoreAppRefVersion>
<MicrosoftNetTestSdkVersion>17.10.0</MicrosoftNetTestSdkVersion>
<MicrosoftSourceLinkGitHubVersion>8.0.0</MicrosoftSourceLinkGitHubVersion>
<MicrosoftTestPlatformObjectModelVersion>$(MicrosoftNetTestSdkVersion)</MicrosoftTestPlatformObjectModelVersion>
<NerdbankGitVersioningVersion>3.6.133</NerdbankGitVersioningVersion>
<NSubstituteVersion>5.1.0</NSubstituteVersion>
<TunnelVisionLabsReferenceAssemblyAnnotatorVersion>1.0.0-alpha.160</TunnelVisionLabsReferenceAssemblyAnnotatorVersion>
<XunitAnalyzersVersion>1.14.0</XunitAnalyzersVersion>
<XunitVersion>2.8.1</XunitVersion>
<XunitAnalyzersVersion>1.15.0-pre.10</XunitAnalyzersVersion>
<XunitV2Version>2.8.2-pre.12</XunitV2Version>
<XunitV3Version>0.1.1-pre.470</XunitV3Version>
</PropertyGroup>

</Project>
4 changes: 2 additions & 2 deletions src/xunit.runner.visualstudio/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ namespace Xunit.Runner.VisualStudio;
public static class Constants
{
#if NETFRAMEWORK
public const string ExecutorUri = "executor://xunit/VsTestRunner2/net";
public const string ExecutorUri = "executor://xunit/VsTestRunner3/netfx/";
#elif NETCOREAPP
public const string ExecutorUri = "executor://xunit/VsTestRunner2/netcoreapp";
public const string ExecutorUri = "executor://xunit/VsTestRunner3/netcore/";
#else
#error Unknown target platform
#endif
Expand Down
4 changes: 4 additions & 0 deletions src/xunit.runner.visualstudio/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// This is not targeted for assemblies in .NET Framework, but when it's seen on an assembly for code coverage
// purposes it'll still be honored, so we redefine it here and disable CS0436 at the project level so that
// the project will still build properly.

[assembly: System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]

#if NETFRAMEWORK
Expand Down
40 changes: 21 additions & 19 deletions src/xunit.runner.visualstudio/Sinks/DiagnosticMessageSink.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
using System;
using Xunit.Runner.Common;

namespace Xunit.Runner.VisualStudio;

public class DiagnosticMessageSink : DiagnosticEventSink
{
DiagnosticMessageSink()
{ }

public static DiagnosticMessageSink ForDiagnostics(
public DiagnosticMessageSink(
LoggerHelper log,
string assemblyDisplayName,
bool showDiagnostics)
string? assemblyDisplayName = null,
bool showDiagnostics = false,
bool showInternalDiagnostics = false)
{
var result = new DiagnosticMessageSink();
var header = assemblyDisplayName is null ? string.Empty : assemblyDisplayName + ": ";

if (showDiagnostics)
result.DiagnosticMessageEvent += args => log.LogWarning("{0}: {1}", assemblyDisplayName, args.Message.Message);

return result;
DiagnosticMessageEvent += args => log.LogWarning("{0}{1}", header, args.Message.Message);
if (showInternalDiagnostics)
InternalDiagnosticMessageEvent += args => log.Log("{0}", args.Message.Message);
}

public static DiagnosticMessageSink ForInternalDiagnostics(
[Obsolete("Would like to see this collapsed")]
public static DiagnosticMessageSink ForDiagnostics(
LoggerHelper log,
bool showDiagnostics)
{
var result = new DiagnosticMessageSink();

if (showDiagnostics)
result.DiagnosticMessageEvent += args => log.Log("{0}", args.Message.Message);
string assemblyDisplayName,
bool showDiagnostics) =>
new(log, assemblyDisplayName, showDiagnostics, showInternalDiagnostics: false);

return result;
}
[Obsolete("Would like to see this collapsed")]
public static DiagnosticMessageSink ForInternalDiagnostics(
LoggerHelper log,
bool showInternalDiagnostics) =>
new(log, assemblyDisplayName: null, showDiagnostics: false, showInternalDiagnostics);
}
3 changes: 2 additions & 1 deletion src/xunit.runner.visualstudio/Sinks/IVsDiscoverySink.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using Xunit.v3;

namespace Xunit.Runner.VisualStudio;

internal interface IVsDiscoverySink : IMessageSinkWithTypes, IDisposable
internal interface IVsDiscoverySink : _IMessageSink, IDisposable
{
int Finish();
}
71 changes: 36 additions & 35 deletions src/xunit.runner.visualstudio/Sinks/VsDiscoverySink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,41 @@
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Xunit.Abstractions;
using Xunit.Runner.Common;
using Xunit.Sdk;
using Xunit.v3;

#if NETCOREAPP
using System.Reflection;
#endif

namespace Xunit.Runner.VisualStudio;

public sealed class VsDiscoverySink : IMessageSinkWithTypes, IVsDiscoverySink, IDisposable
public sealed class VsDiscoverySink : IVsDiscoverySink, IDisposable
{
static readonly string Ellipsis = new((char)183, 3);
const int MaximumDisplayNameLength = 447;
const int TestCaseDescriptorBatchSize = 100;
const int TestCaseBatchSize = 100;

static readonly Action<TestCase, string, string>? addTraitThunk = GetAddTraitThunk();
static readonly Uri uri = new(Constants.ExecutorUri);

readonly Func<bool> cancelThunk;
readonly ITestCaseDescriptorProvider descriptorProvider;
readonly ITestFrameworkDiscoveryOptions discoveryOptions;
readonly _ITestFrameworkDiscoveryOptions discoveryOptions;
readonly ITestCaseDiscoverySink discoverySink;
readonly DiscoveryEventSink discoveryEventSink = new();
readonly LoggerHelper logger;
readonly string source;
readonly List<ITestCase> testCaseBatch = new();
readonly List<_TestCaseDiscovered> testCaseBatch = new();
readonly TestPlatformContext testPlatformContext;
readonly TestCaseFilter testCaseFilter;

public VsDiscoverySink(
string source,
ITestFrameworkDiscoverer discoverer,
IFrontControllerDiscoverer discoverer,
LoggerHelper logger,
ITestCaseDiscoverySink discoverySink,
ITestFrameworkDiscoveryOptions discoveryOptions,
_ITestFrameworkDiscoveryOptions discoveryOptions,
TestPlatformContext testPlatformContext,
TestCaseFilter testCaseFilter,
Func<bool> cancelThunk)
Expand All @@ -52,10 +53,8 @@ public VsDiscoverySink(
this.testCaseFilter = testCaseFilter;
this.cancelThunk = cancelThunk;

descriptorProvider = (discoverer as ITestCaseDescriptorProvider) ?? new DefaultTestCaseDescriptorProvider(discoverer);

discoveryEventSink.TestCaseDiscoveryMessageEvent += HandleTestCaseDiscoveryMessage;
discoveryEventSink.DiscoveryCompleteMessageEvent += HandleDiscoveryCompleteMessage;
discoveryEventSink.TestCaseDiscoveredEvent += HandleTestCaseDiscoveredMessage;
discoveryEventSink.DiscoveryCompleteEvent += HandleDiscoveryCompleteMessage;
}

public ManualResetEvent Finished { get; } = new ManualResetEvent(initialState: false);
Expand All @@ -65,30 +64,35 @@ public VsDiscoverySink(
public void Dispose()
{
Finished.Dispose();
discoveryEventSink.Dispose();
}

public static TestCase? CreateVsTestCase(
string source,
TestCaseDescriptor descriptor,
_TestCaseDiscovered testCase,
LoggerHelper logger,
TestPlatformContext testPlatformContext)
{
if (testCase.TestClassNameWithNamespace is null)
{
logger.LogErrorWithSource(source, "Error creating Visual Studio test case for {0}: TestClassWithNamespace is null", testCase.TestCaseDisplayName);
return null;
}

try
{
var fqTestMethodName = $"{descriptor.ClassName}.{descriptor.MethodName}";
var result = new TestCase(fqTestMethodName, uri, source) { DisplayName = Escape(descriptor.DisplayName) };
var result = new TestCase(testCase.TestClassNameWithNamespace, uri, source) { DisplayName = Escape(testCase.TestCaseDisplayName) };
result.SetPropertyValue(VsTestRunner.TestCaseUniqueIDProperty, testCase.TestCaseUniqueID);

if (testPlatformContext.RequireSerialization)
result.SetPropertyValue(VsTestRunner.SerializedTestCaseProperty, descriptor.Serialization);
if (testPlatformContext.DesignMode)
result.SetPropertyValue(VsTestRunner.TestCaseSerializationProperty, testCase.Serialization);

result.Id = GuidFromString(uri + descriptor.UniqueID);
result.CodeFilePath = descriptor.SourceFileName;
result.LineNumber = descriptor.SourceLineNumber.GetValueOrDefault();
result.Id = GuidFromString(uri + testCase.TestCaseUniqueID);
result.CodeFilePath = testCase.SourceFilePath;
result.LineNumber = testCase.SourceLineNumber.GetValueOrDefault();

if (addTraitThunk is not null)
{
var traits = descriptor.Traits;
var traits = testCase.Traits;

foreach (var key in traits.Keys)
foreach (var value in traits[key])
Expand All @@ -99,7 +103,7 @@ public void Dispose()
}
catch (Exception ex)
{
logger.LogErrorWithSource(source, "Error creating Visual Studio test case for {0}: {1}", descriptor.DisplayName, ex);
logger.LogErrorWithSource(source, "Error creating Visual Studio test case for {0}: {1}", testCase.TestCaseDisplayName, ex);
return null;
}
}
Expand Down Expand Up @@ -169,18 +173,18 @@ void HandleCancellation(MessageHandlerArgs args)
args.Stop();
}

void HandleTestCaseDiscoveryMessage(MessageHandlerArgs<ITestCaseDiscoveryMessage> args)
void HandleTestCaseDiscoveredMessage(MessageHandlerArgs<_TestCaseDiscovered> args)
{
testCaseBatch.Add(args.Message.TestCase);
testCaseBatch.Add(args.Message);
TotalTests++;

if (testCaseBatch.Count == TestCaseDescriptorBatchSize)
if (testCaseBatch.Count == TestCaseBatchSize)
SendExistingTestCases();

HandleCancellation(args);
}

void HandleDiscoveryCompleteMessage(MessageHandlerArgs<IDiscoveryCompleteMessage> args)
void HandleDiscoveryCompleteMessage(MessageHandlerArgs<_DiscoveryComplete> args)
{
try
{
Expand All @@ -196,24 +200,21 @@ void HandleDiscoveryCompleteMessage(MessageHandlerArgs<IDiscoveryCompleteMessage
HandleCancellation(args);
}

bool IMessageSinkWithTypes.OnMessageWithTypes(
IMessageSinkMessage message,
HashSet<string> messageTypes) =>
discoveryEventSink.OnMessageWithTypes(message, messageTypes);
bool _IMessageSink.OnMessage(_MessageSinkMessage message) =>
discoveryEventSink.OnMessage(message);

private void SendExistingTestCases()
{
if (testCaseBatch.Count == 0)
return;

var descriptors = descriptorProvider.GetTestCaseDescriptors(testCaseBatch, includeSerialization: testPlatformContext.RequireSerialization);
foreach (var descriptor in descriptors)
foreach (var testCase in testCaseBatch)
{
var vsTestCase = CreateVsTestCase(source, descriptor, logger, testPlatformContext);
var vsTestCase = CreateVsTestCase(source, testCase, logger, testPlatformContext);
if (vsTestCase is not null && testCaseFilter.MatchTestCase(vsTestCase))
{
if (discoveryOptions.GetInternalDiagnosticMessagesOrDefault())
logger.LogWithSource(source, "Discovered test case '{0}' (ID = '{1}', VS FQN = '{2}')", descriptor.DisplayName, descriptor.UniqueID, vsTestCase.FullyQualifiedName);
logger.LogWithSource(source, "Discovered test case '{0}' (ID = '{1}', VS FQN = '{2}')", testCase.TestCaseDisplayName, testCase.TestCaseUniqueID, vsTestCase.FullyQualifiedName);

discoverySink.SendTestCase(vsTestCase);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Xunit.Runner.Common;

namespace Xunit.Runner.VisualStudio;

Expand Down
Loading

0 comments on commit e546e93

Please sign in to comment.