From fb6865587afe1f47567f5bafdf99f04b10e294e0 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Sun, 29 Jan 2023 22:55:35 +0100 Subject: [PATCH] Add IgnoreIf attribute to only run SDC tests on Windows --- FFMpegCore.Test/AudioTest.cs | 5 +- .../Utilities/IgnoreIfAttribute.cs | 39 ++++++++++ .../Utilities/OperatingSystemUtils.cs | 11 +++ .../TestMethodWithIgnoreIfSupportAttribute.cs | 75 +++++++++++++++++++ FFMpegCore.Test/VideoTest.cs | 53 +++++++++---- 5 files changed, 166 insertions(+), 17 deletions(-) create mode 100644 FFMpegCore.Test/Utilities/IgnoreIfAttribute.cs create mode 100644 FFMpegCore.Test/Utilities/OperatingSystemUtils.cs create mode 100644 FFMpegCore.Test/Utilities/TestMethodWithIgnoreIfSupportAttribute.cs 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();