Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beta tag heuristic should use >= when comparing platform version information #994

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public class DocumentationContentRenderer {
}

// Verify that the current platform is in beta and the version number matches the introduced platform version.
guard current.beta && SemanticVersion(introduced).isEqualToVersionTriplet(current.version) else {
guard current.beta && SemanticVersion(introduced) >= SemanticVersion(versionTriplet: current.version) else {
return false
}
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1762,7 +1762,8 @@ public struct RenderNodeTranslator: SemanticVisitor {
// Build a module availability version, defaulting the patch number to 0 if not provided (e.g. 10.15)
let moduleVersionTriplet = VersionTriplet(moduleVersion[0], moduleVersion[1], moduleVersion.count > 2 ? moduleVersion[2] : 0)

return moduleVersionTriplet == targetPlatformVersion.version
// Consider the module beta if its version is greater than or equal to the target platform
return moduleVersionTriplet >= targetPlatformVersion.version
}

/// The default availability for modules in a given bundle and module.
Expand Down
29 changes: 27 additions & 2 deletions Sources/SwiftDocC/Model/Rendering/SemanticVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
/// A semantic version.
///
/// A version that follows the [Semantic Versioning](https://semver.org) specification.
public struct SemanticVersion: Codable, Equatable, CustomStringConvertible {
public struct SemanticVersion: Codable, Equatable, Comparable, CustomStringConvertible {

/// The major version number.
///
/// For example, the `1` in `1.2.3`
Expand Down Expand Up @@ -51,6 +51,31 @@ public struct SemanticVersion: Codable, Equatable, CustomStringConvertible {
self.buildMetadata = try container.decodeIfPresent(String.self, forKey: .buildMetadata)
}

// TODO: https://github.com/swiftlang/swift-docc/issues/970
// Migrate all the code to use semantic versions, and not version triplets.
/// Create a semantic version from a version triplet.
init(versionTriplet: VersionTriplet) {
self.major = versionTriplet.major
self.minor = versionTriplet.minor
self.patch = versionTriplet.patch
}

/// Compare one semantic version with another.
///
/// - Parameters:
/// - lhs: A version to compare.
/// - rhs: Another version to compare.
///
/// - Returns: a Boolean value that indicates whether the first version is less than the second version.
public static func < (lhs: SemanticVersion, rhs: SemanticVersion) -> Bool {
if lhs.major != rhs.major { return lhs.major < rhs.major }
if lhs.minor != rhs.minor { return lhs.minor < rhs.minor }
if lhs.patch != rhs.patch { return lhs.patch < rhs.patch }
// Note: don't compare the values of prerelease, even if it is
// present in both semantic versions.
return false // The version are equal
}

public var description: String {
var result = "\(major).\(minor).\(patch)"
if let prerelease {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,6 @@ extension SemanticVersion {
self.prerelease = semanticVersion.prerelease
self.buildMetadata = semanticVersion.buildMetadata
}

/// Compares a version triplet to a semantic version.
/// - Parameter version: A version triplet to compare to this semantic version.
/// - Returns: Returns whether the given triple represents the same version as the current version.
func isEqualToVersionTriplet(_ version: VersionTriplet) -> Bool {
return major == version.major &&
minor == version.minor &&
patch == version.patch
}
}

/// Availability information of a symbol on a specific platform.
Expand Down Expand Up @@ -154,11 +145,11 @@ public struct AvailabilityRenderItem: Codable, Hashable, Equatable {
}

private static func isBeta(introduced: SemanticVersion?, current: PlatformVersion?) -> Bool {
guard let introduced, let current, current.beta, introduced.isEqualToVersionTriplet(current.version) else {
guard let introduced, let current, current.beta else {
return false
}
return true

return introduced >= SemanticVersion(versionTriplet: current.version)
}

/// Creates a new item with the given platform name and version string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ extension Docc {
help: ArgumentHelp("Specify information about the current release of a platform.", discussion: """
Each platform's information is specified via separate "--platform" values using the following format: "name={platform name},version={semantic version}".
Optionally, the platform information can include a 'beta={true|false}' component. If no beta information is provided, the platform is considered not in beta.
If the platform is set to beta, any symbol introduced in a version equal to or greater than the specified semantic version will be marked as beta.
""")
)
var platforms: [String] = []
Expand Down
13 changes: 12 additions & 1 deletion Tests/SwiftDocCTests/Model/SemaToRenderNodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,17 @@ Document

// Beta platform matching the introduced version
context.externalMetadata.currentPlatforms = ["macOS": PlatformVersion(VersionTriplet(10, 15, 0), beta: true)]

do {
var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: reference)
let renderNode = translator.visitSymbol(symbol) as! RenderNode

// Verify platform beta was plumbed all the way to the render JSON
XCTAssertEqual(renderNode.metadata.platforms?.first?.isBeta, true)
}

// Beta platform earlier than the introduced version
context.externalMetadata.currentPlatforms = ["macOS": PlatformVersion(VersionTriplet(10, 14, 0), beta: true)]

do {
var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: reference)
Expand All @@ -1959,7 +1970,7 @@ Document
// Set only some platforms to beta & the exact version MyClass is being introduced at
context.externalMetadata.currentPlatforms = [
"macOS": PlatformVersion(VersionTriplet(10, 15, 0), beta: true),
"watchOS": PlatformVersion(VersionTriplet(3, 0, 0), beta: true),
"watchOS": PlatformVersion(VersionTriplet(9, 0, 0), beta: true),
"tvOS": PlatformVersion(VersionTriplet(1, 0, 0), beta: true),
]

Expand Down
22 changes: 21 additions & 1 deletion Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,32 @@ class DefaultAvailabilityTests: XCTestCase {
try? FileManager.default.removeItem(at: url.appendingPathComponent("Info.plist"))
try? FileManager.default.copyItem(at: self.infoPlistAvailabilityURL, to: url.appendingPathComponent("Info.plist"))
}

// Set a beta status for the docs (which would normally be set via command line argument)
context.externalMetadata.currentPlatforms = [
"macOS": PlatformVersion(VersionTriplet(10, 15, 1), beta: true),
"Mac Catalyst": PlatformVersion(VersionTriplet(13, 5, 0), beta: true),
]

// Test if the module availability is also "beta" for the "macOS" platform,
// verify that the Mac Catalyst platform's name (including a space) is rendered correctly
do {
let identifier = ResolvedTopicReference(bundleIdentifier: "org.swift.docc.example", path: "/documentation/MyKit", fragment: nil, sourceLanguage: .swift)
let node = try context.entity(with: identifier)
var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: node.reference)
let renderNode = translator.visit(node.semantic) as! RenderNode

XCTAssertEqual(renderNode.metadata.platforms?.map({ "\($0.name ?? "") \($0.introduced ?? "")\($0.isBeta == true ? "(beta)" : "")" }).sorted(), [
"Mac Catalyst 13.5(beta)",
"macOS 10.15.1(beta)",
])
}

// Repeat the assertions, but use an earlier platform version this time
context.externalMetadata.currentPlatforms = [
"macOS": PlatformVersion(VersionTriplet(10, 14, 1), beta: true),
"Mac Catalyst": PlatformVersion(VersionTriplet(13, 5, 0), beta: true),
]

// Test if the module availability is also "beta" for the "macOS" platform,
// verify that the Mac Catalyst platform's name (including a space) is rendered correctly
Expand Down