Skip to content

Commit 190a8d3

Browse files
committed
implemented ConditionalOuterLoopAttribute, which conditionally sets OuterLoop category
1 parent 5faea1b commit 190a8d3

5 files changed

Lines changed: 125 additions & 51 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using Xunit.Sdk;
6+
7+
namespace Xunit
8+
{
9+
/// <summary>
10+
/// Apply this attribute to your test method to specify a outer-loop category.
11+
/// </summary>
12+
[TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.ConditionalOuterLoopTestsDiscoverer", "Microsoft.DotNet.XUnitExtensions")]
13+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
14+
public class ConditionalOuterLoopAttribute : Attribute, ITraitAttribute
15+
{
16+
public Type CalleeType { get; private set; }
17+
public string[] ConditionMemberNames { get; private set; }
18+
19+
public ConditionalOuterLoopAttribute(string reason, TestPlatforms platforms) { }
20+
public ConditionalOuterLoopAttribute(string reason, TargetFrameworkMonikers framework) { }
21+
public ConditionalOuterLoopAttribute(string reason, TestRuntimes runtimes) { }
22+
public ConditionalOuterLoopAttribute(string reason, TestPlatforms platforms = TestPlatforms.Any, TargetFrameworkMonikers framework = TargetFrameworkMonikers.Any, TestRuntimes runtimes = TestRuntimes.Any) { }
23+
public ConditionalOuterLoopAttribute(string isreasonsue, Type calleeType, params string[] conditionMemberNames)
24+
{
25+
CalleeType = calleeType;
26+
ConditionMemberNames = conditionMemberNames;
27+
}
28+
}
29+
}

src/Microsoft.DotNet.XUnitExtensions/src/DiscovererHelpers.cs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Collections.Generic;
66
using System.Diagnostics;
77
using System.Linq;
8-
using System.Reflection;
98
using System.Runtime.InteropServices;
109
using Xunit;
1110

@@ -59,5 +58,54 @@ internal static bool Evaluate(Type calleeType, string[] conditionMemberNames)
5958

6059
return true;
6160
}
61+
62+
internal static IEnumerable<KeyValuePair<string, string>> EvaluateArguments(IEnumerable<object> ctorArgs,string category, int skipFirst=1)
63+
{
64+
Debug.Assert(ctorArgs.Count() >= 2);
65+
66+
TestPlatforms platforms = TestPlatforms.Any;
67+
TargetFrameworkMonikers frameworks = TargetFrameworkMonikers.Any;
68+
TestRuntimes runtimes = TestRuntimes.Any;
69+
Type calleeType = null;
70+
string[] conditionMemberNames = null;
71+
72+
foreach (object arg in ctorArgs.Skip(skipFirst)) // First argument is the issue number or reason.
73+
{
74+
if (arg is TestPlatforms)
75+
{
76+
platforms = (TestPlatforms)arg;
77+
}
78+
else if (arg is TargetFrameworkMonikers)
79+
{
80+
frameworks = (TargetFrameworkMonikers)arg;
81+
}
82+
else if (arg is TestRuntimes)
83+
{
84+
runtimes = (TestRuntimes)arg;
85+
}
86+
else if (arg is Type)
87+
{
88+
calleeType = (Type)arg;
89+
}
90+
else if (arg is string[])
91+
{
92+
conditionMemberNames = (string[])arg;
93+
}
94+
}
95+
96+
if (calleeType != null && conditionMemberNames != null)
97+
{
98+
if (DiscovererHelpers.Evaluate(calleeType, conditionMemberNames))
99+
{
100+
yield return new KeyValuePair<string, string>(XunitConstants.Category, category);
101+
}
102+
}
103+
else if (DiscovererHelpers.TestPlatformApplies(platforms) &&
104+
DiscovererHelpers.TestRuntimeApplies(runtimes) &&
105+
DiscovererHelpers.TestFrameworkApplies(frameworks))
106+
{
107+
yield return new KeyValuePair<string, string>(XunitConstants.Category, category);
108+
}
109+
}
62110
}
63111
}
Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
54
using System.Collections.Generic;
6-
using System.Diagnostics;
7-
using System.Linq;
8-
using Xunit;
95
using Xunit.Abstractions;
106
using Xunit.Sdk;
117

