Skip to content
Open
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
1 change: 1 addition & 0 deletions Arcade.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<Project Path="src/Microsoft.DotNet.SourceBuild/tests/Microsoft.DotNet.SourceBuild.Tasks.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.XliffTasks.Tests/Microsoft.DotNet.XliffTasks.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.XUnitExtensions/tests/Microsoft.DotNet.XUnitExtensions.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.XUnitV3Extensions/tests/Microsoft.DotNet.XUnitV3Extensions.AlwaysFalseConditionalAssembly.Tests.csproj" />
Comment thread
AndriySvyryd marked this conversation as resolved.
<Project Path="src/Microsoft.DotNet.XUnitV3Extensions/tests/Microsoft.DotNet.XUnitV3Extensions.Tests.csproj" />
<Project Path="src/VersionTools/Microsoft.DotNet.VersionTools.Cli.Tests/Microsoft.DotNet.VersionTools.Cli.Tests.csproj" />
</Folder>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.Diagnostics.CodeAnalysis;
using Microsoft.DotNet.XUnitExtensions;
using Xunit.Sdk;

namespace Xunit
{
/// <summary>
/// An assembly-level attribute that conditionally marks all tests in the assembly to be skipped
/// based on the evaluation of one or more static boolean members. When any of the referenced
/// condition members evaluates to <c>false</c>, the attribute contributes a <c>category=failing</c>
/// trait so that the test runner can exclude the affected tests.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ConditionalAssemblyAttribute : Attribute, ITraitAttribute
{
[DynamicallyAccessedMembers(StaticReflectionConstants.ConditionalMemberKinds)]
public Type CalleeType { get; private set; }
public string[] ConditionMemberNames { get; private set; }

public ConditionalAssemblyAttribute(
[DynamicallyAccessedMembers(StaticReflectionConstants.ConditionalMemberKinds)]
Type calleeType,
params string[] conditionMemberNames)
{
CalleeType = calleeType;
ConditionMemberNames = conditionMemberNames;
}

public IReadOnlyCollection<KeyValuePair<string, string>> GetTraits()
{
// If evaluated to false, skip all tests in the assembly.
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Xunit;

// The assembly-level ConditionalAssembly attribute below references a condition member
// that always returns false. As a result, every test in this assembly is tagged with the
// "category=failing" trait, and the test runner is configured (via the project's
// TestRunnerAdditionalArguments) to skip tests with that trait. If the attribute were
// ever broken and stopped contributing the trait, the deliberately failing test below
// would run and fail the build, catching the regression.
[assembly: ConditionalAssembly(typeof(Microsoft.DotNet.XUnitV3Extensions.AlwaysFalseConditionalAssemblyTests.Conditions),
nameof(Microsoft.DotNet.XUnitV3Extensions.AlwaysFalseConditionalAssemblyTests.Conditions.AlwaysFalse))]

namespace Microsoft.DotNet.XUnitV3Extensions.AlwaysFalseConditionalAssemblyTests
{
public static class Conditions
{
public static bool AlwaysFalse => false;
}

public class FailingTests
{
[Fact]
public void AlwaysFails()
{
Assert.Fail("This test is expected to be skipped via [assembly: ConditionalAssembly].");
}
}
}
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.

using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit;

Expand Down Expand Up @@ -86,6 +88,61 @@ public void ValidateConditionalTheoryTrueReceivedArgs()
Assert.NotNull(GetConditionalTheoryAttribute(nameof(ConditionalTheoryTrue)));
}

[Fact]
public void ConditionalAssemblyAttribute_MultipleConditions_AllTrue_ReturnsNoTraits()
{
ConditionalAssemblyAttribute attribute = new ConditionalAssemblyAttribute(
typeof(ConditionalAttributeTests),
nameof(AlwaysTrue),
nameof(AlwaysTrue));

Assert.Empty(attribute.GetTraits());
}

[Fact]
public void ConditionalAssemblyAttribute_MultipleConditions_OneFalse_ReturnsFailingCategoryTrait()
{
ConditionalAssemblyAttribute attribute = new ConditionalAssemblyAttribute(
typeof(ConditionalAttributeTests),
nameof(AlwaysTrue),
nameof(AlwaysFalse));

KeyValuePair<string, string> trait = Assert.Single(attribute.GetTraits());
Assert.Equal(XunitConstants.Category, trait.Key);
Assert.Equal("failing", trait.Value);
}

[Fact]
public void ConditionalAssemblyAttribute_NoConditionMembers_ReturnsNoTraits()
{
// With no condition names supplied, the attribute is treated as "no conditions" and tests run normally.
ConditionalAssemblyAttribute attribute = new ConditionalAssemblyAttribute(typeof(ConditionalAttributeTests));

Assert.Empty(attribute.GetTraits());
}

[Fact]
public void ConditionalAssemblyAttribute_MissingMember_Throws()
{
ConditionalAssemblyAttribute attribute = new ConditionalAssemblyAttribute(
typeof(ConditionalAttributeTests),
"MemberThatDoesNotExist");

Assert.Throws<InvalidOperationException>(() => attribute.GetTraits());
}

[Fact]
public void ConditionalAssemblyAttribute_StoresConstructorArguments()
{
ConditionalAssemblyAttribute attribute = new ConditionalAssemblyAttribute(
typeof(ConditionalAttributeTests),
nameof(AlwaysTrue),
nameof(AlwaysFalse));

Assert.Equal(typeof(ConditionalAttributeTests), attribute.CalleeType);
Assert.Equal(new[] { nameof(AlwaysTrue), nameof(AlwaysFalse) }, attribute.ConditionMemberNames);
}

private static ConditionalFactAttribute GetConditionalFactAttribute(string methodName)
{
return (ConditionalFactAttribute)typeof(ConditionalAttributeTests)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(BundledNETCoreAppTargetFramework)</TargetFramework>
<TestRunnerName>XUnitV3</TestRunnerName>
<OutputType>Exe</OutputType>
<!--
The assembly-level [ConditionalAssembly] in this project always evaluates to false,
which tags every test in the assembly with the 'category=failing' trait. Filter out
tests with that trait so the deliberately failing test is skipped, which validates
that ConditionalAssemblyAttribute is applied to all tests in the assembly. After the
filter, no tests run, so ignore the MTP "zero tests" exit code (8).

Keep the argument string in one property and surface it through both
TestRunnerAdditionalArguments and Arguments so it applies to direct execution and
to Helix XUnitV3 work item creation paths that only propagate Arguments.
-->
<ConditionalAssemblyFilterArguments>--filter-not-trait "category=failing" --ignore-exit-code 8</ConditionalAssemblyFilterArguments>
<TestRunnerAdditionalArguments>$(ConditionalAssemblyFilterArguments)</TestRunnerAdditionalArguments>
<Arguments>$(ConditionalAssemblyFilterArguments)</Arguments>
<!--
This project lives alongside Microsoft.DotNet.XUnitV3Extensions.Tests.csproj in the
same folder. Restrict default compile globbing to just this project's source so the
assembly-level attribute and failing test don't bleed into the sibling project.
-->
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>

<ItemGroup>
<Compile Include="AlwaysFalseConditionalAssemblyTests.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\Microsoft.DotNet.XUnitV3Extensions.csproj" />
</ItemGroup>

</Project>

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<!-- Belongs to the sibling Microsoft.DotNet.XUnitV3Extensions.AlwaysFalseConditionalAssembly.Tests project. -->
<Compile Remove="AlwaysFalseConditionalAssemblyTests.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\Microsoft.DotNet.XUnitV3Extensions.csproj" />
</ItemGroup>
Expand Down
11 changes: 11 additions & 0 deletions tests/UnitTests.proj
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,21 @@

<!-- XUnit v3 tests are self-hosting executables and use XUnitV3Project instead -->
<XUnitProject Remove="..\src\Microsoft.DotNet.XUnitV3Extensions\tests\Microsoft.DotNet.XUnitV3Extensions.Tests.csproj"/>
<XUnitProject Remove="..\src\Microsoft.DotNet.XUnitV3Extensions\tests\Microsoft.DotNet.XUnitV3Extensions.AlwaysFalseConditionalAssembly.Tests.csproj"/>
</ItemGroup>

<ItemGroup>
<XUnitV3Project Include="..\src\Microsoft.DotNet.XUnitV3Extensions\tests\Microsoft.DotNet.XUnitV3Extensions.Tests.csproj"/>
<!--
The assembly-level [ConditionalAssembly] in this project always evaluates to false,
tagging every test with the 'category=failing' trait. Filter that trait out so the
deliberately failing test is skipped on Helix; ignore the MTP "zero tests ran" exit
code (8) since no tests run after the filter. This validates that the attribute is
actually applied to all tests in the assembly.
-->
<XUnitV3Project Include="..\src\Microsoft.DotNet.XUnitV3Extensions\tests\Microsoft.DotNet.XUnitV3Extensions.AlwaysFalseConditionalAssembly.Tests.csproj">
<Arguments>--filter-not-trait "category=failing" --ignore-exit-code 8</Arguments>
</XUnitV3Project>
</ItemGroup>

<ItemGroup>
Expand Down
Loading