Skip to content

Commit 34c92ae

Browse files
committed
Add skipping tests based on the [SupportedOSPlatform] attribute
Adding support for `SupportedOSPlatform` is a great because this attributes suppresses the CA1416 code analysis warning. This feature was requested in xunit/xunit#2820 but was not implemented in xUnit.net
1 parent 8bc417a commit 34c92ae

File tree

5 files changed

+120
-1
lines changed

5 files changed

+120
-1
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,23 @@ public void TestFunctionalityWhichIsNotSupportedOnSomePlatforms()
3838
}
3939
```
4040

41+
### The [SupportedOSPlatform] attribute
42+
43+
Since version 1.5, `Xunit.SkippableFact` understands the `SupportedOSPlatform` attribute to skip tests on unsupported platforms.
44+
45+
```csharp
46+
[SkippableFact, SupportedOSPlatform("Windows")]
47+
public void TestCngKey()
48+
{
49+
var key = CngKey.Create(CngAlgorithm.Sha256);
50+
Assert.NotNull(key);
51+
}
52+
```
53+
54+
Without `[SupportedOSPlatform("Windows")` the [CA1416](CA1416) code analysis warning would trigger:
55+
> This call site is reachable on all platforms. 'CngKey. Create(CngAlgorithm)' is only supported on: 'windows'.
56+
57+
Adding `[SupportedOSPlatform("Windows")` both suppresses this platform compatibility warning and skips the test when running on Linux or macOS.
58+
4159
[NuPkg]: https://www.nuget.org/packages/Xunit.SkippableFact
60+
[CA1416]: https://learn.microsoft.com/en-gb/dotnet/fundamentals/code-analysis/quality-rules/ca1416

src/Xunit.SkippableFact/Sdk/SkippableFactTestCase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,10 @@ public override void Deserialize(IXunitSerializationInfo data)
8888
base.Deserialize(data);
8989
this.SkippingExceptionNames = data.GetValue<string[]>(nameof(this.SkippingExceptionNames));
9090
}
91+
92+
/// <inheritdoc/>
93+
protected override string GetSkipReason(IAttributeInfo factAttribute)
94+
{
95+
return this.TestMethod.GetPlatformSkipReason() ?? base.GetSkipReason(factAttribute);
96+
}
9197
}

src/Xunit.SkippableFact/Sdk/SkippableTheoryTestCase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,10 @@ public override void Deserialize(IXunitSerializationInfo data)
8686
base.Deserialize(data);
8787
this.SkippingExceptionNames = data.GetValue<string[]>(nameof(this.SkippingExceptionNames));
8888
}
89+
90+
/// <inheritdoc/>
91+
protected override string GetSkipReason(IAttributeInfo factAttribute)
92+
{
93+
return this.TestMethod.GetPlatformSkipReason() ?? base.GetSkipReason(factAttribute);
94+
}
8995
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Andrew Arnott. All rights reserved.
2+
// Licensed under the Microsoft Public License (Ms-PL). See LICENSE.txt file in the project root for full license information.
3+
4+
using System.Runtime.InteropServices;
5+
using Xunit.Abstractions;
6+
7+
namespace Xunit.Sdk;
8+
9+
/// <summary>
10+
/// Extensions methods on <see cref="ITestMethod"/>.
11+
/// </summary>
12+
internal static class TestMethodExtensions
13+
{
14+
/// <summary>
15+
/// Assesses whether the test method can run on the current platform by looking at the <c>[SupportedOSPlatform]</c> attributes on the test method and on the test class.
16+
/// </summary>
17+
/// <param name="testMethod">The <see cref="ITestMethod"/>.</param>
18+
/// <returns>A description of the supported platforms if the test can not run on the current platform or <see langword="null"/> if the test can run on the current platform.</returns>
19+
public static string? GetPlatformSkipReason(this ITestMethod testMethod)
20+
{
21+
#if NET462
22+
return null;
23+
#else
24+
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
25+
AddPlatforms(platforms, testMethod.Method.GetCustomAttributes("System.Runtime.Versioning.SupportedOSPlatformAttribute"));
26+
AddPlatforms(platforms, testMethod.Method.Type.GetCustomAttributes("System.Runtime.Versioning.SupportedOSPlatformAttribute"));
27+
28+
if (platforms.Count == 0 || platforms.Any(platform => RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform))))
29+
{
30+
return null;
31+
}
32+
33+
string platformsDescription = platforms.Count == 1 ? platforms.First() : string.Join(", ", platforms.Reverse().Skip(1).Reverse()) + " and " + platforms.Last();
34+
return $"Only supported on {platformsDescription}";
35+
#endif
36+
}
37+
38+
#if !NET462
39+
private static void AddPlatforms(HashSet<string> platforms, IEnumerable<IAttributeInfo> supportedPlatformAttributes)
40+
{
41+
foreach (IAttributeInfo supportedPlatformAttribute in supportedPlatformAttributes)
42+
{
43+
platforms.Add(supportedPlatformAttribute.GetNamedArgument<string>("PlatformName"));
44+
}
45+
}
46+
#endif
47+
}

test/Xunit.SkippableFact.Tests/SampleTests.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Andrew Arnott. All rights reserved.
22
// Licensed under the Microsoft Public License (Ms-PL). See LICENSE.txt file in the project root for full license information.
33

4-
using System;
4+
using System.Runtime.Versioning;
55

66
namespace Xunit.SkippableFact.Tests;
77

@@ -76,4 +76,45 @@ public void SkipInsideAssertThrows()
7676
throw new Exception();
7777
}));
7878
}
79+
80+
#if NET5_0_OR_GREATER
81+
[SkippableFact, SupportedOSPlatform("Linux")]
82+
public void LinuxOnly()
83+
{
84+
Assert.True(OperatingSystem.IsLinux(), "This should only run on Linux");
85+
}
86+
87+
[SkippableFact, SupportedOSPlatform("macOS")]
88+
public void MacOsOnly()
89+
{
90+
Assert.True(OperatingSystem.IsMacOS(), "This should only run on macOS");
91+
}
92+
93+
[SkippableFact, SupportedOSPlatform("Windows")]
94+
public void WindowsOnly()
95+
{
96+
Assert.True(OperatingSystem.IsWindows(), "This should only run on Windows");
97+
}
98+
99+
[SkippableFact, SupportedOSPlatform("Android"), SupportedOSPlatform("Browser")]
100+
public void AndroidAndBrowserFact()
101+
{
102+
Assert.True(OperatingSystem.IsAndroid() || OperatingSystem.IsBrowser(), "This should only run on Android and Browser");
103+
}
104+
105+
[SkippableTheory, SupportedOSPlatform("Android"), SupportedOSPlatform("Browser")]
106+
[InlineData(1)]
107+
[InlineData(2)]
108+
public void AndroidAndBrowserTheory(int number)
109+
{
110+
_ = number;
111+
Assert.True(OperatingSystem.IsAndroid() || OperatingSystem.IsBrowser(), "This should only run on Android and Browser");
112+
}
113+
114+
[SkippableFact, SupportedOSPlatform("Android"), SupportedOSPlatform("Browser"), SupportedOSPlatform("Wasi")]
115+
public void AndroidAndBrowserAndWasiOnly()
116+
{
117+
Assert.True(OperatingSystem.IsAndroid() || OperatingSystem.IsBrowser() || OperatingSystem.IsWasi(), "This should only run on Android, Browser and Wasi");
118+
}
119+
#endif
79120
}

0 commit comments

Comments
 (0)