Skip to content

Commit

Permalink
[external-links] Display frameworks as beta only if all platforms are…
Browse files Browse the repository at this point in the history
… beta (swiftlang#938)

* Display frameworks as beta only if all platforms are beta

Changes the behaviour of external link resolution in `OutOfProcessReferenceResolver` to only display a framework to be in beta if all the platforms its supported in are in beta.

This makes it match the behaviour for internal link resolution, so that the behaviour is consistent whether you're linking to a symbol internally within a framework or externally from another framework.

* Refactor code to have same behaviour in both external link resolvers

Makes changes to `OutOfProcessReferenceResolver .ResolvedInformation` so that it can be replaced by `LinkDestinationSummary` at some point in the future. Both `OutOfProcessReferenceResolver .ResolvedInformation` and `LinkDestinationSummary` now contain information about whether the resolved external link points to a symbol in beta or not, using equivalent API.

This is an attempt at keeping both of the logics about whether an external symbol is in beta in sync with each other. Hopefully in the future we can unify and have the same code path for both, but right now the logic is duplicated (and equal).

* Add a test for beta logic in ExternalPathHierarchyResolver

Adds a unit test which verifies that the logic for determining whether a symbol is in beta when using `ExternalPathHierarchyResolver` is as expected.

* Fix formatting

Test file `OutOfProcessReferenceResolverTests` had inconsistent and confusing formatting, particularly with how the type of `makeResolver` was being defined across multiple lines in a way inconsistent with the rest of the codebase.

`makeResolver` is now declared as a one-liner and fixes other minor formatting/spacing issues.

* Remove noop Info.plist

The file isn't needed, so removing it.

* Remove changes to testBundleAndContext helpers

We want to avoid spreading the configure context parameter to the other test helpers, as we're slowly working to make the context not be configured after creation.

Instead, `ExternalPathHierarchyResolverTests` now calls the underlying `loadBundle(from:configureContext:)` directly.

---

Resolves rdar://128997995.
  • Loading branch information
anferbui committed Jun 13, 2024
1 parent daf46af commit 9629c9a
Show file tree
Hide file tree
Showing 5 changed files with 462 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public class OutOfProcessReferenceResolver: ExternalDocumentationSource, GlobalE
kind: kind,
role: role,
fragments: resolvedInformation.declarationFragments?.declarationFragments.map { DeclarationRenderSection.Token(fragment: $0, identifier: nil) },
isBeta: (resolvedInformation.platforms ?? []).contains(where: { $0.isBeta == true }),
isBeta: resolvedInformation.isBeta,
isDeprecated: (resolvedInformation.platforms ?? []).contains(where: { $0.deprecated != nil }),
images: resolvedInformation.topicImages ?? []
)
Expand Down Expand Up @@ -587,6 +587,15 @@ extension OutOfProcessReferenceResolver {

/// The variants of content (kind, url, title, abstract, language, declaration) for this resolver information.
public var variants: [Variant]?

/// A value that indicates whether this symbol is under development and likely to change.
var isBeta: Bool {
guard let platforms, !platforms.isEmpty else {
return false
}

return platforms.allSatisfy { $0.isBeta == true }
}

/// Creates a new resolved information value with all its values.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ private extension Sequence<DeclarationRenderSection.Token> {
// MARK: ExternalEntity

private extension LinkDestinationSummary {
/// A value that indicates whether this symbol is under development and likely to change.
var isBeta: Bool {
guard let platforms, !platforms.isEmpty else {
return false
}

return platforms.allSatisfy { $0.isBeta == true }
}

/// Create a topic render render reference for this link summary and its content variants.
func topicRenderReference() -> TopicRenderReference {
let (kind, role) = DocumentationContentRenderer.renderKindAndRole(kind, semantic: nil)
Expand Down Expand Up @@ -215,7 +224,7 @@ private extension LinkDestinationSummary {
navigatorTitleVariants: .init(defaultValue: nil),
estimatedTime: nil,
conformance: nil,
isBeta: platforms?.contains(where: { $0.isBeta == true }) ?? false,
isBeta: isBeta,
isDeprecated: platforms?.contains(where: { $0.unconditionallyDeprecated == true }) ?? false,
defaultImplementationCount: nil,
propertyListKeyNames: nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,30 @@ class ExternalPathHierarchyResolverTests: XCTestCase {
to: "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-9b6be"
)
}

func testBetaInformationPreserved() throws {
let platformMetadata = [
"macOS": PlatformVersion(VersionTriplet(1, 0, 0), beta: true),
"watchOS": PlatformVersion(VersionTriplet(2, 0, 0), beta: true),
"tvOS": PlatformVersion(VersionTriplet(3, 0, 0), beta: true),
"iOS": PlatformVersion(VersionTriplet(4, 0, 0), beta: true),
"Mac Catalyst": PlatformVersion(VersionTriplet(4, 0, 0), beta: true),
"iPadOS": PlatformVersion(VersionTriplet(4, 0, 0), beta: true),
]
let linkResolvers = try makeLinkResolversForTestBundle(named: "AvailabilityBetaBundle") { context in
context.externalMetadata.currentPlatforms = platformMetadata
}

// MyClass is only available on beta platforms (macos=1.0.0, watchos=2.0.0, tvos=3.0.0, ios=4.0.0)
try linkResolvers.assertBetaStatus(authoredLink: "/MyKit/MyClass", isBeta: true)

// MyOtherClass is available on some beta platforms (macos=1.0.0, watchos=2.0.0, tvos=3.0.0, ios=3.0.0)
try linkResolvers.assertBetaStatus(authoredLink: "/MyKit/MyOtherClass", isBeta: false)

// MyThirdClass has no platform availability information
try linkResolvers.assertBetaStatus(authoredLink: "/MyKit/MyThirdClass", isBeta: false)

}

// MARK: Test helpers

Expand Down Expand Up @@ -864,6 +888,23 @@ class ExternalPathHierarchyResolverTests: XCTestCase {
}
}

func assertBetaStatus(
authoredLink: String,
isBeta: Bool,
file: StaticString = #file,
line: UInt = #line
) throws {
try assertResults(authoredLink: authoredLink) { result, label in
switch result {
case .success(let resolved):
let entity = externalResolver.entity(resolved)
XCTAssertEqual(entity.topicRenderReference.isBeta, isBeta, file: file, line: line)
case .failure(_, let errorInfo):
XCTFail("Unexpectedly failed to resolve \(label) link: \(errorInfo.message) \(errorInfo.solutions.map(\.summary).joined(separator: ", "))", file: file, line: line)
}
}
}

func assertFailsToResolve(
authoredLink: String,
errorMessage: String,
Expand Down Expand Up @@ -896,8 +937,10 @@ class ExternalPathHierarchyResolverTests: XCTestCase {
}
}

private func makeLinkResolversForTestBundle(named testBundleName: String) throws -> LinkResolvers {
let (bundle, context) = try testBundleAndContext(named: testBundleName)
private func makeLinkResolversForTestBundle(named testBundleName: String, configureContext: ((DocumentationContext) throws -> Void)? = nil) throws -> LinkResolvers {
let bundleURL = try XCTUnwrap(Bundle.module.url(forResource: testBundleName, withExtension: "docc", subdirectory: "Test Bundles"))
let (_, bundle, context) = try loadBundle(from: bundleURL, configureContext: configureContext)

let localResolver = try XCTUnwrap(context.linkResolver.localResolver)

let resolverInfo = try localResolver.prepareForSerialization(bundleID: bundle.identifier)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
{
"metadata": {
"formatVersion" : {
"major" : 1
},
"generator" : "app/1.0"
},
"module" : {
"name" : "MyKit",
"platform" : {
"architecture" : "x86_64",
"vendor" : "apple",
"operatingSystem" : {
"name" : "macosx",
"minimumVersion" : {
"major" : 1,
"minor" : 0,
"patch" : 0
}
}
}
},
"symbols" : [
{
"accessLevel" : "public",
"kind" : {
"identifier" : "swift.class",
"displayName" : "Class"
},
"names" : {
"title" : "MyClass",
"subHeading" : [
{
"kind" : "keyword",
"spelling" : "class"
},
{
"kind" : "text",
"spelling" : " "
},
{
"kind" : "identifier",
"spelling" : "MyClass"
}
],
"navigator" : [
{
"kind" : "identifier",
"spelling" : "MyClassNavigator"
}
]
},
"availability" : [
{
"domain": "macOS",
"introduced": {
"major": 1,
"minor": 0
}
},
{
"domain": "watchOS",
"introduced": {
"major": 2,
"minor": 0
}
},
{
"domain": "tvOS",
"introduced": {
"major": 3,
"minor": 0
}
},
{
"domain": "iOS",
"introduced": {
"major": 4,
"minor": 0
}
}
],
"pathComponents" : [
"MyClass"
],
"identifier" : {
"precise" : "s:5MyKit0A5ClassC",
"interfaceLanguage": "swift"
},
"declarationFragments" : [
{
"kind" : "keyword",
"spelling" : "class"
},
{
"kind" : "text",
"spelling" : " "
},
{
"kind" : "identifier",
"spelling" : "MyClass"
}
]
},
{
"accessLevel" : "public",
"kind" : {
"identifier" : "swift.class",
"displayName" : "Class"
},
"names" : {
"title" : "MyOtherClass",
"subHeading" : [
{
"kind" : "keyword",
"spelling" : "class"
},
{
"kind" : "text",
"spelling" : " "
},
{
"kind" : "identifier",
"spelling" : "MyOtherClass"
}
],
"navigator" : [
{
"kind" : "identifier",
"spelling" : "MyOtherClassNavigator"
}
]
},
"availability" : [
{
"domain": "macOS",
"introduced": {
"major": 1,
"minor": 0
}
},
{
"domain": "watchOS",
"introduced": {
"major": 2,
"minor": 0
}
},
{
"domain": "tvOS",
"introduced": {
"major": 3,
"minor": 0
}
},
{
"domain": "iOS",
"introduced": {
"major": 3,
"minor": 0
}
}
],
"pathComponents" : [
"MyOtherClass"
],
"identifier" : {
"precise" : "s:5MyKit0A5MyOtherClassC",
"interfaceLanguage": "swift"
},
"declarationFragments" : [
{
"kind" : "keyword",
"spelling" : "class"
},
{
"kind" : "text",
"spelling" : " "
},
{
"kind" : "identifier",
"spelling" : "MyOtherClass"
}
]
},
{
"accessLevel" : "public",
"kind" : {
"identifier" : "swift.class",
"displayName" : "Class"
},
"names" : {
"title" : "MyThirdClass",
"subHeading" : [
{
"kind" : "keyword",
"spelling" : "class"
},
{
"kind" : "text",
"spelling" : " "
},
{
"kind" : "identifier",
"spelling" : "MyThirdClass"
}
],
"navigator" : [
{
"kind" : "identifier",
"spelling" : "MyThirdClassNavigator"
}
]
},
"availability" : [],
"pathComponents" : [
"MyThirdClass"
],
"identifier" : {
"precise" : "s:5MyKit0A5MyThirdClassC",
"interfaceLanguage": "swift"
},
"declarationFragments" : [
{
"kind" : "keyword",
"spelling" : "class"
},
{
"kind" : "text",
"spelling" : " "
},
{
"kind" : "identifier",
"spelling" : "MyThirdClass"
}
]
},

],
"relationships" : []
}
Loading

0 comments on commit 9629c9a

Please sign in to comment.