@@ -25,52 +21,7 @@ public class ActiveIssueDiscoverer : ITraitDiscoverer
2521
public IEnumerable<KeyValuePair<string, string>> GetTraits(IAttributeInfo traitAttribute)
2622
{
2723
IEnumerable<object> ctorArgs = traitAttribute.GetConstructorArguments();
28-
Debug.Assert(ctorArgs.Count() >= 2);
29-
30-
string issue = ctorArgs.First().ToString();
31-
TestPlatforms platforms = TestPlatforms.Any;
32-
TargetFrameworkMonikers frameworks = TargetFrameworkMonikers.Any;
33-
TestRuntimes runtimes = TestRuntimes.Any;
34-
Type calleeType = null;
35-
string[] conditionMemberNames = null;
36-
37-
foreach (object arg in ctorArgs.Skip(1)) // First argument is the issue number.
38-
{
39-
if (arg is TestPlatforms)
40-
{
41-
platforms = (TestPlatforms)arg;
42-
}
43-
else if (arg is TargetFrameworkMonikers)
44-
{
45-
frameworks = (TargetFrameworkMonikers)arg;
46-
}
47-
else if (arg is TestRuntimes)
48-
{
49-
runtimes = (TestRuntimes)arg;
50-
}
51-
else if (arg is Type)
52-
{
53-
calleeType = (Type)arg;
54-
}
55-
else if (arg is string[])
56-
{
57-
conditionMemberNames = (string[])arg;
58-
}
59-
}
60-
61-
if (calleeType != null && conditionMemberNames != null)
62-
{
63-
if (DiscovererHelpers.Evaluate(calleeType, conditionMemberNames))
64-
{
65-
yield return new KeyValuePair<string, string>(XunitConstants.Category, XunitConstants.Failing);
66-
}
67-
}
68-
else if (DiscovererHelpers.TestPlatformApplies(platforms) &&
69-
DiscovererHelpers.TestRuntimeApplies(runtimes) &&
70-
DiscovererHelpers.TestFrameworkApplies(frameworks))
71-
{
72-
yield return new KeyValuePair<string, string>(XunitConstants.Category, XunitConstants.Failing);
73-
}
24+
return DiscovererHelpers.EvaluateArguments(ctorArgs, XunitConstants.Failing);
7425
}
7526
}
7627
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using Xunit.Abstractions;
6+
using Xunit.Sdk;
7+
using System.Linq;
8+
9+
namespace Microsoft.DotNet.XUnitExtensions
10+
{
11+
/// <summary>
12+
/// This class discovers all of the tests and test classes that have
13+
/// applied the OuterLoop attribute
14+
/// </summary>
15+
public class ConditionalOuterLoopTestsDiscoverer : ITraitDiscoverer
16+
{
17+
/// <summary>
18+
/// Gets the trait values from the Category attribute.
19+
/// </summary>
20+
/// <param name="traitAttribute">The trait attribute containing the trait values.</param>
21+
/// <returns>The trait values.</returns>
22+
public IEnumerable<KeyValuePair<string, string>> GetTraits(IAttributeInfo traitAttribute)
23+
{
24+
IEnumerable<object> ctorArgs = traitAttribute.GetConstructorArguments();
25+
if (ctorArgs.Count() <= 2)
26+
{
27+
return new[] { new KeyValuePair<string, string>(XunitConstants.Category, XunitConstants.OuterLoop) };
28+
}
29+
return DiscovererHelpers.EvaluateArguments(ctorArgs, XunitConstants.OuterLoop);
30+
}
31+
}
32+
}

src/Microsoft.DotNet.XUnitExtensions/tests/ConditionalAttributeTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class ConditionalAttributeTests
1313
// This test class is test order dependent so do not rename the tests.
1414
// If new tests need to be added, follow the same naming pattern ConditionalAttribute{LetterToOrderTest} and then add a Validate{TestName}.
1515

16+
private static bool s_conditionalOuterLoop;
1617
private static bool s_conditionalFactExecuted;
1718
private static int s_conditionalTheoryCount;
1819

@@ -24,6 +25,13 @@ public void ConditionalAttributeA()
2425
s_conditionalFactExecuted = true;
2526
}
2627

28+
[Fact]
29+
[ConditionalOuterLoop("never outer loop", TestPlatforms.Any & ~TestPlatforms.Any)]
30+
public void ConditionalOuterLoopAttribute()
31+
{
32+
s_conditionalOuterLoop = true;
33+
}
34+
2735
[ConditionalTheory(nameof(AlwaysTrue))]
2836
[InlineData(1)]
2937
[InlineData(2)]
@@ -41,6 +49,12 @@ public void ValidateConditionalFact()
4149
Assert.True(s_conditionalFactExecuted);
4250
}
4351

52+
[Fact]
53+
public void ValidateConditionalOuterLoop()
54+
{
55+
Assert.True(s_conditionalOuterLoop);
56+
}
57+
4458
[Fact]
4559
public void ValidateConditionalTheory()
4660
{

0 commit comments

Comments
 (0)