diff --git a/FFMpegCore.Test/AudioTest.cs b/FFMpegCore.Test/AudioTest.cs
index aae8c3a..5da9b08 100644
--- a/FFMpegCore.Test/AudioTest.cs
+++ b/FFMpegCore.Test/AudioTest.cs
@@ -11,6 +11,7 @@
using System.Runtime.Versioning;
using System.Threading.Tasks;
using FFMpegCore.Extensions.System.Drawing.Common;
+using FFMpegCore.Test.Utilities;
namespace FFMpegCore.Test
{
@@ -66,8 +67,8 @@ public void Audio_Add()
Assert.IsTrue(File.Exists(outputFile));
}
- [TestMethod]
- [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public void Image_AddAudio()
{
using var outputFile = new TemporaryFile("out.mp4");
diff --git a/FFMpegCore.Test/Utilities/IgnoreIfAttribute.cs b/FFMpegCore.Test/Utilities/IgnoreIfAttribute.cs
new file mode 100644
index 0000000..526a76c
--- /dev/null
+++ b/FFMpegCore.Test/Utilities/IgnoreIfAttribute.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Reflection;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace FFMpegCore.Test.Utilities;
+
+// https://matt.kotsenas.com/posts/ignoreif-mstest
+///
+/// An extension to the [Ignore] attribute. Instead of using test lists / test categories to conditionally
+/// skip tests, allow a [TestClass] or [TestMethod] to specify a method to run. If the method returns
+/// `true` the test method will be skipped. The "ignore criteria" method must be `static`, return a single
+/// `bool` value, and not accept any parameters. By default, it is named "IgnoreIf".
+///
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
+public class IgnoreIfAttribute : Attribute
+{
+ public string IgnoreCriteriaMethodName { get; set; }
+
+ public IgnoreIfAttribute(string ignoreCriteriaMethodName = "IgnoreIf")
+ {
+ IgnoreCriteriaMethodName = ignoreCriteriaMethodName;
+ }
+
+ internal bool ShouldIgnore(ITestMethod testMethod)
+ {
+ try
+ {
+ // Search for the method specified by name in this class or any parent classes.
+ var searchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Static;
+ var method = testMethod.MethodInfo.DeclaringType!.GetMethod(IgnoreCriteriaMethodName, searchFlags);
+ return (bool) method?.Invoke(null, null)!;
+ }
+ catch (Exception e)
+ {
+ var message = $"Conditional ignore method {IgnoreCriteriaMethodName} not found. Ensure the method is in the same class as the test method, marked as `static`, returns a `bool`, and doesn't accept any parameters.";
+ throw new ArgumentException(message, e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FFMpegCore.Test/Utilities/OperatingSystemUtils.cs b/FFMpegCore.Test/Utilities/OperatingSystemUtils.cs
new file mode 100644
index 0000000..5526a2a
--- /dev/null
+++ b/FFMpegCore.Test/Utilities/OperatingSystemUtils.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace FFMpegCore.Test.Utilities;
+
+public static class OperatingSystemUtils
+{
+ public static bool NotWindows()
+ {
+ return !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ }
+}
\ No newline at end of file
diff --git a/FFMpegCore.Test/Utilities/TestMethodWithIgnoreIfSupportAttribute.cs b/FFMpegCore.Test/Utilities/TestMethodWithIgnoreIfSupportAttribute.cs
new file mode 100644
index 0000000..5ea2436
--- /dev/null
+++ b/FFMpegCore.Test/Utilities/TestMethodWithIgnoreIfSupportAttribute.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace FFMpegCore.Test.Utilities;
+
+// https://matt.kotsenas.com/posts/ignoreif-mstest
+///
+/// An extension to the [TestMethod] attribute. It walks the method and class hierarchy looking
+/// for an [IgnoreIf] attribute. If one or more are found, they are each evaluated, if the attribute
+/// returns `true`, evaluation is short-circuited, and the test method is skipped.
+///
+public class TestMethodWithIgnoreIfSupportAttribute : TestMethodAttribute
+{
+ public override TestResult[] Execute(ITestMethod testMethod)
+ {
+ var ignoreResults = TestMethodUtils.GetIgnoreResults(testMethod);
+ return ignoreResults.Any()
+ ? ignoreResults
+ : base.Execute(testMethod);
+ }
+}
+public class DataTestMethodWithIgnoreIfSupportAttribute : DataTestMethodAttribute
+{
+ public override TestResult[] Execute(ITestMethod testMethod)
+ {
+ var ignoreResults = TestMethodUtils.GetIgnoreResults(testMethod);
+ return ignoreResults.Any()
+ ? ignoreResults
+ : base.Execute(testMethod);
+ }
+}
+
+internal class TestMethodUtils
+{
+ internal static TestResult[] GetIgnoreResults(ITestMethod testMethod)
+ {
+ var ignoreAttributes = FindAttributes(testMethod);
+
+ // Evaluate each attribute, and skip if one returns `true`
+ foreach (var ignoreAttribute in ignoreAttributes)
+ {
+ if (ignoreAttribute.ShouldIgnore(testMethod))
+ {
+ var message = $"Test not executed. Conditional ignore method '{ignoreAttribute.IgnoreCriteriaMethodName}' evaluated to 'true'.";
+ {
+ return new[]
+ {
+ new TestResult { Outcome = UnitTestOutcome.Inconclusive, TestFailureException = new AssertInconclusiveException(message) }
+ };
+ }
+ }
+ }
+
+ return Array.Empty();
+ }
+
+ private static IEnumerable FindAttributes(ITestMethod testMethod)
+ {
+ // Look for an [IgnoreIf] on the method, including any virtuals this method overrides
+ var ignoreAttributes = new List();
+ ignoreAttributes.AddRange(testMethod.GetAttributes(inherit: true));
+
+ // Walk the class hierarchy looking for an [IgnoreIf] attribute
+ var type = testMethod.MethodInfo.DeclaringType;
+ while (type != null)
+ {
+ ignoreAttributes.AddRange(type.GetCustomAttributes(inherit: true));
+ type = type.DeclaringType;
+ }
+ return ignoreAttributes;
+ }
+}
\ No newline at end of file
diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs
index ad72e57..06e030c 100644
--- a/FFMpegCore.Test/VideoTest.cs
+++ b/FFMpegCore.Test/VideoTest.cs
@@ -10,10 +10,12 @@
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
+using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FFMpegCore.Extensions.System.Drawing.Common;
+using FFMpegCore.Test.Utilities;
namespace FFMpegCore.Test
{
@@ -86,7 +88,9 @@ public void Video_ToH265_MKV_Args()
Assert.IsTrue(success);
}
- [DataTestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [DataTestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
@@ -102,7 +106,9 @@ public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat
Assert.IsTrue(success);
}
- [TestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public void Video_ToMP4_Args_Pipe_DifferentImageSizes()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
@@ -121,8 +127,9 @@ public void Video_ToMP4_Args_Pipe_DifferentImageSizes()
.ProcessSynchronously());
}
-
- [TestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)][SupportedOSPlatform("windows")]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public async Task Video_ToMP4_Args_Pipe_DifferentImageSizes_Async()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
@@ -141,7 +148,9 @@ public async Task Video_ToMP4_Args_Pipe_DifferentImageSizes_Async()
.ProcessAsynchronously());
}
- [TestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public void Video_ToMP4_Args_Pipe_DifferentPixelFormats()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
@@ -159,9 +168,10 @@ public void Video_ToMP4_Args_Pipe_DifferentPixelFormats()
.WithVideoCodec(VideoCodec.LibX264))
.ProcessSynchronously());
}
-
-
- [TestMethod, Timeout(10000)]
+
+ [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public async Task Video_ToMP4_Args_Pipe_DifferentPixelFormats_Async()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
@@ -315,7 +325,9 @@ public void Video_ToTS_Args()
Assert.IsTrue(success);
}
- [DataTestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [DataTestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
public async Task Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
@@ -347,7 +359,9 @@ public async Task Video_ToOGV_Resize()
Assert.IsTrue(success);
}
- [DataTestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [DataTestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
// [DataRow(PixelFormat.Format48bppRgb)]
@@ -382,7 +396,9 @@ public void Scale_Mp4_Multithreaded()
Assert.IsTrue(success);
}
- [DataTestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [DataTestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
// [DataRow(PixelFormat.Format48bppRgb)]
@@ -399,7 +415,9 @@ public void Video_ToMP4_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixe
Assert.IsTrue(success);
}
- [TestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public void Video_Snapshot_InMemory()
{
var input = FFProbe.Analyse(TestResources.Mp4Video);
@@ -410,7 +428,9 @@ public void Video_Snapshot_InMemory()
Assert.AreEqual(bitmap.RawFormat, ImageFormat.Png);
}
- [TestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public void Video_Snapshot_PersistSnapshot()
{
var outputPath = new TemporaryFile("out.png");
@@ -446,7 +466,8 @@ public void Video_Join()
Assert.AreEqual(input.PrimaryVideoStream.Width, result.PrimaryVideoStream.Width);
}
- [TestMethod, Timeout(10000)]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public void Video_Join_Image_Sequence()
{
var imageSet = new List();
@@ -555,7 +576,9 @@ public void Video_OutputsData()
Assert.IsTrue(File.Exists(outputFile));
}
- [TestMethod, Timeout(10000)]
+ [SupportedOSPlatform("windows")]
+ [TestMethodWithIgnoreIfSupport, Timeout(10000)]
+ [IgnoreIf(nameof(OperatingSystemUtils.NotWindows))]
public void Video_TranscodeInMemory()
{
using var resStream = new MemoryStream();