Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support xunit.v3 in Microsoft.DotNet.XUnitExtensions #15668

Merged
merged 7 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Arcade.sln
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MacOsPkg.T
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.StrongName", "src\Microsoft.DotNet.StrongName\Microsoft.DotNet.StrongName.csproj", "{B65809A4-C6DB-4994-BBDE-D5B8396958A6}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.DotNet.XUnitExtensions.Shared", "src\Microsoft.DotNet.XUnitExtensions.Shared\Microsoft.DotNet.XUnitExtensions.Shared.shproj", "{3A0BC9CF-F9BF-4961-8615-1919C99FDD27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.XUnitV3Extensions", "src\Microsoft.DotNet.XUnitV3Extensions\src\Microsoft.DotNet.XUnitV3Extensions.csproj", "{B6625157-A06A-431F-8424-7CC41EE7038E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1015,6 +1019,18 @@ Global
{B65809A4-C6DB-4994-BBDE-D5B8396958A6}.Release|x64.Build.0 = Release|Any CPU
{B65809A4-C6DB-4994-BBDE-D5B8396958A6}.Release|x86.ActiveCfg = Release|Any CPU
{B65809A4-C6DB-4994-BBDE-D5B8396958A6}.Release|x86.Build.0 = Release|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Debug|x64.ActiveCfg = Debug|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Debug|x64.Build.0 = Debug|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Debug|x86.ActiveCfg = Debug|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Debug|x86.Build.0 = Debug|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Release|Any CPU.Build.0 = Release|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Release|x64.ActiveCfg = Release|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Release|x64.Build.0 = Release|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Release|x86.ActiveCfg = Release|Any CPU
{B6625157-A06A-431F-8424-7CC41EE7038E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1053,4 +1069,9 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {32B9C883-432E-4FC8-A1BF-090EB033DD5B}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Microsoft.DotNet.XUnitExtensions.Shared\Microsoft.DotNet.XUnitExtensions.Shared.projitems*{3a0bc9cf-f9bf-4961-8615-1919c99fdd27}*SharedItemsImports = 13
src\Microsoft.DotNet.XUnitExtensions.Shared\Microsoft.DotNet.XUnitExtensions.Shared.projitems*{b6625157-a06a-431f-8424-7cc41ee7038e}*SharedItemsImports = 5
src\Microsoft.DotNet.XUnitExtensions.Shared\Microsoft.DotNet.XUnitExtensions.Shared.projitems*{f8e7fec3-b9e0-4a08-8608-f48379204f3a}*SharedItemsImports = 5
EndGlobalSection
EndGlobal
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<!-- Overwrite XUnitVersion/XUnitAnalyzersVersion/XUnitRunnerConsoleVersion/XUnitRunnerVisualStudioVersion that comes from the Arcade SDK to be in sync as Arcade doesn't use a live Arcade SDK.
Keep in sync with DefaultVersions.props. -->
<XUnitVersion>2.9.2</XUnitVersion>
<XUnitV3Version>2.0.0</XUnitV3Version>
<XUnitAnalyzersVersion>1.16.0</XUnitAnalyzersVersion>
<XUnitRunnerConsoleVersion>$(XUnitVersion)</XUnitRunnerConsoleVersion>
<XUnitRunnerVisualStudioVersion>3.0.2</XUnitRunnerVisualStudioVersion>
Expand Down Expand Up @@ -103,6 +104,7 @@
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
<PackageVersion Include="xunit.extensibility.core" Version="$(XUnitVersion)" />
<PackageVersion Include="xunit.extensibility.execution" Version="$(XUnitVersion)" />
<PackageVersion Include="xunit.v3.extensibility.core" Version="$(XUnitV3Version)" />
<PackageVersion Include="xunit.runner.reporters" Version="$(XUnitVersion)" />
<PackageVersion Include="nsubstitute" Version="5.1.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,71 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.XUnitExtensions;
using Xunit.Sdk;

namespace Xunit
{
/// <summary>
/// Apply this attribute to your test method to specify an active issue.
/// </summary>
#if !USES_XUNIT_3
[TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.ActiveIssueDiscoverer", "Microsoft.DotNet.XUnitExtensions")]
#endif
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)]
public class ActiveIssueAttribute : Attribute, ITraitAttribute
{
#if USES_XUNIT_3
private readonly IEnumerable<object> _ctorArgs;
#endif

public Type CalleeType { get; private set; }
public string[] ConditionMemberNames { get; private set; }

public ActiveIssueAttribute(string issue, TestPlatforms platforms) { }
public ActiveIssueAttribute(string issue, TargetFrameworkMonikers framework) { }
public ActiveIssueAttribute(string issue, TestRuntimes runtimes) { }
public ActiveIssueAttribute(string issue, TestPlatforms platforms = TestPlatforms.Any, TargetFrameworkMonikers framework = TargetFrameworkMonikers.Any, TestRuntimes runtimes = TestRuntimes.Any) { }
public ActiveIssueAttribute(string issue, TestPlatforms platforms)
{
#if USES_XUNIT_3
_ctorArgs = [issue, platforms];
#endif
}

public ActiveIssueAttribute(string issue, TargetFrameworkMonikers framework)
{
#if USES_XUNIT_3
_ctorArgs = [issue, framework];
#endif
}

public ActiveIssueAttribute(string issue, TestRuntimes runtimes)
{
#if USES_XUNIT_3
_ctorArgs = [issue, runtimes];
#endif
}

public ActiveIssueAttribute(string issue, TestPlatforms platforms = TestPlatforms.Any, TargetFrameworkMonikers framework = TargetFrameworkMonikers.Any, TestRuntimes runtimes = TestRuntimes.Any)
{
#if USES_XUNIT_3
_ctorArgs = [issue, platforms, framework, runtimes];
#endif
}

public ActiveIssueAttribute(string issue, Type calleeType, params string[] conditionMemberNames)
{
#if USES_XUNIT_3
_ctorArgs = [issue, calleeType, conditionMemberNames];
#endif
CalleeType = calleeType;
ConditionMemberNames = conditionMemberNames;
}

#if USES_XUNIT_3
public IReadOnlyCollection<KeyValuePair<string, string>> GetTraits()
{
return DiscovererHelpers.EvaluateArguments(_ctorArgs, XunitConstants.Failing).ToArray();
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.DotNet.XUnitExtensions;
using Xunit.Sdk;

namespace Xunit
{
#if !USES_XUNIT_3
[TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.ConditionalClassDiscoverer", "Microsoft.DotNet.XUnitExtensions")]
#endif
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class ConditionalClassAttribute : Attribute, ITraitAttribute
{
Expand All @@ -23,5 +29,31 @@ public ConditionalClassAttribute(
CalleeType = calleeType;
ConditionMemberNames = conditionMemberNames;
}

#if USES_XUNIT_3
public IReadOnlyCollection<KeyValuePair<string, string>> GetTraits()
{
// If evaluated to false, skip the test class entirely.
if (!EvaluateParameterHelper())
{
return [new KeyValuePair<string, string>(XunitConstants.Category, XunitConstants.Failing)];
}

return [];
}

internal bool EvaluateParameterHelper()
{
Type calleeType = null;
string[] conditionMemberNames = null;

if (ConditionalTestDiscoverer.CheckInputToSkipExecution([CalleeType, ConditionMemberNames], ref calleeType, ref conditionMemberNames))
{
return true;
}

return DiscovererHelpers.Evaluate(calleeType, conditionMemberNames);
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.


// Not adding the support for xunit.v3.
// Still keeping the logic inside compatible with xunit.v3 in case we decided to add it.
// For cases that used to do [ConditionalFact] in xunit.v2, they can now call Assert.Skip instead of throwing SkipTestException
// In this case, [Fact] will just work because Assert.Skip is natively supported in xunit.v3
// TODO: Evaluate whether or not we want to still expose this attribute in xunit.v3 for usages of CalleeType and ConditionMemberNames?
#if !USES_XUNIT_3

using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.DotNet.XUnitExtensions;
using Xunit.Sdk;

namespace Xunit
{
#if USES_XUNIT_3
[XunitTestCaseDiscoverer(typeof(ConditionalFactDiscoverer))]
#else
[XunitTestCaseDiscoverer("Microsoft.DotNet.XUnitExtensions.ConditionalFactDiscoverer", "Microsoft.DotNet.XUnitExtensions")]
#endif
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ConditionalFactAttribute : FactAttribute
{
Expand All @@ -30,3 +43,4 @@ public ConditionalFactAttribute(params string[] conditionMemberNames)
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Not adding the support for xunit.v3.
// Still keeping the logic inside compatible with xunit.v3 in case we decided to add it.
// For cases that used to do [ConditionalTheory] in xunit.v2, they can now call Assert.Skip instead of throwing SkipTestException
// In this case, [Fact] will just work because Assert.Skip is natively supported in xunit.v3
// TODO: Evaluate whether or not we want to still expose this attribute in xunit.v3 for usages of CalleeType and ConditionMemberNames?
#if !USES_XUNIT_3

using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.DotNet.XUnitExtensions;
using Xunit.Sdk;

namespace Xunit
{
#if USES_XUNIT_3
[XunitTestCaseDiscoverer(typeof(ConditionalTheoryDiscoverer))]
#else
[XunitTestCaseDiscoverer("Microsoft.DotNet.XUnitExtensions.ConditionalTheoryDiscoverer", "Microsoft.DotNet.XUnitExtensions")]
#endif
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ConditionalTheoryAttribute : TheoryAttribute
{
Expand All @@ -30,3 +42,4 @@ public ConditionalTheoryAttribute(params string[] conditionMemberNames)
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.XUnitExtensions;
using Xunit.Sdk;

namespace Xunit
{
/// <summary>
/// Apply this attribute to your test method to specify a outer-loop category.
/// </summary>
#if !USES_XUNIT_3
[TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.OuterLoopTestsDiscoverer", "Microsoft.DotNet.XUnitExtensions")]
#endif
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public class OuterLoopAttribute : Attribute, ITraitAttribute
{
#if USES_XUNIT_3
private readonly object[] _ctorArgs;
#endif

public Type CalleeType { get; private set; }
public string[] ConditionMemberNames { get; private set; }

public OuterLoopAttribute()
{
#if USES_XUNIT_3
_ctorArgs = [];
#endif
}

public OuterLoopAttribute(string reason)
{
#if USES_XUNIT_3
_ctorArgs = [reason];
#endif
}

public OuterLoopAttribute(string reason, TestPlatforms platforms)
{
#if USES_XUNIT_3
_ctorArgs = [reason, platforms];
#endif
}

public OuterLoopAttribute(string reason, TargetFrameworkMonikers framework)
{
#if USES_XUNIT_3
_ctorArgs = [reason, framework];
#endif
}

public OuterLoopAttribute(string reason, TestRuntimes runtimes)
{
#if USES_XUNIT_3
_ctorArgs = [reason, runtimes];
#endif
}

public OuterLoopAttribute(string reason, TestPlatforms platforms = TestPlatforms.Any, TargetFrameworkMonikers framework = TargetFrameworkMonikers.Any, TestRuntimes runtimes = TestRuntimes.Any)
{
#if USES_XUNIT_3
_ctorArgs = [reason, platforms, framework, runtimes];
#endif
}

public OuterLoopAttribute(string reason, Type calleeType, params string[] conditionMemberNames)
{
#if USES_XUNIT_3
_ctorArgs = [reason, calleeType, conditionMemberNames];
#endif
CalleeType = calleeType;
ConditionMemberNames = conditionMemberNames;
}

#if USES_XUNIT_3
public IReadOnlyCollection<KeyValuePair<string, string>> GetTraits()
{
if (_ctorArgs.Length < 2)
{
return new[] { new KeyValuePair<string, string>(XunitConstants.Category, XunitConstants.OuterLoop) };
}

return DiscovererHelpers.EvaluateArguments(_ctorArgs, XunitConstants.OuterLoop).ToArray();
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// TODO: Not yet supported for xunit.v3
#if !USES_XUNIT_3
using System;
using System.Collections.Generic;
using System.Text;
Expand All @@ -9,9 +11,14 @@

namespace Microsoft.DotNet.XUnitExtensions.Attributes
{
#if USES_XUNIT_3
[XunitTestCaseDiscoverer(typeof(ParallelTheoryDiscoverer))]
#else
[XunitTestCaseDiscoverer("Microsoft.DotNet.XUnitExtensions.ParallelTheoryDiscoverer", "Microsoft.DotNet.XUnitExtensions")]
#endif
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ParallelTheoryAttribute : TheoryAttribute
{
}
}
#endif
Loading
Loading