diff --git a/src/Testing/src/xunit/OSMinVersionAttribute.cs b/src/Testing/src/xunit/OSMinVersionAttribute.cs new file mode 100644 index 0000000000..3bef606ee6 --- /dev/null +++ b/src/Testing/src/xunit/OSMinVersionAttribute.cs @@ -0,0 +1,81 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Testing +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public class OSMinVersionAttribute : Attribute, ITestCondition + { + private readonly OperatingSystems _targetOS; + private readonly Version _minVersion; + private readonly OperatingSystems _currentOS; + private readonly Version _currentVersion; + private readonly bool _skip; + + /// + /// Used to indicate the minimum version a test can run on for the given operating system. + /// Also add to skip other operating systems. + /// + /// The OS to check for a version. Only Windows is currently supported. + /// The minimum OS version NOT to skip. + public OSMinVersionAttribute(OperatingSystems targetOS, string minVersion) : + this(targetOS, Version.Parse(minVersion), GetCurrentOS(), GetCurrentOSVersion()) + { + } + + // to enable unit testing + internal OSMinVersionAttribute(OperatingSystems targetOS, Version minVersion, OperatingSystems currentOS, Version currentVersion) + { + if (targetOS != OperatingSystems.Windows) + { + throw new NotImplementedException(targetOS.ToString()); + } + + _targetOS = targetOS; + _minVersion = minVersion; + _currentOS = currentOS; + _currentVersion = currentVersion; + + _skip = _targetOS == _currentOS && _minVersion > _currentVersion; + SkipReason = $"The test cannot run on this operating system version '{currentVersion}'."; + } + + // Since a test would be excuted only if 'IsMet' is true, return false if we want to skip + public bool IsMet => !_skip; + + public string SkipReason { get; set; } + + static private OperatingSystems GetCurrentOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OperatingSystems.Windows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystems.Linux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystems.MacOSX; + } + throw new PlatformNotSupportedException(); + } + + static private Version GetCurrentOSVersion() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Environment.OSVersion.Version; + } + else + { + // Not implmeneted, but this will still be called before the OS check happens so don't throw. + return new Version(0, 0); + } + } + } +} diff --git a/src/Testing/src/xunit/WindowsVersions.cs b/src/Testing/src/xunit/WindowsVersions.cs index d89da44de3..d0ef86d1a8 100644 --- a/src/Testing/src/xunit/WindowsVersions.cs +++ b/src/Testing/src/xunit/WindowsVersions.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Testing { + /// + /// https://en.wikipedia.org/wiki/Windows_10_version_history + /// public static class WindowsVersions { public const string Win7 = "6.1"; @@ -14,5 +17,20 @@ namespace Microsoft.AspNetCore.Testing public const string Win81 = "6.3"; public const string Win10 = "10.0"; + + /// + /// 1803, RS4, 17134 + /// + public const string Win10_RS4 = "10.0.17134"; + + /// + /// 1909, 19H2, 18363 + /// + public const string Win10_19H2 = "10.0.18363"; + + /// + /// _, 20H2, 18990 + /// + public const string Win10_20H1 = "10.0.18990"; } } diff --git a/src/Testing/test/OSMinVersionAttributeTest.cs b/src/Testing/test/OSMinVersionAttributeTest.cs new file mode 100644 index 0000000000..f156607e8b --- /dev/null +++ b/src/Testing/test/OSMinVersionAttributeTest.cs @@ -0,0 +1,77 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class OSMinVersionAttributeTest + { + [Fact] + public void Linux_ThrowsNotImplemeneted() + { + Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.Linux, "2.5")); + } + + [Fact] + public void Mac_ThrowsNotImplemeneted() + { + Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.MacOSX, "2.5")); + } + + [Fact] + public void WindowsOrLinux_ThrowsNotImplemeneted() + { + Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, "2.5")); + } + + [Fact] + public void DoesNotSkip_LaterVersions() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("2.0"), + OperatingSystems.Windows, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_SameVersion() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Windows, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void Skip_EarlierVersion() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("3.0"), + OperatingSystems.Windows, + new Version("2.5")); + + Assert.False(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_WhenOnlyVersionsMatch() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Linux, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + } +} diff --git a/src/Testing/test/OSMinVersionTest.cs b/src/Testing/test/OSMinVersionTest.cs new file mode 100644 index 0000000000..30233823e2 --- /dev/null +++ b/src/Testing/test/OSMinVersionTest.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class OSMinVersionTest + { + [ConditionalFact] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + public void RunTest_Win8DoesNotRunOnWin7() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should not be running on Win7 or Win2008R2."); + } + + [ConditionalTheory] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + [InlineData(1)] + public void RunTheory_Win8DoesNotRunOnWin7(int arg) + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should not be running on Win7 or Win2008R2."); + } + + [ConditionalFact] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win10_RS4)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public void RunTest_Win10_RS4() + { + Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + var versionKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + Assert.NotNull(versionKey); + var currentVersion = (string)versionKey.GetValue("CurrentBuildNumber"); + Assert.NotNull(currentVersion); + Assert.True(17134 <= int.Parse(currentVersion)); + } + + [ConditionalFact] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H2)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public void RunTest_Win10_19H2() + { + Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + var versionKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + Assert.NotNull(versionKey); + var currentVersion = (string)versionKey.GetValue("CurrentBuildNumber"); + Assert.NotNull(currentVersion); + Assert.True(18363 <= int.Parse(currentVersion)); + } + } + + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + public class OSMinVersionClassTest + { + [ConditionalFact] + public void TestSkipClass_Win8DoesNotRunOnWin7() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should not be running on Win7 or Win2008R2."); + } + } +}