diff --git a/src/Microsoft.PackageManagement.NuGetProvider/NugetLightRequest.cs b/src/Microsoft.PackageManagement.NuGetProvider/NugetLightRequest.cs
index 6f437885e7..5bc0aa9518 100644
--- a/src/Microsoft.PackageManagement.NuGetProvider/NugetLightRequest.cs
+++ b/src/Microsoft.PackageManagement.NuGetProvider/NugetLightRequest.cs
@@ -20,6 +20,8 @@
using System.Net;
using Microsoft.PackageManagement.Provider.Utility;
+ using SemanticVersion = Microsoft.PackageManagement.Provider.Utility.SemanticVersion;
+
///
/// This class drives the Request class that is an interface exposed from the PackageManagement Platform to the provider to use.
///
diff --git a/src/Microsoft.PackageManagement.NuGetProvider/nugetlightprovider.cs b/src/Microsoft.PackageManagement.NuGetProvider/nugetlightprovider.cs
index b21a211ca0..5bc32760ec 100644
--- a/src/Microsoft.PackageManagement.NuGetProvider/nugetlightprovider.cs
+++ b/src/Microsoft.PackageManagement.NuGetProvider/nugetlightprovider.cs
@@ -11,6 +11,8 @@ namespace Microsoft.PackageManagement.NuGetProvider
using System.Reflection;
using System.Management.Automation;
+ using SemanticVersion = Microsoft.PackageManagement.Provider.Utility.SemanticVersion;
+
///
/// A Package provider to the PackageManagement Platform.
///
diff --git a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/ExePackageInstaller.cs b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/ExePackageInstaller.cs
index a36cf34052..d3539d802a 100644
--- a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/ExePackageInstaller.cs
+++ b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/ExePackageInstaller.cs
@@ -29,6 +29,7 @@ namespace Microsoft.PackageManagement.PackageSourceListProvider
using Microsoft.PackageManagement.Provider.Utility;
using Microsoft.Win32;
using ErrorCategory = PackageManagement.Internal.ErrorCategory;
+ using SemanticVersion = Microsoft.PackageManagement.Provider.Utility.SemanticVersion;
internal static class ExePackageInstaller
{
diff --git a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/NupkgInstaller.cs b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/NupkgInstaller.cs
index 54a580a7fd..30e5387857 100644
--- a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/NupkgInstaller.cs
+++ b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/NupkgInstaller.cs
@@ -27,6 +27,7 @@ namespace Microsoft.PackageManagement.PackageSourceListProvider
using Microsoft.PackageManagement.Provider.Utility;
using ErrorCategory = PackageManagement.Internal.ErrorCategory;
using System.Globalization;
+ using SemanticVersion = Microsoft.PackageManagement.Provider.Utility.SemanticVersion;
internal static class NupkgInstaller {
diff --git a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListParser.cs b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListParser.cs
index afb0cb0253..f1194779b2 100644
--- a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListParser.cs
+++ b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListParser.cs
@@ -9,6 +9,8 @@ using Microsoft.PackageManagement.Provider.Utility;
using System.Reflection;
using System.Globalization;
+using SemanticVersion = Microsoft.PackageManagement.Provider.Utility.SemanticVersion;
+
namespace Microsoft.PackageManagement.PackageSourceListProvider
{
internal static class JsonParser
diff --git a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListRequest.cs b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListRequest.cs
index 7429a0815b..e04536575b 100644
--- a/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListRequest.cs
+++ b/src/Microsoft.PackageManagement.PackageSourceListProvider/PackageList/PackageListRequest.cs
@@ -34,6 +34,7 @@ namespace Microsoft.PackageManagement.PackageSourceListProvider
using Microsoft.PackageManagement.Implementation;
using Microsoft.PackageManagement.Internal.Api;
using Microsoft.PackageManagement.Provider.Utility;
+ using SemanticVersion = Microsoft.PackageManagement.Provider.Utility.SemanticVersion;
public abstract class PackageSourceListRequest : Request {
diff --git a/src/System.Management.Automation/engine/PSVersionInfo.cs b/src/System.Management.Automation/engine/PSVersionInfo.cs
index 0414668471..7a8ba3b8b6 100644
--- a/src/System.Management.Automation/engine/PSVersionInfo.cs
+++ b/src/System.Management.Automation/engine/PSVersionInfo.cs
@@ -4,6 +4,8 @@ Copyright (c) Microsoft Corporation. All rights reserved.
using System.Diagnostics;
using System.Reflection;
using System.Collections;
+using System.Globalization;
+using System.Management.Automation.Internal;
using Microsoft.Win32;
namespace System.Management.Automation
@@ -37,6 +39,7 @@ namespace System.Management.Automation
static Version _psV4Version = new Version(4, 0);
static Version _psV5Version = new Version(5, 0);
static Version _psV51Version = new Version(5, 1, NTVerpVars.PRODUCTBUILD, NTVerpVars.PRODUCTBUILD_QFE);
+ static SemanticVersion _psV6Version = new SemanticVersion(6, 0, 0, "alpha");
///
/// A constant to track current PowerShell Edition
@@ -57,11 +60,11 @@ namespace System.Management.Automation
{
_psVersionTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
- _psVersionTable[PSVersionInfo.PSVersionName] = _psV51Version;
+ _psVersionTable[PSVersionInfo.PSVersionName] = _psV6Version;
_psVersionTable["PSEdition"] = PSEditionValue;
_psVersionTable["BuildVersion"] = GetBuildVersion();
_psVersionTable["GitCommitId"] = GetCommitInfo();
- _psVersionTable["PSCompatibleVersions"] = new Version[] { _psV1Version, _psV2Version, _psV3Version, _psV4Version, _psV5Version, _psV51Version };
+ _psVersionTable["PSCompatibleVersions"] = new Version[] { _psV1Version, _psV2Version, _psV3Version, _psV4Version, _psV5Version, _psV51Version, _psV6Version };
_psVersionTable[PSVersionInfo.SerializationVersionName] = new Version(InternalSerializer.DefaultVersion);
_psVersionTable[PSVersionInfo.PSRemotingProtocolVersionName] = RemotingConstants.ProtocolVersion;
_psVersionTable[PSVersionInfo.WSManStackVersionName] = GetWSManStackVersion();
@@ -140,7 +143,7 @@ namespace System.Management.Automation
{
get
{
- return (Version) GetPSVersionTable()[PSVersionInfo.PSVersionName];
+ return (SemanticVersion) GetPSVersionTable()[PSVersionInfo.PSVersionName];
}
}
@@ -247,6 +250,10 @@ namespace System.Management.Automation
static internal bool IsValidPSVersion(Version version)
{
+ if (version.Major == _psV6Version.Major)
+ {
+ return version.Minor == _psV6Version.Minor;
+ }
if (version.Major == _psV5Version.Major)
{
return (version.Minor == _psV5Version.Minor || version.Minor == _psV51Version.Minor);
@@ -286,7 +293,460 @@ namespace System.Management.Automation
get { return _psV51Version; }
}
+ static internal SemanticVersion PSV6Version
+ {
+ get { return _psV6Version; }
+ }
+
#endregion
}
+
+ ///
+ /// An implementation of semantic versioning (http://semver.org)
+ /// that can be converted to/from .
+ ///
+ /// When converting to , a PSNoteProperty is
+ /// added to the instance to store the semantic version label so
+ /// that it can be recovered when creating a new SemanticVersion.
+ ///
+ public sealed class SemanticVersion : IComparable, IComparable, IEquatable
+ {
+ ///
+ /// Construct a SemanticVersion from a string.
+ ///
+ /// The version to parse
+ ///
+ ///
+ ///
+ ///
+ public SemanticVersion(string version)
+ {
+ var v = SemanticVersion.Parse(version);
+
+ Major = v.Major;
+ Minor = v.Minor;
+ Patch = v.Patch;
+ Label = v.Label;
+ }
+
+ ///
+ /// Construct a SemanticVersion.
+ ///
+ /// The major version
+ /// The minor version
+ /// The minor version
+ /// The label for the version
+ ///
+ /// If , , or is less than 0.
+ ///
+ ///
+ /// If is null or an empty string.
+ ///
+ public SemanticVersion(int major, int minor, int patch, string label)
+ : this(major, minor, patch)
+ {
+ if (string.IsNullOrEmpty(label)) throw PSTraceSource.NewArgumentNullException(nameof(label));
+
+ Label = label;
+ }
+
+ ///
+ /// Construct a SemanticVersion.
+ ///
+ /// The major version
+ /// The minor version
+ /// The minor version
+ ///
+ /// If , , or is less than 0.
+ ///
+ public SemanticVersion(int major, int minor, int patch)
+ {
+ if (major < 0) throw PSTraceSource.NewArgumentException(nameof(major));
+ if (minor < 0) throw PSTraceSource.NewArgumentException(nameof(minor));
+ if (patch < 0) throw PSTraceSource.NewArgumentException(nameof(patch));
+
+ Major = major;
+ Minor = minor;
+ Patch = patch;
+ Label = null;
+ }
+
+ const string LabelPropertyName = "PSSemanticVersionLabel";
+
+ ///
+ /// Construct a from a ,
+ /// copying the NoteProperty storing the label if the expected property exists.
+ ///
+ /// The version.
+ public SemanticVersion(Version version)
+ {
+ if (version.Revision > 0 || version.Build < 0) throw PSTraceSource.NewArgumentException(nameof(version));
+
+ Major = version.Major;
+ Minor = version.Minor;
+ Patch = version.Build;
+ var psobj = new PSObject(version);
+ var labelNote = psobj.Properties[LabelPropertyName];
+ if (labelNote != null)
+ {
+ Label = labelNote.Value as string;
+ }
+ }
+
+ ///
+ /// Convert a to a .
+ /// If there is a , it is added as a NoteProperty to the
+ /// result so that you can round trip back to a
+ /// without losing the label.
+ ///
+ ///
+ public static implicit operator Version(SemanticVersion semver)
+ {
+ var result = new Version(semver.Major, semver.Minor, semver.Patch);
+
+ if (!string.IsNullOrEmpty(semver.Label))
+ {
+ var psobj = new PSObject(result);
+ psobj.Properties.Add(new PSNoteProperty(LabelPropertyName, semver.Label));
+ }
+
+ return result;
+ }
+
+ ///
+ /// The major version number, never negative.
+ ///
+ public int Major { get; }
+
+ ///
+ /// The minor version number, never negative.
+ ///
+ public int Minor { get; }
+
+ ///
+ /// The patch version, -1 if not specified.
+ ///
+ public int Patch { get; }
+
+ ///
+ /// The last component in a SemanticVersion - may be null if not specified.
+ ///
+ public string Label { get; }
+
+ ///
+ /// Parse and return the result if it is a valid , otherwise throws an exception.
+ ///
+ /// The string to parse
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static SemanticVersion Parse(string version)
+ {
+ if (version == null) throw PSTraceSource.NewArgumentNullException(nameof(version));
+
+ var r = new VersionResult();
+ r.Init(true);
+ TryParseVersion(version, ref r);
+
+ return r._parsedVersion;
+ }
+
+ ///
+ /// Parse and return true if it is a valid , otherwise return false.
+ /// No exceptions are raised.
+ ///
+ /// The string to parse
+ /// The return value when the string is a valid
+ public static bool TryParse(string version, out SemanticVersion result)
+ {
+ if (version != null)
+ {
+ var r = new VersionResult();
+ r.Init(false);
+
+ if (TryParseVersion(version, ref r))
+ {
+ result = r._parsedVersion;
+ return true;
+ }
+ }
+
+ result = null;
+ return false;
+ }
+
+ private static bool TryParseVersion(string version, ref VersionResult result)
+ {
+ var dashIndex = version.IndexOf('-');
+
+ // Empty label?
+ if (dashIndex == version.Length - 1)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentException);
+ return false;
+ }
+
+ var versionSansLabel = (dashIndex < 0) ? version : version.Substring(0, dashIndex);
+ string[] parsedComponents = versionSansLabel.Split(Utils.Separators.Dot);
+ if (parsedComponents.Length != 3)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentException);
+ return false;
+ }
+
+ int major, minor, patch;
+ if (!TryParseComponent(parsedComponents[0], "major", ref result, out major))
+ {
+ return false;
+ }
+
+ if (!TryParseComponent(parsedComponents[1], "minor", ref result, out minor))
+ {
+ return false;
+ }
+
+ if (!TryParseComponent(parsedComponents[2], "patch", ref result, out patch))
+ {
+ return false;
+ }
+
+ result._parsedVersion = dashIndex < 0
+ ? new SemanticVersion(major, minor, patch)
+ : new SemanticVersion(major, minor, patch, version.Substring(dashIndex + 1));
+ return true;
+ }
+
+ private static bool TryParseComponent(string component, string componentName, ref VersionResult result, out int parsedComponent)
+ {
+ if (!Int32.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent))
+ {
+ result.SetFailure(ParseFailureKind.FormatException, component);
+ return false;
+ }
+
+ if (parsedComponent < 0)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentOutOfRangeException, componentName);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// ToString
+ ///
+ public override string ToString()
+ {
+ if (Patch < 0)
+ {
+ return string.IsNullOrEmpty(Label)
+ ? StringUtil.Format("{0}.{1}", Major, Minor)
+ : StringUtil.Format("{0}.{1}-{2}", Major, Minor, Label);
+ }
+
+ return string.IsNullOrEmpty(Label)
+ ? StringUtil.Format("{0}.{1}.{2}", Major, Minor, Patch)
+ : StringUtil.Format("{0}.{1}.{2}-{3}", Major, Minor, Patch, Label);
+ }
+
+ ///
+ /// Implement
+ ///
+ public int CompareTo(object version)
+ {
+ if (version == null)
+ {
+ return 1;
+ }
+
+ var v = version as SemanticVersion;
+ if (v == null)
+ {
+ throw PSTraceSource.NewArgumentException(nameof(version));
+ }
+
+ return CompareTo(v);
+ }
+
+ ///
+ /// Implement
+ ///
+ public int CompareTo(SemanticVersion value)
+ {
+ if ((object)value == null)
+ return 1;
+
+ if (Major != value.Major)
+ return Major > value.Major ? 1 : -1;
+
+ if (Minor != value.Minor)
+ return Minor > value.Minor ? 1 : -1;
+
+ if (Patch != value.Patch)
+ return Patch > value.Patch ? 1 : -1;
+
+ if (Label == null)
+ return value.Label == null ? 0 : 1;
+
+ if (value.Label == null)
+ return -1;
+
+ if (!string.Equals(Label, value.Label, StringComparison.Ordinal))
+ return string.Compare(Label, value.Label, StringComparison.Ordinal);
+
+ return 0;
+ }
+
+ ///
+ /// Override
+ ///
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as SemanticVersion);
+ }
+
+ ///
+ /// Implement
+ ///
+ public bool Equals(SemanticVersion other)
+ {
+ return other != null &&
+ (Major == other.Major) && (Minor == other.Minor) && (Patch == other.Patch) &&
+ string.Equals(Label, other.Label, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Override
+ ///
+ public override int GetHashCode()
+ {
+ return Utils.CombineHashCodes(
+ Major.GetHashCode(),
+ Minor.GetHashCode(),
+ Patch.GetHashCode(),
+ Label == null ? 0 : Label.GetHashCode());
+ }
+
+ ///
+ /// Overloaded == operator
+ ///
+ public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
+ {
+ if (object.ReferenceEquals(v1, null)) {
+ return object.ReferenceEquals(v2, null);
+ }
+
+ return v1.Equals(v2);
+ }
+
+ ///
+ /// Overloaded != operator
+ ///
+ public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
+ {
+ return !(v1 == v2);
+ }
+
+ ///
+ /// Overloaded < operator
+ ///
+ public static bool operator <(SemanticVersion v1, SemanticVersion v2)
+ {
+ if ((object) v1 == null) throw PSTraceSource.NewArgumentException(nameof(v1));
+ return (v1.CompareTo(v2) < 0);
+ }
+
+ ///
+ /// Overloaded <= operator
+ ///
+ public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
+ {
+ if ((object) v1 == null) throw PSTraceSource.NewArgumentException(nameof(v1));
+ return (v1.CompareTo(v2) <= 0);
+ }
+
+ ///
+ /// Overloaded > operator
+ ///
+ public static bool operator >(SemanticVersion v1, SemanticVersion v2)
+ {
+ return (v2 < v1);
+ }
+
+ ///
+ /// Overloaded >= operator
+ ///
+ public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
+ {
+ return (v2 <= v1);
+ }
+
+ internal enum ParseFailureKind
+ {
+ ArgumentException,
+ ArgumentOutOfRangeException,
+ FormatException
+ }
+
+ internal struct VersionResult
+ {
+ internal SemanticVersion _parsedVersion;
+ internal ParseFailureKind _failure;
+ internal string _exceptionArgument;
+ internal bool _canThrow;
+
+ internal void Init(bool canThrow)
+ {
+ _canThrow = canThrow;
+ }
+
+ internal void SetFailure(ParseFailureKind failure)
+ {
+ SetFailure(failure, String.Empty);
+ }
+
+ internal void SetFailure(ParseFailureKind failure, string argument)
+ {
+ _failure = failure;
+ _exceptionArgument = argument;
+ if (_canThrow)
+ {
+ throw GetVersionParseException();
+ }
+ }
+
+ internal Exception GetVersionParseException()
+ {
+ switch (_failure)
+ {
+ case ParseFailureKind.ArgumentException:
+ return PSTraceSource.NewArgumentException("version");
+ case ParseFailureKind.ArgumentOutOfRangeException:
+ throw new ValidationMetadataException("ValidateRangeTooSmall",
+ null, Metadata.ValidateRangeSmallerThanMinRangeFailure,
+ _exceptionArgument, "0");
+ case ParseFailureKind.FormatException:
+ // Regenerate the FormatException as would be thrown by Int32.Parse()
+ try
+ {
+ Int32.Parse(_exceptionArgument, CultureInfo.InvariantCulture);
+ }
+ catch (FormatException e)
+ {
+ return e;
+ }
+ catch (OverflowException e)
+ {
+ return e;
+ }
+ break;
+ }
+ return PSTraceSource.NewArgumentException("version");
+ }
+ }
+ }
}
diff --git a/test/powershell/engine/SemanticVersion.Tests.ps1 b/test/powershell/engine/SemanticVersion.Tests.ps1
new file mode 100644
index 0000000000..c350dcfc7c
--- /dev/null
+++ b/test/powershell/engine/SemanticVersion.Tests.ps1
@@ -0,0 +1,187 @@
+using namespace System.Management.Automation
+using namespace System.Management.Automation.Language
+
+Describe "SemanticVersion api tests" {
+ Context "constructing valid versions" {
+ It "string argument constructor" {
+ $v = [SemanticVersion]::new("1.2.3-alpha")
+ $v.Major | Should Be 1
+ $v.Minor | Should Be 2
+ $v.Patch | Should Be 3
+ $v.Label | Should Be "alpha"
+ $v.ToString() | Should Be "1.2.3-alpha"
+
+ $v = [SemanticVersion]::new("1.0.0")
+ $v.Major | Should Be 1
+ $v.Minor | Should Be 0
+ $v.Patch | Should Be 0
+ $v.Label | Should BeNullOrEmpty
+ $v.ToString() | Should Be "1.0.0"
+ }
+
+ # After the above test, we trust the properties and rely on ToString for validation
+
+ It "int args constructor" {
+ $v = [SemanticVersion]::new(1, 0, 0)
+ $v.ToString() | Should Be "1.0.0"
+
+ $v = [SemanticVersion]::new(3, 2, 0, "beta.1")
+ $v.ToString() | Should Be "3.2.0-beta.1"
+ }
+
+ It "version arg constructor" {
+ $v = [SemanticVersion]::new([Version]::new(1, 2, 3))
+ $v.ToString() | Should Be '1.2.3'
+ }
+
+ It "semantic version can round trip through version" {
+ $v1 = [SemanticVersion]::new(3, 2, 1, "prerelease")
+ $v2 = [SemanticVersion]::new([Version]$v1)
+ $v2.ToString() | Should Be "3.2.1-prerelease"
+ }
+ }
+
+ Context "Comparisons" {
+ $v1_0_0 = [SemanticVersion]::new(1, 0, 0)
+ $v1_1_0 = [SemanticVersion]::new(1, 1, 0)
+ $v1_1_1 = [SemanticVersion]::new(1, 1, 1)
+ $v2_1_0 = [SemanticVersion]::new(2, 1, 0)
+ $v1_0_0_alpha = [SemanticVersion]::new(1, 0, 0, "alpha")
+ $v1_0_0_beta = [SemanticVersion]::new(1, 0, 0, "beta")
+
+ $testCases = @(
+ @{ lhs = $v1_0_0; rhs = $v1_1_0 }
+ @{ lhs = $v1_0_0; rhs = $v1_1_1 }
+ @{ lhs = $v1_1_0; rhs = $v1_1_1 }
+ @{ lhs = $v1_0_0; rhs = $v2_1_0 }
+ @{ lhs = $v1_0_0_alpha; rhs = $v1_0_0_beta }
+ @{ lhs = $v1_0_0_alpha; rhs = $v1_0_0 }
+ @{ lhs = $v1_0_0_beta; rhs = $v1_0_0 }
+ )
+ It "less than" -TestCases $testCases {
+ param($lhs, $rhs)
+ $lhs -lt $rhs | Should Be $true
+ $rhs -lt $lhs | Should Be $false
+ }
+ It "less than or equal" -TestCases $testCases {
+ param($lhs, $rhs)
+ $lhs -le $rhs | Should Be $true
+ $rhs -le $lhs | Should Be $false
+ $lhs -le $lhs | Should Be $true
+ $rhs -le $rhs | Should Be $true
+ }
+ It "greater than" -TestCases $testCases {
+ param($lhs, $rhs)
+ $lhs -gt $rhs | Should Be $false
+ $rhs -gt $lhs | Should Be $true
+ }
+ It "greater than or equal" -TestCases $testCases {
+ param($lhs, $rhs)
+ $lhs -ge $rhs | Should Be $false
+ $rhs -ge $lhs | Should Be $true
+ $lhs -ge $lhs | Should Be $true
+ $rhs -ge $rhs | Should Be $true
+ }
+
+ $testCases = @(
+ @{ operand = $v1_0_0 }
+ @{ operand = $v1_0_0_alpha }
+ )
+ It "Equality" -TestCases $testCases {
+ param($operand)
+ $operand -eq $operand | Should Be $true
+ $operand -ne $operand | Should Be $false
+ $null -eq $operand | Should Be $false
+ $operand -eq $null | Should Be $false
+ $null -ne $operand | Should Be $true
+ $operand -ne $null | Should Be $true
+ }
+
+ It "comparisons with null" {
+ $v1_0_0 -lt $null | Should Be $false
+ $null -lt $v1_0_0 | Should Be $true
+ $v1_0_0 -le $null | Should Be $false
+ $null -le $v1_0_0 | Should Be $true
+ $v1_0_0 -gt $null | Should Be $true
+ $null -gt $v1_0_0 | Should Be $false
+ $v1_0_0 -ge $null | Should Be $true
+ $null -ge $v1_0_0 | Should Be $false
+ }
+ }
+
+ Context "error handling" {
+
+ # The specific errors aren't too useful here, but noted in comments
+ # so when we pick up a version of Pester that will let us check FullyQualifiedErrorId,
+ # it's easier to tweak the tests
+
+ $testCases = @(
+ @{ expectedResult = $false; version = $null }
+ @{ expectedResult = $false; version = [NullString]::Value }
+ @{ expectedResult = $false; version = "" }
+ @{ expectedResult = $false; version = "1.0.0-" }
+ @{ expectedResult = $false; version = "-" }
+ @{ expectedResult = $false; version = "-alpha" }
+ @{ expectedResult = $false; version = "1.0" } # REVIEW - should this be allowed
+ @{ expectedResult = $false; version = "1..0" }
+ @{ expectedResult = $false; version = "1.0.-alpha" }
+ @{ expectedResult = $false; version = "1.0." }
+ @{ expectedResult = $false; version = ".0.0" }
+ )
+
+ It "parts of version missing" -TestCases $testCases {
+ param($version, $expectedResult)
+ { [SemanticVersion]::new($version) } | Should Throw # PSArgumentException
+ { [SemanticVersion]::Parse($version) } | Should Throw # PSArgumentException
+ $semVer = $null
+ [SemanticVersion]::TryParse($_, [ref]$semVer) | Should Be $expectedResult
+ $semVer | Should Be $null
+ }
+
+ $testCases = @(
+ @{ expectedResult = $false; version = "-1.0.0" }
+ @{ expectedResult = $false; version = "1.-1.0" }
+ @{ expectedResult = $false; version = "1.0.-1" }
+ )
+
+ It "range check of versions" -TestCases $testCases {
+ param($version, $expectedResult)
+ { [SemanticVersion]::new($version) } | Should Throw # PSArgumentException
+ { [SemanticVersion]::Parse($version) } | Should Throw # PSArgumentException
+ $semVer = $null
+ [SemanticVersion]::TryParse($_, [ref]$semVer) | Should Be $expectedResult
+ $semVer | Should Be $null
+ }
+
+ $testCases = @(
+ @{ expectedResult = $false; version = "aa.0.0" }
+ @{ expectedResult = $false; version = "1.bb.0" }
+ @{ expectedResult = $false; version = "1.0.cc" }
+ )
+
+ It "format errors" -TestCases $testCases {
+ param($version, $expectedResult)
+ { [SemanticVersion]::new($version) } | Should Throw # PSArgumentException
+ { [SemanticVersion]::Parse($version) } | Should Throw # PSArgumentException
+ $semVer = $null
+ [SemanticVersion]::TryParse($_, [ref]$semVer) | Should Be $expectedResult
+ $semVer | Should Be $null
+ }
+
+ It "Negative version arguments" {
+ { [SemanticVersion]::new(-1, 0) } | Should Throw # PSArgumentException
+ { [SemanticVersion]::new(1, -1) } | Should Throw # PSArgumentException
+ { [SemanticVersion]::new(1, 1, -1) } | Should Throw # PSArgumentException
+ }
+
+ It "Incompatible version throws" {
+ # Revision isn't supported
+ { [SemanticVersion]::new([Version]::new(0, 0, 0, 4)) } | Should Throw # PSArgumentException
+ { [SemanticVersion]::new([Version]::new("1.2.3.4")) } | Should Throw # PSArgumentException
+
+ # Build is required
+ { [SemanticVersion]::new([Version]::new(1, 2)) } | Should Throw # PSArgumentException
+ { [SemanticVersion]::new([Version]::new("1.2")) } | Should Throw # PSArgumentException
+ }
+ }
+}
\ No newline at end of file