From f11bbdc918afb8c5f795711acb08af27421b4006 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 9 Jul 2020 13:27:36 +0200 Subject: [PATCH 1/5] add tests for IsOSPlatformOrLater and IsOSPlatformEarlierThan methods --- ...time.InteropServices.RuntimeInformation.cs | 10 + .../tests/PlatformVersionTests.cs | 238 ++++++++++++++++++ ...opServices.RuntimeInformation.Tests.csproj | 3 +- 3 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs index aae34f0b6a70e4..5bba24994424d7 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs @@ -39,5 +39,15 @@ public static partial class RuntimeInformation public static string OSDescription { get { throw null; } } public static System.Runtime.InteropServices.Architecture ProcessArchitecture { get { throw null; } } public static bool IsOSPlatform(System.Runtime.InteropServices.OSPlatform osPlatform) { throw null; } + public static bool IsOSPlatformOrLater(string platformName) { throw null; } + public static bool IsOSPlatformOrLater(System.Runtime.InteropServices.OSPlatform osPlatform, int major) { throw null; } + public static bool IsOSPlatformOrLater(System.Runtime.InteropServices.OSPlatform osPlatform, int major, int minor) { throw null; } + public static bool IsOSPlatformOrLater(System.Runtime.InteropServices.OSPlatform osPlatform, int major, int minor, int build) { throw null; } + public static bool IsOSPlatformOrLater(System.Runtime.InteropServices.OSPlatform osPlatform, int major, int minor, int build, int revision) { throw null; } + public static bool IsOSPlatformEarlierThan(string platformName) { throw null; } + public static bool IsOSPlatformEarlierThan(System.Runtime.InteropServices.OSPlatform osPlatform, int major) { throw null; } + public static bool IsOSPlatformEarlierThan(System.Runtime.InteropServices.OSPlatform osPlatform, int major, int minor) { throw null; } + public static bool IsOSPlatformEarlierThan(System.Runtime.InteropServices.OSPlatform osPlatform, int major, int minor, int build) { throw null; } + public static bool IsOSPlatformEarlierThan(System.Runtime.InteropServices.OSPlatform osPlatform, int major, int minor, int build, int revision) { throw null; } } } diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs new file mode 100644 index 00000000000000..4346c571b4fc13 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Runtime.InteropServices.RuntimeInformationTests +{ + public class PlatformVersionTests + { + public static IEnumerable AllKnownOsPlatforms() + { + yield return new object[] { OSPlatform.Windows }; + yield return new object[] { OSPlatform.Linux }; + yield return new object[] { OSPlatform.OSX }; + yield return new object[] { OSPlatform.Browser }; + } + + [Fact] + public void IsOSPlatformOrLater_Null_ThrowsArgumentNullExceptionWithArgumentName() + { + Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformOrLater(null)); + } + + [Fact] + public void IsOSPlatformOrLater_Empty_ThrowsArgumentNullExceptionWithArgumentName() + { + Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformOrLater(string.Empty)); + } + + [Theory] + [InlineData("ios")] // missing version number + [InlineData("ios14")] // ios14.0 is fine, ios14 is not + [InlineData("ios14.0.0.0.0.0")] // too many numbers + public void IsOSPlatformOrLater_InvalidVersionNumber_ThrowsArgumentExceptionWithArgumentName(string platformName) + { + Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformOrLater(platformName)); + } + + [Theory] + [MemberData(nameof(AllKnownOsPlatforms))] + public void IsOSPlatformOrLater_ReturnsTrue_ForCurrentOS(OSPlatform osPlatform) + { + // IsOSPlatformOrLater("xyz1.2.3.4") running as "xyz1.2.3.4" should return true + + bool isCurrentPlatfom = RuntimeInformation.IsOSPlatform(osPlatform); + Version current = Environment.OSVersion.Version; + + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{current}")); + + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, current.Major)); + + if (current.Minor >= 0) + { + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, current.Major, current.Minor)); + + if (current.Build >= 0) + { + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, current.Major, current.Minor, current.Build)); + + if (current.Revision >= 0) + { + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, current.Major, current.Minor, current.Build, current.Revision)); + } + } + } + } + + [Theory] + [MemberData(nameof(AllKnownOsPlatforms))] + public void IsOSPlatformOrLater_ReturnsFalse_ForNewerVersionOfCurrentOS(OSPlatform osPlatform) + { + // IsOSPlatformOrLater("xyz11.0") running as "xyz10.0" should return false + + Version currentVersion = Environment.OSVersion.Version; + + Version newer = new Version(currentVersion.Major + 1, 0); + Assert.False(RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{newer}")); + Assert.False(RuntimeInformation.IsOSPlatformOrLater(osPlatform, newer.Major)); + + newer = new Version(currentVersion.Major, currentVersion.Minor + 1); + Assert.False(RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{newer}")); + Assert.False(RuntimeInformation.IsOSPlatformOrLater(osPlatform, newer.Major, newer.Minor)); + + newer = new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build + 1); + Assert.False(RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{newer}")); + Assert.False(RuntimeInformation.IsOSPlatformOrLater(osPlatform, newer.Major, newer.Minor, newer.Build)); + + newer = new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build, currentVersion.Revision + 1); + Assert.False(RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{newer}")); + Assert.False(RuntimeInformation.IsOSPlatformOrLater(osPlatform, newer.Major, newer.Minor, newer.Build, newer.Revision)); + } + + [Theory] + [MemberData(nameof(AllKnownOsPlatforms))] + public void IsOSPlatformOrLater_ReturnsTrue_ForOlderVersionOfCurrentOS(OSPlatform osPlatform) + { + // IsOSPlatformOrLater("xyz10.0") running as "xyz11.0" should return true + + bool isCurrentPlatfom = RuntimeInformation.IsOSPlatform(osPlatform); + Version current = Environment.OSVersion.Version; + + Version older = new Version(current.Major - 1, 0); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{older}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, older.Major)); + + if (current.Minor > 0) + { + older = new Version(current.Major, current.Minor - 1); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{older}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, older.Major, older.Minor)); + } + + if (current.Build > 0) + { + older = new Version(current.Major, current.Minor, current.Build - 1); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{older}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, older.Major, older.Minor, older.Build)); + } + + if (current.Revision > 0) + { + older = new Version(current.Major, current.Minor, current.Build, current.Revision - 1); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater($"{osPlatform}{older}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformOrLater(osPlatform, older.Major, older.Minor, older.Build, older.Revision)); + } + } + + [Fact] + public void IsOSPlatformEarlierThan_Null_ThrowsArgumentNullExceptionWithArgumentName() + { + Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformEarlierThan(null)); + } + + [Fact] + public void IsOSPlatformEarlierThan_Empty_ThrowsArgumentNullExceptionWithArgumentName() + { + Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformEarlierThan(string.Empty)); + } + + [Theory] + [InlineData("ios")] // missing version number + [InlineData("ios14")] // ios14.0 is fine, ios14 is not + [InlineData("ios14.0.0.0.0.0")] // too many numbers + public void IsOSPlatformEarlierThan_InvalidVersionNumber_ThrowsArgumentExceptionWithArgumentName(string platformName) + { + Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformEarlierThan(platformName)); + } + + [Theory] + [MemberData(nameof(AllKnownOsPlatforms))] + public void IsOSPlatformEarlierThan_ReturnsFalse_ForCurrentOS(OSPlatform osPlatform) + { + // IsOSPlatformEarlierThan("xyz1.2.3.4") running as "xyz1.2.3.4" should return false + + Version current = Environment.OSVersion.Version; + + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{current}")); + + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, current.Major)); + + if (current.Minor >= 0) + { + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, current.Major, current.Minor)); + + if (current.Build >= 0) + { + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, current.Major, current.Minor, current.Build)); + + if (current.Revision >= 0) + { + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, current.Major, current.Minor, current.Build, current.Revision)); + } + } + } + } + + [Theory] + [MemberData(nameof(AllKnownOsPlatforms))] + public void IsOSPlatformEarlierThan_ReturnsTrue_ForNewerVersionOfCurrentOS(OSPlatform osPlatform) + { + // IsOSPlatformEarlierThan("xyz11.0") running as "xyz10.0" should return true + + bool isCurrentPlatfom = RuntimeInformation.IsOSPlatform(osPlatform); + Version current = Environment.OSVersion.Version; + + Version newer = new Version(current.Major + 1, 0); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{newer}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, newer.Major)); + + newer = new Version(current.Major, current.Minor + 1); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{newer}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, newer.Major, newer.Minor)); + + newer = new Version(current.Major, current.Minor, current.Build + 1); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{newer}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, newer.Major, newer.Minor, newer.Build)); + + newer = new Version(current.Major, current.Minor, current.Build, current.Revision + 1); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{newer}")); + Assert.Equal(isCurrentPlatfom, RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, newer.Major, newer.Minor, newer.Build, newer.Revision)); + } + + [Theory] + [MemberData(nameof(AllKnownOsPlatforms))] + public void IsOSPlatformEarlierThan_ReturnsFalse_ForOlderVersionOfCurrentOS(OSPlatform osPlatform) + { + // IsOSPlatformEarlierThan("xyz10.0") running as "xyz11.0" should return false + + Version current = Environment.OSVersion.Version; + + Version older = new Version(current.Major - 1, 0); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{older}")); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, older.Major)); + + if (current.Minor > 0) + { + older = new Version(current.Major, current.Minor - 1); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{older}")); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, older.Major, older.Minor)); + } + + if (current.Build > 0) + { + older = new Version(current.Major, current.Minor, current.Build - 1); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{older}")); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, older.Major, older.Minor, older.Build)); + } + + if (current.Revision > 0) + { + older = new Version(current.Major, current.Minor, current.Build, current.Revision - 1); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan($"{osPlatform}{older}")); + Assert.False(RuntimeInformation.IsOSPlatformEarlierThan(osPlatform, older.Major, older.Minor, older.Build, older.Revision)); + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj index ce83c0a5a00384..e3745518d39eff 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj @@ -1,4 +1,4 @@ - + true $(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser @@ -8,6 +8,7 @@ + From cb65eca07ec63e046e8bfbcd59a2314428fefbe8 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 9 Jul 2020 18:47:25 +0200 Subject: [PATCH 2/5] add IsOSPlatformOrLater and IsOSPlatformEarlierThan methods --- .../src/Resources/Strings.resx | 3 + ....InteropServices.RuntimeInformation.csproj | 2 + .../RuntimeInformation.PlatformVersion.cs | 94 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx index e554cfe1fe2eb1..ad90fd22b413cb 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx @@ -60,6 +60,9 @@ Value cannot be empty. + + "{0}" is not valid platform name. It must contain both name and version. Example: "ios14.0". + RuntimeInformation is not supported for Portable Class Libraries. diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj index 7b46386905a072..402c6678843b96 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj @@ -7,6 +7,7 @@ + @@ -45,5 +46,6 @@ + diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs new file mode 100644 index 00000000000000..d1d0fb3c046318 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + public static partial class RuntimeInformation + { + public static bool IsOSPlatformOrLater(string platformName) + { + (OSPlatform platform, Version version) = Parse(platformName); + + return IsOSPlatformOrLater(platform, version.Major, version.Minor, version.Build, version.Revision); + } + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) + => IsOSPlatform(osPlatform) && Environment.OSVersion.Version.Major >= major; + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) + => IsOSPlatform(osPlatform) && IsOSVersionOrLater(major, minor, int.MinValue, int.MinValue); + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) + => IsOSPlatform(osPlatform) && IsOSVersionOrLater(major, minor, build, int.MinValue); + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) + => IsOSPlatform(osPlatform) && IsOSVersionOrLater(major, minor, build, revision); + + public static bool IsOSPlatformEarlierThan(string platformName) + { + (OSPlatform platform, Version version) = Parse(platformName); + + return IsOSPlatformEarlierThan(platform, version.Major, version.Minor, version.Build, version.Revision); + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) + => IsOSPlatform(osPlatform) && Environment.OSVersion.Version.Major < major; + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) + => IsOSPlatform(osPlatform) && !IsOSVersionOrLater(major, minor, int.MinValue, int.MinValue); + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) + => IsOSPlatform(osPlatform) && !IsOSVersionOrLater(major, minor, build, int.MinValue); + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) + => IsOSPlatform(osPlatform) && !IsOSVersionOrLater(major, minor, build, revision); + + private static bool IsOSVersionOrLater(int major, int minor, int build, int revision) + { + Version current = Environment.OSVersion.Version; + if (current.Major != major) + { + return current.Major > major; + } + if (current.Minor != minor) + { + return current.Minor > minor; + } + if (current.Build != build) + { + return current.Build > build; + } + + return current.Revision >= revision; + } + + private static (OSPlatform, Version) Parse(string platformName) + { + if (platformName == null) + { + throw new ArgumentNullException(nameof(platformName)); + } + if (platformName.Length == 0) + { + throw new ArgumentException(SR.Argument_EmptyValue, nameof(platformName)); + } + + for (int i = 0; i < platformName.Length; i++) + { + if (char.IsDigit(platformName[i])) + { + if (Version.TryParse(platformName.AsSpan().Slice(i), out var version)) + { + return (OSPlatform.Create(platformName.Substring(0, i)), version); + } + else + { + break; + } + } + } + + throw new ArgumentException(SR.Format(SR.Argument_InvalidPlatfromName, platformName), nameof(platformName)); + } + } +} From d8122504b1102b9c5b0692858535c5b5bf3ac024 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 10 Jul 2020 08:01:32 +0200 Subject: [PATCH 3/5] add xml docs that explain what the API does and what input is supported --- .../src/Resources/Strings.resx | 2 +- .../RuntimeInformation.PlatformVersion.cs | 36 +++++++++++++++++++ .../tests/PlatformVersionTests.cs | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx index ad90fd22b413cb..c7f9079719cce4 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/Resources/Strings.resx @@ -61,7 +61,7 @@ Value cannot be empty. - "{0}" is not valid platform name. It must contain both name and version. Example: "ios14.0". + "{0}" is not a valid platform name. It must contain both name and version (with at least major and minor number specified). Example: "ios14.0". RuntimeInformation is not supported for Portable Class Libraries. diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs index d1d0fb3c046318..761abc4986c714 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs @@ -5,6 +5,12 @@ namespace System.Runtime.InteropServices { public static partial class RuntimeInformation { + /// + /// Check for the OS with a >= version comparison. Used to guard APIs that were added in the given OS release. + /// + /// OS name concatenated with a version number. + /// The version number must contain at least major and minor numbers separated with a dot. + /// Example: "ios14.0" is OK, "ios14" is NOT OK. public static bool IsOSPlatformOrLater(string platformName) { (OSPlatform platform, Version version) = Parse(platformName); @@ -12,18 +18,36 @@ public static bool IsOSPlatformOrLater(string platformName) return IsOSPlatformOrLater(platform, version.Major, version.Minor, version.Build, version.Revision); } + /// + /// Check for the OS with a >= version comparison. Used to guard APIs that were added in the given OS release. + /// public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) => IsOSPlatform(osPlatform) && Environment.OSVersion.Version.Major >= major; + /// + /// Check for the OS with a >= version comparison. Used to guard APIs that were added in the given OS release. + /// public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) => IsOSPlatform(osPlatform) && IsOSVersionOrLater(major, minor, int.MinValue, int.MinValue); + /// + /// Check for the OS with a >= version comparison. Used to guard APIs that were added in the given OS release. + /// public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) => IsOSPlatform(osPlatform) && IsOSVersionOrLater(major, minor, build, int.MinValue); + /// + /// Check for the OS with a >= version comparison. Used to guard APIs that were added in the given OS release. + /// public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) => IsOSPlatform(osPlatform) && IsOSVersionOrLater(major, minor, build, revision); + /// + /// Check for the OS with a < version comparison. Used to guard APIs that were obsoleted or removed in the given OS release. + /// + /// OS name concatenated with a version number. + /// The version number must contain at least major and minor numbers separated with a dot. + /// Example: "ios14.0" is OK, "ios14" is NOT OK. public static bool IsOSPlatformEarlierThan(string platformName) { (OSPlatform platform, Version version) = Parse(platformName); @@ -31,15 +55,27 @@ public static bool IsOSPlatformEarlierThan(string platformName) return IsOSPlatformEarlierThan(platform, version.Major, version.Minor, version.Build, version.Revision); } + /// + /// Check for the OS with a < version comparison. Used to guard APIs that were obsoleted or removed in the given OS release. + /// public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) => IsOSPlatform(osPlatform) && Environment.OSVersion.Version.Major < major; + /// + /// Check for the OS with a < version comparison. Used to guard APIs that were obsoleted or removed in the given OS release. + /// public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) => IsOSPlatform(osPlatform) && !IsOSVersionOrLater(major, minor, int.MinValue, int.MinValue); + /// + /// Check for the OS with a < version comparison. Used to guard APIs that were obsoleted or removed in the given OS release. + /// public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) => IsOSPlatform(osPlatform) && !IsOSVersionOrLater(major, minor, build, int.MinValue); + /// + /// Check for the OS with a < version comparison. Used to guard APIs that were obsoleted or removed in the given OS release. + /// public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) => IsOSPlatform(osPlatform) && !IsOSVersionOrLater(major, minor, build, revision); diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs index 4346c571b4fc13..a63b30386cd5db 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs @@ -30,7 +30,7 @@ public void IsOSPlatformOrLater_Empty_ThrowsArgumentNullExceptionWithArgumentNam [Theory] [InlineData("ios")] // missing version number - [InlineData("ios14")] // ios14.0 is fine, ios14 is not + [InlineData("ios14")] // ios14.0 is fine, ios14 is not: https://github.com/dotnet/runtime/pull/39005#discussion_r452541491 [InlineData("ios14.0.0.0.0.0")] // too many numbers public void IsOSPlatformOrLater_InvalidVersionNumber_ThrowsArgumentExceptionWithArgumentName(string platformName) { From 14b233cd3cdf9ee547399152deae7c79cd643986 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 10 Jul 2020 08:22:40 +0200 Subject: [PATCH 4/5] address code review feedback --- .../RuntimeInformation.PlatformVersion.cs | 21 ++++++++----------- .../tests/PlatformVersionTests.cs | 2 ++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs index 761abc4986c714..24834a4b23d42d 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs @@ -109,19 +109,16 @@ private static (OSPlatform, Version) Parse(string platformName) throw new ArgumentException(SR.Argument_EmptyValue, nameof(platformName)); } - for (int i = 0; i < platformName.Length; i++) + int i = platformName.Length - 1; + while (i >= 0 && (char.IsDigit(platformName[i]) || platformName[i] == '.')) { - if (char.IsDigit(platformName[i])) - { - if (Version.TryParse(platformName.AsSpan().Slice(i), out var version)) - { - return (OSPlatform.Create(platformName.Substring(0, i)), version); - } - else - { - break; - } - } + i--; + } + + i++; + if (i < platformName.Length - 1 && Version.TryParse(platformName.AsSpan(i), out Version? version)) + { + return (OSPlatform.Create(platformName.Substring(0, i)), version); } throw new ArgumentException(SR.Format(SR.Argument_InvalidPlatfromName, platformName), nameof(platformName)); diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs index a63b30386cd5db..003dc2a5c6b1ff 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs @@ -32,6 +32,8 @@ public void IsOSPlatformOrLater_Empty_ThrowsArgumentNullExceptionWithArgumentNam [InlineData("ios")] // missing version number [InlineData("ios14")] // ios14.0 is fine, ios14 is not: https://github.com/dotnet/runtime/pull/39005#discussion_r452541491 [InlineData("ios14.0.0.0.0.0")] // too many numbers + [InlineData("ios.14.0")] // version should not start with dot + [InlineData("ios14.0.")] // version should not end with dot public void IsOSPlatformOrLater_InvalidVersionNumber_ThrowsArgumentExceptionWithArgumentName(string platformName) { Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformOrLater(platformName)); From 966864141c1e3821ffd7ae4815ce0f820d2a3a2c Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 10 Jul 2020 08:38:40 +0200 Subject: [PATCH 5/5] numbers in the middle of the platform name are not supported --- .../RuntimeInformation.PlatformVersion.cs | 22 +++++++++++-------- .../tests/PlatformVersionTests.cs | 8 ++++--- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs index 24834a4b23d42d..0eec21939bfa82 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.PlatformVersion.cs @@ -109,16 +109,20 @@ private static (OSPlatform, Version) Parse(string platformName) throw new ArgumentException(SR.Argument_EmptyValue, nameof(platformName)); } - int i = platformName.Length - 1; - while (i >= 0 && (char.IsDigit(platformName[i]) || platformName[i] == '.')) + // iterate from the begining, as digits in the middle of the names are not supported by design + for (int i = 0; i < platformName.Length; i++) { - i--; - } - - i++; - if (i < platformName.Length - 1 && Version.TryParse(platformName.AsSpan(i), out Version? version)) - { - return (OSPlatform.Create(platformName.Substring(0, i)), version); + if (char.IsDigit(platformName[i])) + { + if (i > 0 && Version.TryParse(platformName.AsSpan(i), out Version? version)) + { + return (OSPlatform.Create(platformName.Substring(0, i)), version); + } + else + { + break; + } + } } throw new ArgumentException(SR.Format(SR.Argument_InvalidPlatfromName, platformName), nameof(platformName)); diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs index 003dc2a5c6b1ff..c00ace31490338 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/PlatformVersionTests.cs @@ -32,8 +32,8 @@ public void IsOSPlatformOrLater_Empty_ThrowsArgumentNullExceptionWithArgumentNam [InlineData("ios")] // missing version number [InlineData("ios14")] // ios14.0 is fine, ios14 is not: https://github.com/dotnet/runtime/pull/39005#discussion_r452541491 [InlineData("ios14.0.0.0.0.0")] // too many numbers - [InlineData("ios.14.0")] // version should not start with dot - [InlineData("ios14.0.")] // version should not end with dot + [InlineData("ios14.0.")] // version should not end with dot (but OS name could potentially end with dot, imagine "NET.") + [InlineData("numbers1.2inplatformname1.2")] // numbers in platform names are not supported https://github.com/dotnet/runtime/pull/39005#discussion_r452644601 public void IsOSPlatformOrLater_InvalidVersionNumber_ThrowsArgumentExceptionWithArgumentName(string platformName) { Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformOrLater(platformName)); @@ -142,8 +142,10 @@ public void IsOSPlatformEarlierThan_Empty_ThrowsArgumentNullExceptionWithArgumen [Theory] [InlineData("ios")] // missing version number - [InlineData("ios14")] // ios14.0 is fine, ios14 is not + [InlineData("ios14")] // ios14.0 is fine, ios14 is not: https://github.com/dotnet/runtime/pull/39005#discussion_r452541491 [InlineData("ios14.0.0.0.0.0")] // too many numbers + [InlineData("ios14.0.")] // version should not end with dot (but OS name could potentially end with dot, imagine "NET.") + [InlineData("numbers1.2inplatformname1.2")] // numbers in platform names are not supported https://github.com/dotnet/runtime/pull/39005#discussion_r452644601 public void IsOSPlatformEarlierThan_InvalidVersionNumber_ThrowsArgumentExceptionWithArgumentName(string platformName) { Assert.Throws("platformName", () => RuntimeInformation.IsOSPlatformEarlierThan(platformName));