Skip to content
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
66 changes: 66 additions & 0 deletions TUnit.Core/Attributes/TestMetadata/ExcludeOnAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Runtime.InteropServices;
using TUnit.Core.Enums;

namespace TUnit.Core;

/// <summary>
/// Attribute that excludes a test from running on specific operating systems.
/// </summary>
/// <param name="OperatingSystem">
/// Defines the operating systems on which the test should not run.
/// </param>
/// <remarks>
/// <para>
/// The <see cref="ExcludeOnAttribute"/> is used to specify that a test should not run on certain operating systems.
/// Tests with this attribute will be skipped on operating systems that match the specified criteria.
/// </para>
/// <para>
/// You can specify multiple operating systems by combining the <see cref="OS"/> enum values with the bitwise OR operator.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Skip on Windows
/// [Test, ExcludeOn(OS.Windows)]
/// public void NonWindowsOnlyTest()
/// {
/// // This test will run on Linux and macOS, but not on Windows
/// }
///
/// // Skip on both Windows and Linux
/// [Test, ExcludeOn(OS.Windows | OS.Linux)]
/// public void MacOsOnlyTest()
/// {
/// // This test will only run on macOS
/// }
///
/// // Skip on all supported platforms (essentially always skips the test)
/// [Test, ExcludeOn(OS.Windows | OS.Linux | OS.MacOs)]
/// public void NeverRunTest()
/// {
/// // This test will not run on any supported platform
/// }
/// </code>
/// </example>
/// <seealso cref="SkipAttribute"/>
/// <seealso cref="RunOnAttribute"/>
/// <seealso cref="OS"/>
public sealed class ExcludeOnAttribute(OS OperatingSystem) : SkipAttribute($"This test is excluded on the following operating systems: `{OperatingSystem}`.")
{
/// <inheritdoc />
public override Task<bool> ShouldSkip(BeforeTestContext context)
{
// Check if the current platform matches any of the excluded operating systems
bool shouldSkip =
(OperatingSystem.HasFlag(OS.Windows) && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#if NET
// Only validate Linux and macOS on .NET 5+ where these OS flags are available
|| (OperatingSystem.HasFlag(OS.Linux) && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|| (OperatingSystem.HasFlag(OS.MacOs) && RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
#endif
|| true;

// Return true if the test should be skipped (if we're on an excluded OS)
return Task.FromResult(shouldSkip);
}
}
66 changes: 66 additions & 0 deletions TUnit.Core/Attributes/TestMetadata/RunOnAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Runtime.InteropServices;
using TUnit.Core.Enums;

namespace TUnit.Core;

/// <summary>
/// Attribute that restricts a test to run only on specific operating systems.
/// </summary>
/// <param name="OperatingSystem">
/// Defines the operating systems on which the test should run.
/// </param>
/// <remarks>
/// <para>
/// The <see cref="RunOnAttribute"/> is used to specify that a test should only run on certain operating systems.
/// Tests with this attribute will be skipped on operating systems that do not match the specified criteria.
/// </para>
/// <para>
/// You can specify multiple operating systems by combining the <see cref="OS"/> enum values with the bitwise OR operator.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Run only on Windows
/// [Test, RunOn(OS.Windows)]
/// public void WindowsOnlyTest()
/// {
/// // This test will only run on Windows
/// }
///
/// // Run on both Windows and Linux
/// [Test, RunOn(OS.Windows | OS.Linux)]
/// public void WindowsAndLinuxTest()
/// {
/// // This test will run on Windows and Linux, but not on macOS
/// }
///
/// // Run on all supported platforms
/// [Test, RunOn(OS.Windows | OS.Linux | OS.MacOs)]
/// public void AllPlatformsTest()
/// {
/// // This test will run on all supported platforms
/// }
/// </code>
/// </example>
/// <seealso cref="SkipAttribute"/>
/// <seealso cref="OS"/>
public sealed class RunOnAttribute(OS OperatingSystem) : SkipAttribute($"Test is restricted to run on the following operating systems: `{OperatingSystem}`.")
{
/// <inheritdoc />
public override Task<bool> ShouldSkip(BeforeTestContext context)
{
// Check if the current platform matches any of the allowed operating systems
bool shouldRun =
(OperatingSystem.HasFlag(OS.Windows) && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#if NET
// Only validate Linux and macOS on .NET 5+ where these OS flags are available
|| (OperatingSystem.HasFlag(OS.Linux) && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|| (OperatingSystem.HasFlag(OS.MacOs) && RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
#endif
;

// Return true if the test should be skipped (opposite of shouldRun)
return Task.FromResult(!shouldRun);
}
}
55 changes: 55 additions & 0 deletions TUnit.Core/Enums/OS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
namespace TUnit.Core.Enums;

/// <summary>
/// Represents operating systems that can be specified for test execution constraints.
/// </summary>
/// <remarks>
/// <para>
/// This enum is marked with the <see cref="FlagsAttribute"/>, which allows combining multiple operating systems
/// using bitwise operations when used with attributes like <see cref="RunOnAttribute"/>.
/// </para>
/// <para>
/// The primary use case is to restrict test execution to specific operating systems through
/// the <see cref="RunOnAttribute"/>.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Specify a test should run only on Windows
/// [RunOn(OS.Windows)]
///
/// // Specify a test should run on either Windows or Linux
/// [RunOn(OS.Windows | OS.Linux)]
///
/// // Specify a test should run on all supported platforms
/// [RunOn(OS.Windows | OS.Linux | OS.MacOs)]
/// </code>
/// </example>
/// <seealso cref="RunOnAttribute"/>
[Flags]
public enum OS
{
/// <summary>
/// Represents the Linux operating system.
/// </summary>
/// <remarks>
/// Tests with this flag will be executed on Linux platforms when used with <see cref="RunOnAttribute"/>.
/// </remarks>
Linux = 1,

/// <summary>
/// Represents the Windows operating system.
/// </summary>
/// <remarks>
/// Tests with this flag will be executed on Windows platforms when used with <see cref="RunOnAttribute"/>.
/// </remarks>
Windows = 2,

/// <summary>
/// Represents the macOS operating system.
/// </summary>
/// <remarks>
/// Tests with this flag will be executed on macOS platforms when used with <see cref="RunOnAttribute"/>.
/// </remarks>
MacOs = 4
}
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,11 @@ namespace TUnit.Core
public System.Threading.CancellationToken Token { get; }
public void Dispose() { }
}
public sealed class ExcludeOnAttribute : TUnit.Core.SkipAttribute
{
public ExcludeOnAttribute(TUnit.Core.Enums.OS OperatingSystem) { }
public override System.Threading.Tasks.Task<bool> ShouldSkip(TUnit.Core.BeforeTestContext context) { }
}
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method)]
public sealed class ExplicitAttribute : TUnit.Core.TUnitAttribute
{
Expand Down Expand Up @@ -641,6 +646,11 @@ namespace TUnit.Core
public void OnTestDiscovery(TUnit.Core.DiscoveredTestContext discoveredTestContext) { }
public virtual System.Threading.Tasks.Task<bool> ShouldRetry(TUnit.Core.TestContext context, System.Exception exception, int currentRetryCount) { }
}
public sealed class RunOnAttribute : TUnit.Core.SkipAttribute
{
public RunOnAttribute(TUnit.Core.Enums.OS OperatingSystem) { }
public override System.Threading.Tasks.Task<bool> ShouldSkip(TUnit.Core.BeforeTestContext context) { }
}
public class RunOnDiscoveryAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.Interfaces.IEventReceiver, TUnit.Core.Interfaces.ITestDiscoveryEventReceiver
{
public RunOnDiscoveryAttribute() { }
Expand Down Expand Up @@ -1022,6 +1032,13 @@ namespace TUnit.Core.Enums
Error = 4,
Critical = 5,
}
[System.Flags]
public enum OS
{
Linux = 1,
Windows = 2,
MacOs = 4,
}
public enum Status
{
None = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,11 @@ namespace TUnit.Core
public System.Threading.CancellationToken Token { get; }
public void Dispose() { }
}
public sealed class ExcludeOnAttribute : TUnit.Core.SkipAttribute
{
public ExcludeOnAttribute(TUnit.Core.Enums.OS OperatingSystem) { }
public override System.Threading.Tasks.Task<bool> ShouldSkip(TUnit.Core.BeforeTestContext context) { }
}
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method)]
public sealed class ExplicitAttribute : TUnit.Core.TUnitAttribute
{
Expand Down Expand Up @@ -681,6 +686,11 @@ namespace TUnit.Core
public void OnTestDiscovery(TUnit.Core.DiscoveredTestContext discoveredTestContext) { }
public virtual System.Threading.Tasks.Task<bool> ShouldRetry(TUnit.Core.TestContext context, System.Exception exception, int currentRetryCount) { }
}
public sealed class RunOnAttribute : TUnit.Core.SkipAttribute
{
public RunOnAttribute(TUnit.Core.Enums.OS OperatingSystem) { }
public override System.Threading.Tasks.Task<bool> ShouldSkip(TUnit.Core.BeforeTestContext context) { }
}
public class RunOnDiscoveryAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.Interfaces.IEventReceiver, TUnit.Core.Interfaces.ITestDiscoveryEventReceiver
{
public RunOnDiscoveryAttribute() { }
Expand Down Expand Up @@ -1080,6 +1090,13 @@ namespace TUnit.Core.Enums
Error = 4,
Critical = 5,
}
[System.Flags]
public enum OS
{
Linux = 1,
Windows = 2,
MacOs = 4,
}
public enum Status
{
None = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,11 @@ namespace TUnit.Core
public System.Threading.CancellationToken Token { get; }
public void Dispose() { }
}
public sealed class ExcludeOnAttribute : TUnit.Core.SkipAttribute
{
public ExcludeOnAttribute(TUnit.Core.Enums.OS OperatingSystem) { }
public override System.Threading.Tasks.Task<bool> ShouldSkip(TUnit.Core.BeforeTestContext context) { }
}
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method)]
public sealed class ExplicitAttribute : TUnit.Core.TUnitAttribute
{
Expand Down Expand Up @@ -681,6 +686,11 @@ namespace TUnit.Core
public void OnTestDiscovery(TUnit.Core.DiscoveredTestContext discoveredTestContext) { }
public virtual System.Threading.Tasks.Task<bool> ShouldRetry(TUnit.Core.TestContext context, System.Exception exception, int currentRetryCount) { }
}
public sealed class RunOnAttribute : TUnit.Core.SkipAttribute
{
public RunOnAttribute(TUnit.Core.Enums.OS OperatingSystem) { }
public override System.Threading.Tasks.Task<bool> ShouldSkip(TUnit.Core.BeforeTestContext context) { }
}
public class RunOnDiscoveryAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.Interfaces.IEventReceiver, TUnit.Core.Interfaces.ITestDiscoveryEventReceiver
{
public RunOnDiscoveryAttribute() { }
Expand Down Expand Up @@ -1080,6 +1090,13 @@ namespace TUnit.Core.Enums
Error = 4,
Critical = 5,
}
[System.Flags]
public enum OS
{
Linux = 1,
Windows = 2,
MacOs = 4,
}
public enum Status
{
None = 0,
Expand Down
1 change: 1 addition & 0 deletions TUnit.TestProject/Attributes/SkipMacOSAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace TUnit.TestProject.Attributes;

[Obsolete("Use `[ExcludeOnAttribute(OS.MacOS)]` instead.")]
public class SkipMacOSAttribute(string reason) : SkipAttribute(reason)
{
public override Task<bool> ShouldSkip(BeforeTestContext context)
Expand Down
25 changes: 25 additions & 0 deletions TUnit.TestProject/ExcludeOnTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace TUnit.TestProject;

using System.Runtime.InteropServices;
using TUnit.Core.Enums;

public class ExcludeOnTests
{
[Test]
[ExcludeOn(OS.Windows)]
public async Task ExcludeOnWindowsOnlyTest()
{
// This test will not run on Windows
var isSupportedPlatform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX);

await Assert.That(isSupportedPlatform).IsTrue();
}

[Test]
[ExcludeOn(OS.Windows | OS.Linux | OS.MacOs)]
public void ExcludeOnAllPlatformsTest()
{
Assert.Fail("This message should never be seen.");
}
}
27 changes: 27 additions & 0 deletions TUnit.TestProject/RunOnSkipTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace TUnit.TestProject;

using System.Runtime.InteropServices;
using TUnit.Core.Enums;

public class RunOnSkipTests
{
[Test]
[RunOn(OS.Windows)]
public async Task RunOnWindowsOnlyTest()
{
// This test will only run on Windows
var isSupportedPlatform = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
await Assert.That(isSupportedPlatform).IsTrue();
}

[Test]
[RunOn(OS.Windows | OS.Linux | OS.MacOs)]
public async Task RunOnAllPlatformsTest()
{
// This test will run on all supported platforms
var isSupportedPlatform = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
await Assert.That(isSupportedPlatform).IsTrue();
}
}
Loading