Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions src/System.Management.Automation/engine/PSVersionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,9 @@ IEnumerator IEnumerable.GetEnumerator()
public sealed class SemanticVersion : IComparable, IComparable<SemanticVersion>, IEquatable<SemanticVersion>
{
private const string VersionSansRegEx = @"^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))?$";
private const string LabelRegEx = @"^((?<preLabel>[0-9A-Za-z][0-9A-Za-z\-\.]*))?(\+(?<buildLabel>[0-9A-Za-z][0-9A-Za-z\-\.]*))?$";
private const string LabelUnitRegEx = @"^[0-9A-Za-z][0-9A-Za-z\-\.]*$";
private const string LabelRegEx = @"^(?<preLabel>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(?:\+(?<buildLabel>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$";
private const string LabelUnitRegEx = @"^((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)$";
private const string BuildUnitRegEx = @"^([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)$";
private const string PreLabelPropertyName = "PSSemVerPreReleaseLabel";
private const string BuildLabelPropertyName = "PSSemVerBuildLabel";
private const string TypeNameForVersionWithLabel = "System.Version#IncludeLabel";
Expand Down Expand Up @@ -370,7 +371,7 @@ public SemanticVersion(string version)
/// <param name="buildLabel">The build metadata for the version.</param>
/// <exception cref="FormatException">
/// If <paramref name="preReleaseLabel"/> don't match 'LabelUnitRegEx'.
/// If <paramref name="buildLabel"/> don't match 'LabelUnitRegEx'.
/// If <paramref name="buildLabel"/> don't match 'BuildUnitRegEx'.
/// </exception>
public SemanticVersion(int major, int minor, int patch, string preReleaseLabel, string buildLabel)
: this(major, minor, patch)
Expand All @@ -387,7 +388,7 @@ public SemanticVersion(int major, int minor, int patch, string preReleaseLabel,

if (!string.IsNullOrEmpty(buildLabel))
{
if (!Regex.IsMatch(buildLabel, LabelUnitRegEx))
if (!Regex.IsMatch(buildLabel, BuildUnitRegEx))
{
throw new FormatException(nameof(buildLabel));
}
Expand All @@ -410,7 +411,7 @@ public SemanticVersion(int major, int minor, int patch, string preReleaseLabel,
public SemanticVersion(int major, int minor, int patch, string label)
: this(major, minor, patch)
{
// We presume the SymVer :
// We presume the SemVer :
// 1) major.minor.patch-label
// 2) 'label' starts with letter or digit.
if (!string.IsNullOrEmpty(label))
Expand Down Expand Up @@ -447,7 +448,7 @@ public SemanticVersion(int major, int minor, int patch)
throw PSTraceSource.NewArgumentException(nameof(minor));
}

if (patch < 0)
if (patch < 0)
{
throw PSTraceSource.NewArgumentException(nameof(patch));
}
Expand Down Expand Up @@ -568,12 +569,12 @@ public static implicit operator Version(SemanticVersion semver)
public int Patch { get; }

/// <summary>
/// PreReleaseLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'.
/// PreReleaseLabel position in the SemVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'.
/// </summary>
public string PreReleaseLabel { get; }

/// <summary>
/// BuildLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'.
/// BuildLabel position in the SemVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'.
/// </summary>
public string BuildLabel { get; }

Expand Down Expand Up @@ -643,7 +644,7 @@ private static bool TryParseVersion(string version, ref VersionResult result)
string preLabel = null;
string buildLabel = null;

// We parse the SymVer 'version' string 'major.minor.patch-PreReleaseLabel+BuildLabel'.
// We parse the SemVer 'version' string 'major.minor.patch-PreReleaseLabel+BuildLabel'.
var dashIndex = version.IndexOf('-');
var plusIndex = version.IndexOf('+');

Expand Down Expand Up @@ -729,7 +730,7 @@ private static bool TryParseVersion(string version, ref VersionResult result)
}

if (preLabel != null && !Regex.IsMatch(preLabel, LabelUnitRegEx) ||
(buildLabel != null && !Regex.IsMatch(buildLabel, LabelUnitRegEx)))
(buildLabel != null && !Regex.IsMatch(buildLabel, BuildUnitRegEx)))
{
result.SetFailure(ParseFailureKind.FormatException);
return false;
Expand Down Expand Up @@ -804,7 +805,7 @@ public int CompareTo(object version)

/// <summary>
/// Implement <see cref="IComparable{T}.CompareTo"/>.
/// Meets SymVer 2.0 p.11 https://semver.org/
/// Meets SemVer 2.0 p.11 https://semver.org/
/// </summary>
public int CompareTo(SemanticVersion value)
{
Expand All @@ -820,7 +821,7 @@ public int CompareTo(SemanticVersion value)
if (Patch != value.Patch)
return Patch > value.Patch ? 1 : -1;

// SymVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata).
// SemVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata).
return ComparePreLabel(this.PreReleaseLabel, value.PreReleaseLabel);
}

Expand All @@ -837,7 +838,7 @@ public override bool Equals(object obj)
/// </summary>
public bool Equals(SemanticVersion other)
{
// SymVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata).
// SemVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata).
return other != null &&
(Major == other.Major) && (Minor == other.Minor) && (Patch == other.Patch) &&
string.Equals(PreReleaseLabel, other.PreReleaseLabel, StringComparison.Ordinal);
Expand Down Expand Up @@ -906,7 +907,7 @@ public override int GetHashCode()

private static int ComparePreLabel(string preLabel1, string preLabel2)
{
// Symver 2.0 standard p.9
// SemVer 2.0 standard p.9
// Pre-release versions have a lower precedence than the associated normal version.
// Comparing each dot separated identifier from left to right
// until a difference is found as follows:
Expand Down
101 changes: 101 additions & 0 deletions test/powershell/engine/Basic/SemanticVersion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,105 @@ Describe "SemanticVersion api tests" -Tags 'CI' {
{ $PSVersionTable.PSVersion | Format-Table | Out-String } | Should -Not -Throw
}
}

Context 'Semver official tests' {
BeforeAll {
$valid = @'
0.0.4
1.2.3
10.20.30
1.1.2-prerelease+meta
1.1.2+meta
1.1.2+meta-valid
1.0.0-alpha
1.0.0-beta
1.0.0-alpha.beta
1.0.0-alpha.beta.1
1.0.0-alpha.1
1.0.0-alpha0.valid
1.0.0-alpha.0valid
1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay
1.0.0-rc.1+build.1
2.0.0-rc.1+build.123
1.2.3-beta
10.2.3-DEV-SNAPSHOT
1.2.3-SNAPSHOT-123
1.0.0
2.0.0
1.1.7
2.0.0+build.1848
2.0.1-alpha.1227
1.0.0-alpha+beta
1.2.3----RC-SNAPSHOT.12.9.1--.12+788
1.2.3----R-S.12.9.1--.12+meta
1.2.3----RC-SNAPSHOT.12.9.1--.12
1.0.0+0.build.1-rc.10000aaa-kk-0.1
1.0.0-0A.is.legal
'@

$validVersions = @()
foreach ($version in $valid.Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)) {
$validVersions += @{version = $version}
}

$invalid = @'
1
1.2
1.2.3-0123
1.2.3-0123.0123
1.1.2+.123
+invalid
-invalid
-invalid+invalid
-invalid.01
alpha
alpha.beta
alpha.beta.1
alpha.1
alpha+beta
alpha_beta
alpha.
alpha..
beta
1.0.0-alpha_beta
-alpha.
1.0.0-alpha..
1.0.0-alpha..1
1.0.0-alpha...1
1.0.0-alpha....1
1.0.0-alpha.....1
1.0.0-alpha......1
1.0.0-alpha.......1
01.1.1
1.01.1
1.1.01
1.2
1.2.3.DEV
1.2-SNAPSHOT
1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788
1.2-RC-SNAPSHOT
-1.0.3-gamma+b7718
+justmeta
9.8.7+meta+meta
9.8.7-whatever+meta+meta
99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12
'@

$invalidVersions = @()
foreach ($version in $invalid.Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)) {
$invalidVersions += @{version = $version}
}
}

It 'Should parse valid versions: <version>' -TestCases $validVersions {
param($version)
$v = [semver]"$version"
$v.ToString() | Should -Be $version
}

It 'Should not parse invalid versions: <version>' -TestCases $invalidVersions {
{ [semver]"$version" } | Should -Throw
}
}

}