diff --git a/Package.swift b/Package.swift index a3406f2..d53edfe 100644 --- a/Package.swift +++ b/Package.swift @@ -9,6 +9,7 @@ let package = Package( .macOS(.v10_10), .iOS(.v11), .tvOS(.v11), + .watchOS(.v4), ], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. diff --git a/README.md b/README.md index 76f52dd..f7b6aa1 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,22 @@ # Swift Semantic Versioning # -A small library that implements [SemVer 2.0.0](https://semver.org/spec/v2.0.0.html), which is copyrighted to Tom Preston-Werner CC BY 3.0. This is designed to be simple to use and to easily fit into any Swift codebase. +A small library that perfectly implements [SemVer 2.0.0](https://semver.org/spec/v2.0.0.html), which is copyrighted to Tom Preston-Werner CC BY 3.0. This is designed to be simple to use and to easily fit into any Swift codebase. This is designed to be as easy as possible to use. You may use the extremely-verbose and explicit initializer which labels every parameter, or the one that does the same thing but excludes the labels, or the one that simply takes any valid SemVer string and gently fails to `nil` if that string is invalid. You are encouraged to use whichever one of these suits the needs at the time of use. Some examples are included in the unit tests. -Keep in mind that the pre-release and build extensions can be easily represented as string- and integer-literals. For instance, `SemVer(1,2,0, SemVer.Build(identifiers: ["123", "4"]))` has the same result as `SemVer(1,2,0, "123.4")` and `SemVer(1,2,0, [123,4])`. Again, this is done for ease-of-use. `SemVer` itself would also be expressible by a string literal, but it has too many resrictions so a failable initializer is presented instead. +Keep in mind that the pre-release and build extensions can be easily represented as string- and integer-literals. For instance, `SemVer(1,2,0, build: SemVer.Build(identifiers: ["123", "4"]))` has the same result as `SemVer(1,2,0, build: "123.4")` and `SemVer(1,2,0, build: [123,4])`. Again, this is done for ease-of-use. `SemVer` itself would also be expressible by a string literal, but it has too many resrictions so a failable initializer conforming to `LosslessStringConvertible` is presented instead, just like `Int`. -This also already conforms to `Comparable`, since comparison and precedence are a major part of the spec. +This also already conforms to `Comparable` since comparison and precedence are a major part of the spec, and to `Codable` since the encoding of semantic versions is clear and simple. -# Examples # + +## Proven Strong ## + +[`500` test assertions](./Tests/SemVerTests) prove that this library behaves precisely as described, conforming completely to the SemVer 2.0.0 spec. + + + +## Examples ## + Let's say you have a release candidate of version 2.0.0 of your app. The following are all equivalent: ```swift @@ -34,21 +42,24 @@ a `nil` object: nil == SemVer("Obviously Bad") nil == SemVer("1") nil == SemVer("1.2") -nil == SemVer("-2.0") +nil == SemVer("-2.0.0") nil == SemVer("2.0-β") nil == SemVer("2.0-beta_1") nil == SemVer("1.-2") nil == SemVer("1.2.-3") nil == SemVer("1.2.3.4") +nil == SemVer("1.2.3-😱") ``` -# License # + +## License ## + This is licensed under [BH-1-PS](https://github.com/BlueHuskyStudios/Licenses/blob/master/Licenses/BH-1-PS.txt). -# Requirements # +## Requirements ## This package requires: - Swift 5.1 or newer diff --git a/Sources/SemVer/Semantic Version + Codable.swift b/Sources/SemVer/Semantic Version + Codable.swift index c63a395..63571e2 100644 --- a/Sources/SemVer/Semantic Version + Codable.swift +++ b/Sources/SemVer/Semantic Version + Codable.swift @@ -3,6 +3,7 @@ // SemVer // // Created by Ky Leggiero on 2021-10-18. +// Copyright © 2021 Ben "Ky" Leggiero BH-1-PS. // import Foundation diff --git a/Sources/SemVer/Semantic Version + Comparable.swift b/Sources/SemVer/Semantic Version + Comparable.swift new file mode 100644 index 0000000..a38eb06 --- /dev/null +++ b/Sources/SemVer/Semantic Version + Comparable.swift @@ -0,0 +1,127 @@ +// +// Semantic Version + Comparable.swift +// SemVer +// +// Created by Ky Leggiero on 2021-10-25. +// Copyright © 2021 Ben "Ky" Leggiero BH-1-PS. +// + +import Foundation + + + +extension SemanticVersion: Comparable { + + /// All of the fields of this `SemanticVersion` that should be used for comparison, + /// in order so that the first one takes the highest precedence + public var orderedComparableIdentifiers: [Identifier] { + var orderedComparableFields: [Identifier] = [major, minor, patch] + if let preRelease = preRelease { + orderedComparableFields.append(preRelease) + } + return orderedComparableFields + } + + + /// Implements Semantic Version precedence + /// + /// https://semver.org/spec/v2.0.0.html#spec-item-11 + /// + /// - Parameters: + /// - lhs: The first semantic version to compare + /// - rhs: The second semantic version to compare + /// + /// - Returns: `true` iff the left has lower precedence than the right + public static func <(lhs: SemanticVersion, rhs: SemanticVersion) -> Bool { + if lhs.major < rhs.major + || (lhs.major == rhs.major && lhs.minor < rhs.minor) + || (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch) + { + return true + } + + if let lhsPreRelease = lhs.preRelease { + if let rhsPreRelease = rhs.preRelease { + return lhsPreRelease < rhsPreRelease + } + else { + return true + } + } + else { + return false + } + } + + + /// Determines whether the given two semantic versions are equivalent. Equivalence is implied by the precedence + /// rules laid out in SemVer 2.0.0 paragraph 11: https://semver.org/spec/v2.0.0.html#spec-item-11 + /// + /// Remember that build metadata does not factor into equality. For example, `"1.2.3+45" == "1.2.3+67"`. + /// + /// - Parameters: + /// - lhs: The first version to compare + /// - rhs: The second version to compare + /// + /// - Returns: `true` if the two versions are equivalent + public static func ==(lhs: SemanticVersion, rhs: SemanticVersion) -> Bool { + return lhs.major == rhs.major + && lhs.minor == rhs.minor + && lhs.patch == rhs.patch + && isEquivalent(lhs.preRelease, rhs.preRelease, isEquivalentToNil: { $0 == "" }) + // According to https://semver.org/spec/v2.0.0.html#spec-item-11, "Build metadata does not figure into precedence" + } + + + /// Compares this semantic version to the other, ignoring the given set of identifiers + /// + /// - Parameters: + /// - other: The other version to compare this one against + /// - ignoring: The identifiers of each version to be ignored in this comparison + /// + /// - Returns: `true` iff this and the given versions match when ignoring the specified identifiers + public func matches(_ other: SemVer, ignoring ignoredIdentifiers: IgnorableIdentifiers) -> Bool { + self.removing(ignoredIdentifiers) + == other.removing(ignoredIdentifiers) + } + + + + /// Returns a copy of this version where the specified identifiers are removed or set to `0` + /// + /// - Parameter ignoredIdentifiers: The identifiers to remove or set to `0` + /// - Returns: A copy of this version without the specified identifiers + private func removing(_ ignoredIdentifiers: IgnorableIdentifiers) -> Self { + var copy = self + + switch ignoredIdentifiers { + case .minorAndPatchAndPreRelease: + copy._minor = 0 + fallthrough + + case .patchAndPreRelease: + copy._patch = 0 + fallthrough + + case .preRelease: + copy.preRelease = nil + } + + return copy + } + + + + /// An identifier which can be ignored + public enum IgnorableIdentifiers { + + /// Ignore the pre-release identifier (treat `1.2.3-Beta` as equal to `1.2.3`) + case preRelease + + /// Ignore the patch and pre-release identifiers (treat `1.2.3` as equal to `1.2.99`; treat `1.2.3-Beta` as equal to `1.2.0`) + case patchAndPreRelease + + /// Ignore the minor, patch, and pre-release identifiers (treat `1.2.3` as equal to `1.99.42`; treat `1.2.3` as equal to `1.2.99`; treat `1.2.3-Beta` as equal to `1.2.0`) + case minorAndPatchAndPreRelease + } +} diff --git a/Sources/SemVer/Semantic Version.swift b/Sources/SemVer/Semantic Version.swift index 18cd781..58a791d 100644 --- a/Sources/SemVer/Semantic Version.swift +++ b/Sources/SemVer/Semantic Version.swift @@ -45,10 +45,10 @@ public struct SemanticVersion { private var _major: Major /// Holds the raw, unmanaged value for `minor` - private var _minor: Minor + internal var _minor: Minor /// Holds the raw, unmanaged value for `patch` - private var _patch: Patch + internal var _patch: Patch /// The MAJOR version; increment this when you make incompatible API changes. @@ -441,71 +441,6 @@ extension SemanticVersion: LosslessStringConvertible { -extension SemanticVersion: Comparable { - - /// All of the fields of this `SemanticVersion` that should be used for comparison, - /// in order so that the first one takes the highest precedence - public var orderedComparableIdentifiers: [Identifier] { - var orderedComparableFields: [Identifier] = [major, minor, patch] - if let preRelease = preRelease { - orderedComparableFields.append(preRelease) - } - return orderedComparableFields - } - - - /// Implements Semantic Version precedence - /// - /// https://semver.org/spec/v2.0.0.html#spec-item-11 - /// - /// - Parameters: - /// - lhs: The first semantic version to compare - /// - rhs: The second semantic version to compare - /// - /// - Returns: `true` iff the left has lower precedence than the right - public static func <(lhs: SemanticVersion, rhs: SemanticVersion) -> Bool { - if lhs.major < rhs.major - || (lhs.major == rhs.major && lhs.minor < rhs.minor) - || (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch) - { - return true - } - - if let lhsPreRelease = lhs.preRelease { - if let rhsPreRelease = rhs.preRelease { - return lhsPreRelease < rhsPreRelease - } - else { - return true - } - } - else { - return false - } - } - - - /// Determines whether the given two semantic versions are equivalent. Equivalence is implied by the precedence - /// rules laid out in SemVer 2.0.0 paragraph 11: https://semver.org/spec/v2.0.0.html#spec-item-11 - /// - /// Remember that build metadata does not factor into equality. For example, `"1.2.3+45" == "1.2.3+67"`. - /// - /// - Parameters: - /// - lhs: The first version to compare - /// - rhs: The second version to compare - /// - /// - Returns: `true` if the two versions are equivalent - public static func ==(lhs: SemanticVersion, rhs: SemanticVersion) -> Bool { - return lhs.major == rhs.major - && lhs.minor == rhs.minor - && lhs.patch == rhs.patch - && isEquivalent(lhs.preRelease, rhs.preRelease, isEquivalentToNil: { $0 == "" }) - // According to https://semver.org/spec/v2.0.0.html#spec-item-11, "Build metadata does not figure into precedence" - } -} - - - /// The PRE-RELEASE extension; this identifies versions that are available before being declared stable. /// /// Identifiers MUST comprise only ASCII alphanumerics and hyphen `[0-9A-Za-z-]`. diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index f365bd2..2a4bcf7 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -6,4 +6,6 @@ isTesting = true XCTMain(SemVerTests.allTests + SemVerHashableTests.allTests - + SemVerMutationTests.allTests) + + SemVerMutationTests.allTests + + SemVerCodableTests.allTests + + SemVerComparableTests.allTests) diff --git a/Tests/SemVerTests/SemVer+Codable Tests.swift b/Tests/SemVerTests/SemVer+Codable Tests.swift index 866f21a..4641bc2 100644 --- a/Tests/SemVerTests/SemVer+Codable Tests.swift +++ b/Tests/SemVerTests/SemVer+Codable Tests.swift @@ -1,6 +1,6 @@ // // SemVer+Codable Tests.swift -// +// SemVerTests // // Created by Ky Leggiero on 2021-10-18. // @@ -89,6 +89,14 @@ class SemVerCodableTests: SemVerTestClass { XCTAssertEqual(SemVer(01,2,3, preRelease: ["RC","4"], build: [567])!, try encodeDecode(SemVer(01,2,3, preRelease: ["RC","4"], build: [567])!)) XCTAssertEqual(SemVer("1.2.3-RC.4+567")!, try encodeDecode(SemVer("1.2.3-RC.4+567")!)) } + + + + static let allTests = [ + ("testEncode", testEncode), + ("testDecode", testDecode), + ("testEncodeDecode", testEncodeDecode), + ] } diff --git a/Tests/SemVerTests/SemVer+Comparable Tests.swift b/Tests/SemVerTests/SemVer+Comparable Tests.swift new file mode 100644 index 0000000..d94f4ac --- /dev/null +++ b/Tests/SemVerTests/SemVer+Comparable Tests.swift @@ -0,0 +1,249 @@ +// +// SemVer+Comparable Tests.swift +// SemVerTests +// +// Created by Ky Leggiero on 2021-10-25. +// + +import XCTest +import SemVer + + + +class SemVerComparableTests: SemVerTestClass { + + // MARK: - Precedence + + func testPrecedence() { + + // MARK: Less than + + XCTAssertLessThan(SemVer("0.0.1")!, SemVer("0.1.0")!) + XCTAssertLessThan(SemVer("0.0.99999")!, SemVer("0.1.0")!) + XCTAssertLessThan(SemVer("0.0.1")!, SemVer("1.0.0")!) + XCTAssertLessThan(SemVer("0.1.0")!, SemVer("1.0.0")!) + XCTAssertLessThan(SemVer("0.0.1")!, SemVer("1.1.0")!) + XCTAssertLessThan(SemVer("0.1.0")!, SemVer("1.1.0")!) + XCTAssertLessThan(SemVer("0.1.1")!, SemVer("1.1.0")!) + XCTAssertLessThan(SemVer("1.0.0")!, SemVer("2.0.0")!) + XCTAssertLessThan(SemVer("2.0.0")!, SemVer("2.1.0")!) + XCTAssertLessThan(SemVer("2.1.0")!, SemVer("2.1.1")!) + XCTAssertLessThan(SemVer("2.0.0")!, SemVer("12.0.0")!) + + XCTAssertLessThan(SemVer("1.0.0-alpha")!, SemVer("1.0.0")!) + XCTAssertLessThan(SemVer("1.0.0-alpha")!, SemVer("1.0.0-alpha.1")!) + XCTAssertLessThan(SemVer("1.0.0-alpha.1")!, SemVer("1.0.0-alpha.beta")!) + XCTAssertLessThan(SemVer("1.0.0-alpha.1")!, SemVer("1.0.0-beta")!) + XCTAssertLessThan(SemVer("1.0.0-beta")!, SemVer("1.0.0-beta.2")!) + XCTAssertLessThan(SemVer("1.0.0-beta.2")!, SemVer("1.0.0-beta.11")!) + XCTAssertLessThan(SemVer("1.0.0-beta.11")!, SemVer("1.0.0-rc.1")!) + XCTAssertLessThan(SemVer("1.0.0-rc.1")!, SemVer("1.0.0")!) + + // MARK: Greater than + + XCTAssertGreaterThan(SemVer("0.1.0")!, SemVer("0.0.1")!) + XCTAssertGreaterThan(SemVer("0.1.0")!, SemVer("0.0.99999")!) + XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("0.0.1")!) + XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("0.1.0")!) + XCTAssertGreaterThan(SemVer("1.1.0")!, SemVer("0.0.1")!) + XCTAssertGreaterThan(SemVer("1.1.0")!, SemVer("0.1.0")!) + XCTAssertGreaterThan(SemVer("1.1.0")!, SemVer("0.1.1")!) + XCTAssertGreaterThan(SemVer("2.0.0")!, SemVer("1.0.0")!) + XCTAssertGreaterThan(SemVer("2.1.0")!, SemVer("2.0.0")!) + XCTAssertGreaterThan(SemVer("2.1.1")!, SemVer("2.1.0")!) + XCTAssertGreaterThan(SemVer("12.0.0")!, SemVer("2.0.0")!) + + XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("1.0.0-alpha")!) + XCTAssertGreaterThan(SemVer("1.0.0-alpha.1")!, SemVer("1.0.0-alpha")!) + XCTAssertGreaterThan(SemVer("1.0.0-alpha.beta")!, SemVer("1.0.0-alpha.1")!) + XCTAssertGreaterThan(SemVer("1.0.0-beta")!, SemVer("1.0.0-alpha.1")!) + XCTAssertGreaterThan(SemVer("1.0.0-beta.2")!, SemVer("1.0.0-beta")!) + XCTAssertGreaterThan(SemVer("1.0.0-beta.11")!, SemVer("1.0.0-beta.2")!) + XCTAssertGreaterThan(SemVer("1.0.0-rc.1")!, SemVer("1.0.0-beta.11")!) + XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("1.0.0-rc.1")!) + + + // MARK: < + + XCTAssertTrue(SemVer("0.0.1")! < SemVer("0.1.0")!) + XCTAssertTrue(SemVer("0.0.99999")! < SemVer("0.1.0")!) + XCTAssertTrue(SemVer("0.0.1")! < SemVer("1.0.0")!) + XCTAssertTrue(SemVer("0.1.0")! < SemVer("1.0.0")!) + XCTAssertTrue(SemVer("0.0.1")! < SemVer("1.1.0")!) + XCTAssertTrue(SemVer("0.1.0")! < SemVer("1.1.0")!) + XCTAssertTrue(SemVer("0.1.1")! < SemVer("1.1.0")!) + XCTAssertTrue(SemVer("1.0.0")! < SemVer("2.0.0")!) + XCTAssertTrue(SemVer("2.0.0")! < SemVer("2.1.0")!) + XCTAssertTrue(SemVer("2.1.0")! < SemVer("2.1.1")!) + XCTAssertTrue(SemVer("2.0.0")! < SemVer("12.0.0")!) + + XCTAssertFalse(SemVer("0.0.1")! > SemVer("0.1.0")!) + XCTAssertFalse(SemVer("0.0.99999")! > SemVer("0.1.0")!) + XCTAssertFalse(SemVer("0.0.1")! > SemVer("1.0.0")!) + XCTAssertFalse(SemVer("0.1.0")! > SemVer("1.0.0")!) + XCTAssertFalse(SemVer("0.0.1")! > SemVer("1.1.0")!) + XCTAssertFalse(SemVer("0.1.0")! > SemVer("1.1.0")!) + XCTAssertFalse(SemVer("0.1.1")! > SemVer("1.1.0")!) + XCTAssertFalse(SemVer("1.0.0")! > SemVer("2.0.0")!) + XCTAssertFalse(SemVer("2.0.0")! > SemVer("2.1.0")!) + XCTAssertFalse(SemVer("2.1.0")! > SemVer("2.1.1")!) + XCTAssertFalse(SemVer("2.0.0")! > SemVer("12.0.0")!) + + XCTAssertTrue(SemVer("1.0.0-alpha")! < SemVer("1.0.0")!) + XCTAssertTrue(SemVer("1.0.0-alpha")! < SemVer("1.0.0-alpha.1")!) + XCTAssertTrue(SemVer("1.0.0-alpha.1")! < SemVer("1.0.0-alpha.beta")!) + XCTAssertTrue(SemVer("1.0.0-alpha.1")! < SemVer("1.0.0-beta")!) + XCTAssertTrue(SemVer("1.0.0-beta")! < SemVer("1.0.0-beta.2")!) + XCTAssertTrue(SemVer("1.0.0-beta.2")! < SemVer("1.0.0-beta.11")!) + XCTAssertTrue(SemVer("1.0.0-beta.11")! < SemVer("1.0.0-rc.1")!) + XCTAssertTrue(SemVer("1.0.0-rc.1")! < SemVer("1.0.0")!) + + XCTAssertFalse(SemVer("1.0.0-alpha")! > SemVer("1.0.0")!) + XCTAssertFalse(SemVer("1.0.0-alpha")! > SemVer("1.0.0-alpha.1")!) + XCTAssertFalse(SemVer("1.0.0-alpha.1")! > SemVer("1.0.0-alpha.beta")!) + XCTAssertFalse(SemVer("1.0.0-alpha.1")! > SemVer("1.0.0-beta")!) + XCTAssertFalse(SemVer("1.0.0-beta")! > SemVer("1.0.0-beta.2")!) + XCTAssertFalse(SemVer("1.0.0-beta.2")! > SemVer("1.0.0-beta.11")!) + XCTAssertFalse(SemVer("1.0.0-beta.11")! > SemVer("1.0.0-rc.1")!) + XCTAssertFalse(SemVer("1.0.0-rc.1")! > SemVer("1.0.0")!) + + // MARK: > + + XCTAssertTrue(SemVer("0.1.0")! > SemVer("0.0.1")!) + XCTAssertTrue(SemVer("0.1.0")! > SemVer("0.0.99999")!) + XCTAssertTrue(SemVer("1.0.0")! > SemVer("0.0.1")!) + XCTAssertTrue(SemVer("1.0.0")! > SemVer("0.1.0")!) + XCTAssertTrue(SemVer("1.1.0")! > SemVer("0.0.1")!) + XCTAssertTrue(SemVer("1.1.0")! > SemVer("0.1.0")!) + XCTAssertTrue(SemVer("1.1.0")! > SemVer("0.1.1")!) + XCTAssertTrue(SemVer("2.0.0")! > SemVer("1.0.0")!) + XCTAssertTrue(SemVer("2.1.0")! > SemVer("2.0.0")!) + XCTAssertTrue(SemVer("2.1.1")! > SemVer("2.1.0")!) + XCTAssertTrue(SemVer("12.0.0")! > SemVer("2.0.0")!) + + XCTAssertFalse(SemVer("0.1.0")! < SemVer("0.0.1")!) + XCTAssertFalse(SemVer("0.1.0")! < SemVer("0.0.99999")!) + XCTAssertFalse(SemVer("1.0.0")! < SemVer("0.0.1")!) + XCTAssertFalse(SemVer("1.0.0")! < SemVer("0.1.0")!) + XCTAssertFalse(SemVer("1.1.0")! < SemVer("0.0.1")!) + XCTAssertFalse(SemVer("1.1.0")! < SemVer("0.1.0")!) + XCTAssertFalse(SemVer("1.1.0")! < SemVer("0.1.1")!) + XCTAssertFalse(SemVer("2.0.0")! < SemVer("1.0.0")!) + XCTAssertFalse(SemVer("2.1.0")! < SemVer("2.0.0")!) + XCTAssertFalse(SemVer("2.1.1")! < SemVer("2.1.0")!) + XCTAssertFalse(SemVer("12.0.0")! < SemVer("2.0.0")!) + + XCTAssertTrue(SemVer("1.0.0")! > SemVer("1.0.0-alpha")!) + XCTAssertTrue(SemVer("1.0.0-alpha.1")! > SemVer("1.0.0-alpha")!) + XCTAssertTrue(SemVer("1.0.0-alpha.beta")! > SemVer("1.0.0-alpha.1")!) + XCTAssertTrue(SemVer("1.0.0-beta")! > SemVer("1.0.0-alpha.1")!) + XCTAssertTrue(SemVer("1.0.0-beta.2")! > SemVer("1.0.0-beta")!) + XCTAssertTrue(SemVer("1.0.0-beta.11")! > SemVer("1.0.0-beta.2")!) + XCTAssertTrue(SemVer("1.0.0-rc.1")! > SemVer("1.0.0-beta.11")!) + XCTAssertTrue(SemVer("1.0.0")! > SemVer("1.0.0-rc.1")!) + + XCTAssertFalse(SemVer("1.0.0")! < SemVer("1.0.0-alpha")!) + XCTAssertFalse(SemVer("1.0.0-alpha.1")! < SemVer("1.0.0-alpha")!) + XCTAssertFalse(SemVer("1.0.0-alpha.beta")! < SemVer("1.0.0-alpha.1")!) + XCTAssertFalse(SemVer("1.0.0-beta")! < SemVer("1.0.0-alpha.1")!) + XCTAssertFalse(SemVer("1.0.0-beta.2")! < SemVer("1.0.0-beta")!) + XCTAssertFalse(SemVer("1.0.0-beta.11")! < SemVer("1.0.0-beta.2")!) + XCTAssertFalse(SemVer("1.0.0-rc.1")! < SemVer("1.0.0-beta.11")!) + XCTAssertFalse(SemVer("1.0.0")! < SemVer("1.0.0-rc.1")!) + + + // MARK: Proof of fix of #7 + // https://github.com/RougeWare/Swift-SemVer/issues/7 + XCTAssertTrue(SemVer(10,0,0)! < SemVer(11,0,0)!) + XCTAssertTrue(SemVer(11,0,0)! > SemVer(10,0,0)!) + + XCTAssertFalse(SemVer(10,0,0)! > SemVer(11,0,0)!) + XCTAssertFalse(SemVer(11,0,0)! < SemVer(10,0,0)!) + } + + + + // MARK: - Equivalence + + func testEquivalence() { + XCTAssertEqual(SemVer(major: 1, minor: 0, patch: 0), SemVer(major: 1, minor: 0, patch: 0)) + XCTAssertEqual(SemVer("1.0.0"), SemVer(major: 1, minor: 0, patch: 0)) + XCTAssertEqual(SemVer("1.0.0"), SemVer("1.0.0")) + // According to https://semver.org/spec/v2.0.0.html#spec-item-11, "Build metadata does not figure into precedence" + XCTAssertEqual(SemVer("1.0+543"), SemVer("1.0+345")) + XCTAssertEqual(SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1]), SemVer(2,0,0, preRelease: ["RC",1])) + XCTAssertEqual(SemVer(2,0,0, preRelease: ["RC",1]), SemVer("2.0.0-RC.1")) + XCTAssertEqual(SemVer("2.0.0-RC.1"), SemVer("2.0.0-RC.1")) + XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: 543)) + XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: "543")) + XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: [543])) + XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: ["543"])) + } + + + func testAdvancedEquality() { + XCTAssertEqual(SemVer(1,2,3), SemVer("1.2.3")) + XCTAssertEqual(SemVer(1,2,3), SemVer("1.2.3+12")) + XCTAssertEqual(SemVer(1,2,3, build: 12), SemVer("1.2.3")) + XCTAssertEqual(SemVer(1,2,3, build: 12), SemVer(1,2,3)) + XCTAssertEqual(SemVer(1,2,3, build: 12), SemVer("1.2.3+12")) + } + + + + // MARK: - Ignoring components + + // MARK: Pre-Release + + func testCompareIgnoringPreRelease() { + XCTAssertTrue(SemVer(0,0,3).matches(SemVer(0,0,3), ignoring: .preRelease)) + XCTAssertTrue(SemVer(0,2,3).matches(SemVer(0,2,3), ignoring: .preRelease)) + XCTAssertTrue(SemVer(1,2,3).matches(SemVer(1,2,3), ignoring: .preRelease)) + XCTAssertTrue(SemVer("1.2.3-Beta")!.matches(SemVer(1,2,3), ignoring: .preRelease)) + XCTAssertTrue(SemVer("1.2.3-Beta")!.matches(SemVer("1.2.3-Alpha")!, ignoring: .preRelease)) + } + + + func testCompareIgnoringPatch() { + XCTAssertTrue(SemVer(0,0,3).matches(SemVer(0,0,3), ignoring: .patchAndPreRelease)) + XCTAssertTrue(SemVer(0,2,3).matches(SemVer(0,2,3), ignoring: .patchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3).matches(SemVer(1,2,3), ignoring: .patchAndPreRelease)) + + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,0), ignoring: .patchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,3), ignoring: .patchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,99), ignoring: .patchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,99, preRelease: "Beta")!.matches(SemVer(1,2,3), ignoring: .patchAndPreRelease)) + + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,0, preRelease: "Alpha")!, ignoring: .patchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,3, preRelease: "Alpha")!, ignoring: .patchAndPreRelease)) + } + + + func testCompareIgnoringMinor() { + XCTAssertTrue(SemVer(0,0,3).matches(SemVer(0,0,3), ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(0,2,3).matches(SemVer(0,2,3), ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3).matches(SemVer(1,2,3), ignoring: .minorAndPatchAndPreRelease)) + + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,0,0), ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,0), ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,3), ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,99,99), ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(1,99,99, preRelease: "Beta")!.matches(SemVer(1,2,3), ignoring: .minorAndPatchAndPreRelease)) + + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,0,0, preRelease: "Alpha")!, ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,0, preRelease: "Alpha")!, ignoring: .minorAndPatchAndPreRelease)) + XCTAssertTrue(SemVer(1,2,3, preRelease: "Beta")!.matches(SemVer(1,2,3, preRelease: "Alpha")!, ignoring: .minorAndPatchAndPreRelease)) + } + + + + static let allTests = [ + ("testPrecedence", testPrecedence), + ("testEquivalence", testEquivalence), + ("testAdvancedEquality", testAdvancedEquality), + ("testCompareIgnoringPreRelease", testCompareIgnoringPreRelease), + ("testCompareIgnoringPatch", testCompareIgnoringPatch), + ("testCompareIgnoringMinor", testCompareIgnoringMinor), + ] +} + diff --git a/Tests/SemVerTests/SemVer+Hashable Tests.swift b/Tests/SemVerTests/SemVer+Hashable Tests.swift index e1cfa38..53d0f2b 100644 --- a/Tests/SemVerTests/SemVer+Hashable Tests.swift +++ b/Tests/SemVerTests/SemVer+Hashable Tests.swift @@ -1,6 +1,6 @@ // // SemVer+Hashable Tests.swift -// SemVer +// SemVerTests // // Created by Ben Leggiero on 2020-04-19. // Copyright © 2021 Ben Leggiero BH-1-PS. diff --git a/Tests/SemVerTests/SemVerTests.swift b/Tests/SemVerTests/SemVerTests.swift index be3f6ac..8fbbbe9 100644 --- a/Tests/SemVerTests/SemVerTests.swift +++ b/Tests/SemVerTests/SemVerTests.swift @@ -41,169 +41,6 @@ class SemVerTests: SemVerTestClass { // MARK: - Precedence - func testPrecedence() { - - // MARK: Less than - - XCTAssertLessThan(SemVer("0.0.1")!, SemVer("0.1.0")!) - XCTAssertLessThan(SemVer("0.0.99999")!, SemVer("0.1.0")!) - XCTAssertLessThan(SemVer("0.0.1")!, SemVer("1.0.0")!) - XCTAssertLessThan(SemVer("0.1.0")!, SemVer("1.0.0")!) - XCTAssertLessThan(SemVer("0.0.1")!, SemVer("1.1.0")!) - XCTAssertLessThan(SemVer("0.1.0")!, SemVer("1.1.0")!) - XCTAssertLessThan(SemVer("0.1.1")!, SemVer("1.1.0")!) - XCTAssertLessThan(SemVer("1.0.0")!, SemVer("2.0.0")!) - XCTAssertLessThan(SemVer("2.0.0")!, SemVer("2.1.0")!) - XCTAssertLessThan(SemVer("2.1.0")!, SemVer("2.1.1")!) - XCTAssertLessThan(SemVer("2.0.0")!, SemVer("12.0.0")!) - - XCTAssertLessThan(SemVer("1.0.0-alpha")!, SemVer("1.0.0")!) - XCTAssertLessThan(SemVer("1.0.0-alpha")!, SemVer("1.0.0-alpha.1")!) - XCTAssertLessThan(SemVer("1.0.0-alpha.1")!, SemVer("1.0.0-alpha.beta")!) - XCTAssertLessThan(SemVer("1.0.0-alpha.1")!, SemVer("1.0.0-beta")!) - XCTAssertLessThan(SemVer("1.0.0-beta")!, SemVer("1.0.0-beta.2")!) - XCTAssertLessThan(SemVer("1.0.0-beta.2")!, SemVer("1.0.0-beta.11")!) - XCTAssertLessThan(SemVer("1.0.0-beta.11")!, SemVer("1.0.0-rc.1")!) - XCTAssertLessThan(SemVer("1.0.0-rc.1")!, SemVer("1.0.0")!) - - // MARK: Greater than - - XCTAssertGreaterThan(SemVer("0.1.0")!, SemVer("0.0.1")!) - XCTAssertGreaterThan(SemVer("0.1.0")!, SemVer("0.0.99999")!) - XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("0.0.1")!) - XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("0.1.0")!) - XCTAssertGreaterThan(SemVer("1.1.0")!, SemVer("0.0.1")!) - XCTAssertGreaterThan(SemVer("1.1.0")!, SemVer("0.1.0")!) - XCTAssertGreaterThan(SemVer("1.1.0")!, SemVer("0.1.1")!) - XCTAssertGreaterThan(SemVer("2.0.0")!, SemVer("1.0.0")!) - XCTAssertGreaterThan(SemVer("2.1.0")!, SemVer("2.0.0")!) - XCTAssertGreaterThan(SemVer("2.1.1")!, SemVer("2.1.0")!) - XCTAssertGreaterThan(SemVer("12.0.0")!, SemVer("2.0.0")!) - - XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("1.0.0-alpha")!) - XCTAssertGreaterThan(SemVer("1.0.0-alpha.1")!, SemVer("1.0.0-alpha")!) - XCTAssertGreaterThan(SemVer("1.0.0-alpha.beta")!, SemVer("1.0.0-alpha.1")!) - XCTAssertGreaterThan(SemVer("1.0.0-beta")!, SemVer("1.0.0-alpha.1")!) - XCTAssertGreaterThan(SemVer("1.0.0-beta.2")!, SemVer("1.0.0-beta")!) - XCTAssertGreaterThan(SemVer("1.0.0-beta.11")!, SemVer("1.0.0-beta.2")!) - XCTAssertGreaterThan(SemVer("1.0.0-rc.1")!, SemVer("1.0.0-beta.11")!) - XCTAssertGreaterThan(SemVer("1.0.0")!, SemVer("1.0.0-rc.1")!) - - - // MARK: < - - XCTAssertTrue(SemVer("0.0.1")! < SemVer("0.1.0")!) - XCTAssertTrue(SemVer("0.0.99999")! < SemVer("0.1.0")!) - XCTAssertTrue(SemVer("0.0.1")! < SemVer("1.0.0")!) - XCTAssertTrue(SemVer("0.1.0")! < SemVer("1.0.0")!) - XCTAssertTrue(SemVer("0.0.1")! < SemVer("1.1.0")!) - XCTAssertTrue(SemVer("0.1.0")! < SemVer("1.1.0")!) - XCTAssertTrue(SemVer("0.1.1")! < SemVer("1.1.0")!) - XCTAssertTrue(SemVer("1.0.0")! < SemVer("2.0.0")!) - XCTAssertTrue(SemVer("2.0.0")! < SemVer("2.1.0")!) - XCTAssertTrue(SemVer("2.1.0")! < SemVer("2.1.1")!) - XCTAssertTrue(SemVer("2.0.0")! < SemVer("12.0.0")!) - - XCTAssertFalse(SemVer("0.0.1")! > SemVer("0.1.0")!) - XCTAssertFalse(SemVer("0.0.99999")! > SemVer("0.1.0")!) - XCTAssertFalse(SemVer("0.0.1")! > SemVer("1.0.0")!) - XCTAssertFalse(SemVer("0.1.0")! > SemVer("1.0.0")!) - XCTAssertFalse(SemVer("0.0.1")! > SemVer("1.1.0")!) - XCTAssertFalse(SemVer("0.1.0")! > SemVer("1.1.0")!) - XCTAssertFalse(SemVer("0.1.1")! > SemVer("1.1.0")!) - XCTAssertFalse(SemVer("1.0.0")! > SemVer("2.0.0")!) - XCTAssertFalse(SemVer("2.0.0")! > SemVer("2.1.0")!) - XCTAssertFalse(SemVer("2.1.0")! > SemVer("2.1.1")!) - XCTAssertFalse(SemVer("2.0.0")! > SemVer("12.0.0")!) - - XCTAssertTrue(SemVer("1.0.0-alpha")! < SemVer("1.0.0")!) - XCTAssertTrue(SemVer("1.0.0-alpha")! < SemVer("1.0.0-alpha.1")!) - XCTAssertTrue(SemVer("1.0.0-alpha.1")! < SemVer("1.0.0-alpha.beta")!) - XCTAssertTrue(SemVer("1.0.0-alpha.1")! < SemVer("1.0.0-beta")!) - XCTAssertTrue(SemVer("1.0.0-beta")! < SemVer("1.0.0-beta.2")!) - XCTAssertTrue(SemVer("1.0.0-beta.2")! < SemVer("1.0.0-beta.11")!) - XCTAssertTrue(SemVer("1.0.0-beta.11")! < SemVer("1.0.0-rc.1")!) - XCTAssertTrue(SemVer("1.0.0-rc.1")! < SemVer("1.0.0")!) - - XCTAssertFalse(SemVer("1.0.0-alpha")! > SemVer("1.0.0")!) - XCTAssertFalse(SemVer("1.0.0-alpha")! > SemVer("1.0.0-alpha.1")!) - XCTAssertFalse(SemVer("1.0.0-alpha.1")! > SemVer("1.0.0-alpha.beta")!) - XCTAssertFalse(SemVer("1.0.0-alpha.1")! > SemVer("1.0.0-beta")!) - XCTAssertFalse(SemVer("1.0.0-beta")! > SemVer("1.0.0-beta.2")!) - XCTAssertFalse(SemVer("1.0.0-beta.2")! > SemVer("1.0.0-beta.11")!) - XCTAssertFalse(SemVer("1.0.0-beta.11")! > SemVer("1.0.0-rc.1")!) - XCTAssertFalse(SemVer("1.0.0-rc.1")! > SemVer("1.0.0")!) - - // MARK: > - - XCTAssertTrue(SemVer("0.1.0")! > SemVer("0.0.1")!) - XCTAssertTrue(SemVer("0.1.0")! > SemVer("0.0.99999")!) - XCTAssertTrue(SemVer("1.0.0")! > SemVer("0.0.1")!) - XCTAssertTrue(SemVer("1.0.0")! > SemVer("0.1.0")!) - XCTAssertTrue(SemVer("1.1.0")! > SemVer("0.0.1")!) - XCTAssertTrue(SemVer("1.1.0")! > SemVer("0.1.0")!) - XCTAssertTrue(SemVer("1.1.0")! > SemVer("0.1.1")!) - XCTAssertTrue(SemVer("2.0.0")! > SemVer("1.0.0")!) - XCTAssertTrue(SemVer("2.1.0")! > SemVer("2.0.0")!) - XCTAssertTrue(SemVer("2.1.1")! > SemVer("2.1.0")!) - XCTAssertTrue(SemVer("12.0.0")! > SemVer("2.0.0")!) - - XCTAssertFalse(SemVer("0.1.0")! < SemVer("0.0.1")!) - XCTAssertFalse(SemVer("0.1.0")! < SemVer("0.0.99999")!) - XCTAssertFalse(SemVer("1.0.0")! < SemVer("0.0.1")!) - XCTAssertFalse(SemVer("1.0.0")! < SemVer("0.1.0")!) - XCTAssertFalse(SemVer("1.1.0")! < SemVer("0.0.1")!) - XCTAssertFalse(SemVer("1.1.0")! < SemVer("0.1.0")!) - XCTAssertFalse(SemVer("1.1.0")! < SemVer("0.1.1")!) - XCTAssertFalse(SemVer("2.0.0")! < SemVer("1.0.0")!) - XCTAssertFalse(SemVer("2.1.0")! < SemVer("2.0.0")!) - XCTAssertFalse(SemVer("2.1.1")! < SemVer("2.1.0")!) - XCTAssertFalse(SemVer("12.0.0")! < SemVer("2.0.0")!) - - XCTAssertTrue(SemVer("1.0.0")! > SemVer("1.0.0-alpha")!) - XCTAssertTrue(SemVer("1.0.0-alpha.1")! > SemVer("1.0.0-alpha")!) - XCTAssertTrue(SemVer("1.0.0-alpha.beta")! > SemVer("1.0.0-alpha.1")!) - XCTAssertTrue(SemVer("1.0.0-beta")! > SemVer("1.0.0-alpha.1")!) - XCTAssertTrue(SemVer("1.0.0-beta.2")! > SemVer("1.0.0-beta")!) - XCTAssertTrue(SemVer("1.0.0-beta.11")! > SemVer("1.0.0-beta.2")!) - XCTAssertTrue(SemVer("1.0.0-rc.1")! > SemVer("1.0.0-beta.11")!) - XCTAssertTrue(SemVer("1.0.0")! > SemVer("1.0.0-rc.1")!) - - XCTAssertFalse(SemVer("1.0.0")! < SemVer("1.0.0-alpha")!) - XCTAssertFalse(SemVer("1.0.0-alpha.1")! < SemVer("1.0.0-alpha")!) - XCTAssertFalse(SemVer("1.0.0-alpha.beta")! < SemVer("1.0.0-alpha.1")!) - XCTAssertFalse(SemVer("1.0.0-beta")! < SemVer("1.0.0-alpha.1")!) - XCTAssertFalse(SemVer("1.0.0-beta.2")! < SemVer("1.0.0-beta")!) - XCTAssertFalse(SemVer("1.0.0-beta.11")! < SemVer("1.0.0-beta.2")!) - XCTAssertFalse(SemVer("1.0.0-rc.1")! < SemVer("1.0.0-beta.11")!) - XCTAssertFalse(SemVer("1.0.0")! < SemVer("1.0.0-rc.1")!) - - - // MARK: Proof of fix of #7 - // https://github.com/RougeWare/Swift-SemVer/issues/7 - XCTAssertTrue(SemVer(10,0,0)! < SemVer(11,0,0)!) - XCTAssertTrue(SemVer(11,0,0)! > SemVer(10,0,0)!) - - XCTAssertFalse(SemVer(10,0,0)! > SemVer(11,0,0)!) - XCTAssertFalse(SemVer(11,0,0)! < SemVer(10,0,0)!) - } - - - func testEquivalence() { - XCTAssertEqual(SemVer(major: 1, minor: 0, patch: 0), SemVer(major: 1, minor: 0, patch: 0)) - XCTAssertEqual(SemVer("1.0.0"), SemVer(major: 1, minor: 0, patch: 0)) - XCTAssertEqual(SemVer("1.0.0"), SemVer("1.0.0")) - // According to https://semver.org/spec/v2.0.0.html#spec-item-11, "Build metadata does not figure into precedence" - XCTAssertEqual(SemVer("1.0+543"), SemVer("1.0+345")) - XCTAssertEqual(SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1]), SemVer(2,0,0, preRelease: ["RC",1])) - XCTAssertEqual(SemVer(2,0,0, preRelease: ["RC",1]), SemVer("2.0.0-RC.1")) - XCTAssertEqual(SemVer("2.0.0-RC.1"), SemVer("2.0.0-RC.1")) - XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: 543)) - XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: "543")) - XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: [543])) - XCTAssertEqual(SemVer("2.0.0-RC.1+543"), SemVer(major: 2, minor: 0, patch: 0, preRelease: ["RC", 1], build: ["543"])) - } - func testInvalid() { XCTAssertNil(SemVer("Obviously Bad")) @@ -214,11 +51,14 @@ class SemVerTests: SemVerTestClass { XCTAssertNil(SemVer("1.2-RC.4+567")) XCTAssertNil(SemVer("1.2-RC+567.8")) XCTAssertNil(SemVer("-2.0")) + XCTAssertNil(SemVer("-2.0.0")) XCTAssertNil(SemVer("2.0-β")) XCTAssertNil(SemVer("2.0-beta_1")) XCTAssertNil(SemVer("1.-2")) XCTAssertNil(SemVer("1.2.-3")) XCTAssertNil(SemVer("1.2.3.4")) + XCTAssertNil(SemVer("1.2.3-😱")) + XCTAssertNil(SemVer("1.2.3-semántice")) // Proof of fix of #14: https://github.com/RougeWare/Swift-SemVer/issues/14 XCTAssertNil(SemVer(1,0,0, preRelease: "01")) @@ -231,21 +71,10 @@ class SemVerTests: SemVerTestClass { } - func testAdvancedEquality() { - XCTAssertEqual(SemVer(1,2,3), SemVer("1.2.3")) - XCTAssertEqual(SemVer(1,2,3), SemVer("1.2.3+12")) - XCTAssertEqual(SemVer(1,2,3, build: 12), SemVer("1.2.3")) - XCTAssertEqual(SemVer(1,2,3, build: 12), SemVer(1,2,3)) - XCTAssertEqual(SemVer(1,2,3, build: 12), SemVer("1.2.3+12")) - } - static let allTests = [ ("testDescription", testDescription), ("testFromString", testFromString), - ("testPrecedence", testPrecedence), - ("testEquivalence", testEquivalence), ("testInvalid", testInvalid), - ("testAdvancedEquality", testAdvancedEquality), ] } diff --git a/Tests/SemVerTests/XCTestManifests.swift b/Tests/SemVerTests/XCTestManifests.swift index 3911b09..205fc6b 100644 --- a/Tests/SemVerTests/XCTestManifests.swift +++ b/Tests/SemVerTests/XCTestManifests.swift @@ -8,6 +8,8 @@ public func allTests() -> [XCTestCaseEntry] { testCase(SemVerTests.allTests), testCase(SemVerHashableTests.allTests), testCase(SemVerMutationTests.allTests), + testCase(SemVerCodableTests.allTests), + testCase(SemVerComparableTests.allTests), ] } #endif