Skip to content
Open
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
46 changes: 30 additions & 16 deletions src/classes/public/PSSemVer.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -172,38 +172,45 @@
}

[int] CompareTo([Object]$other) {
if (-not $other -is [PSSemVer]) {
# A null comparand sorts before any value, per the IComparable convention.
if ($null -eq $other) {
return 1
}
# PowerShell converts the right-hand operand for -lt/-gt and Sort-Object, but a direct
# CompareTo call can pass anything, so convert here as well and reject what cannot be a version.
$comparand = $other -as [PSSemVer]
if ($null -eq $comparand) {
throw [ArgumentException]::new('The argument must be of type PSSemVer')
}
if ($this.Major -lt $other.Major) {
if ($this.Major -lt $comparand.Major) {
return -1
}
if ($this.Major -gt $other.Major) {
if ($this.Major -gt $comparand.Major) {
return 1
}
if ($this.Minor -lt $other.Minor) {
if ($this.Minor -lt $comparand.Minor) {
return -1
}
if ($this.Minor -gt $other.Minor) {
if ($this.Minor -gt $comparand.Minor) {
return 1
}
if ($this.Patch -lt $other.Patch) {
if ($this.Patch -lt $comparand.Patch) {
return -1
}
if ($this.Patch -gt $other.Patch) {
if ($this.Patch -gt $comparand.Patch) {
return 1
}
if ([string]::IsNullOrEmpty($this.Prerelease) -and [string]::IsNullOrEmpty($other.Prerelease)) {
if ([string]::IsNullOrEmpty($this.Prerelease) -and [string]::IsNullOrEmpty($comparand.Prerelease)) {
return 0
}
if ([string]::IsNullOrEmpty($this.Prerelease)) {
return 1
}
if ([string]::IsNullOrEmpty($other.Prerelease)) {
if ([string]::IsNullOrEmpty($comparand.Prerelease)) {
return -1
}
$thisPrereleaseArray = ($this.Prerelease -split '\.')
$otherPrereleaseArray = ($other.Prerelease -split '\.')
$otherPrereleaseArray = ($comparand.Prerelease -split '\.')
for ($i = 0; $i -lt [Math]::Max($thisPrereleaseArray.Length, $otherPrereleaseArray.Length); $i++) {
if ($i -ge $thisPrereleaseArray.Length) {
return -1
Expand All @@ -230,22 +237,29 @@
}

[bool] Equals([Object]$other) {
if (-not $other -is [PSSemVer]) {
# PowerShell does not convert the right-hand operand for -eq, so an unconverted value
# arrives here. Convert it so -eq against a version string keeps working, and treat
# anything that is not a version as simply not equal rather than comparing absent properties.
if ($null -eq $other) {
return $false
}
$comparand = $other -as [PSSemVer]
if ($null -eq $comparand) {
return $false
}
if ($this.Major -ne $other.Major) {
if ($this.Major -ne $comparand.Major) {
return $false
}
if ($this.Minor -ne $other.Minor) {
if ($this.Minor -ne $comparand.Minor) {
return $false
}
if ($this.Patch -ne $other.Patch) {
if ($this.Patch -ne $comparand.Patch) {
return $false
}
if ($this.Prerelease -ne $other.Prerelease) {
if ($this.Prerelease -ne $comparand.Prerelease) {
return $false
}
if ($this.BuildMetadata -ne $other.BuildMetadata) {
if ($this.BuildMetadata -ne $comparand.BuildMetadata) {
return $false
}
return $true
Expand Down
44 changes: 44 additions & 0 deletions tests/PSSemVer.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,50 @@ Describe 'PSSemVer' {
}

Describe 'Class: Comparison' {
Context 'Type guard' {
It 'CompareTo throws on a value that cannot be a version' {
{ ([PSSemVer]'1.0.0').CompareTo([guid]::NewGuid()) } | Should -Throw
}
It 'CompareTo throws on a hashtable that has no version properties' {
{ ([PSSemVer]'1.0.0').CompareTo(@{ Foo = 'bar' }) } | Should -Throw
}
It 'CompareTo accepts a hashtable that describes a version' {
# PowerShell converts a property-shaped hashtable to a class instance. That is a real
# conversion, unlike the old behaviour of reading absent properties off any object.
([PSSemVer]'1.0.0').CompareTo(@{ Major = 9; Minor = 0; Patch = 0 }) | Should -BeLessThan 0
}
It 'CompareTo throws on a garbage string' {
{ ([PSSemVer]'1.0.0').CompareTo('not-a-version') } | Should -Throw
}
It 'CompareTo sorts $null before any version' {
([PSSemVer]'1.0.0').CompareTo($null) | Should -Be 1
}
It 'CompareTo still accepts a convertible version string' {
([PSSemVer]'1.0.0').CompareTo('1.0.1') | Should -BeLessThan 0
}
It 'Equals returns false for a value that cannot be a version' {
([PSSemVer]'1.0.0').Equals([guid]::NewGuid()) | Should -BeFalse
}
It 'Equals returns false for a garbage string' {
([PSSemVer]'1.0.0').Equals('not-a-version') | Should -BeFalse
}
It 'Equals returns false for $null' {
([PSSemVer]'1.0.0').Equals($null) | Should -BeFalse
}
It '-eq against an equal version string still returns true' {
[PSSemVer]'1.0.0' -eq '1.0.0' | Should -BeTrue
}
It '-eq against a different version string returns false' {
[PSSemVer]'1.0.0' -eq '1.0.1' | Should -BeFalse
}
It '-lt against a version string still compares' {
[PSSemVer]'1.0.0' -lt '1.0.1' | Should -BeTrue
}
It 'Sort-Object still orders PSSemVer objects' {
$sorted = @([PSSemVer]'1.0.0', [PSSemVer]'0.9.0') | Sort-Object
$sorted[0].ToString() | Should -Be '0.9.0'
}
}
It "'1.2.3' < '1.2.4'" {
$PSSemVer1 = [PSSemVer]::Parse('1.2.3')
$PSSemVer2 = [PSSemVer]::Parse('1.2.4')
Expand Down
Loading