Skip to content

Commit

Permalink
[test only] Combine many of the various SGF test helpers (swiftlang#955)
Browse files Browse the repository at this point in the history
  • Loading branch information
d-ronnqvist authored Aug 20, 2024
1 parent 979ea2d commit 610d732
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import SymbolKit
/// Translates a symbol's details into a render nodes's details section.
struct PlistDetailsSectionTranslator: RenderSectionTranslator, Decodable {

func generatePlistDetailsRenderSection(_ symbol: Symbol, plistDetails: SymbolGraph.Symbol.PlistDetails) -> PlistDetailsRenderSection {
private func generatePlistDetailsRenderSection(_ symbol: Symbol, plistDetails: SymbolGraph.Symbol.PlistDetails) -> PlistDetailsRenderSection {
PlistDetailsRenderSection(
details: PlistDetailsRenderSection.Details(
rawKey: plistDetails.rawKey,
Expand All @@ -28,9 +28,6 @@ struct PlistDetailsSectionTranslator: RenderSectionTranslator, Decodable {
}

func translateSection(for symbol: Symbol, renderNode: inout RenderNode, renderNodeTranslator: inout RenderNodeTranslator) -> VariantCollection<CodableContentSection?>? {
guard let mixinVariant = symbol.mixinsVariants.allValues.first(where: { mixin in
mixin.variant.keys.contains(SymbolGraph.Symbol.PlistDetails.mixinKey)
}) else { return nil }
guard let plistDetails = symbol.mixinsVariants.allValues.mapFirst(where: { mixin in
mixin.variant[SymbolGraph.Symbol.PlistDetails.mixinKey] as? SymbolGraph.Symbol.PlistDetails
}) else {
Expand Down
157 changes: 148 additions & 9 deletions Sources/SwiftDocCTestUtilities/SymbolGraphCreation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,167 @@
import Foundation
import XCTest
import SymbolKit
import SwiftDocC

// MARK: - Symbol Graph objects

extension XCTestCase {
public func makeSymbolGraph(

package func makeSymbolGraph(
moduleName: String,
platform: SymbolGraph.Platform = .init(),
symbols: [SymbolGraph.Symbol] = [],
relationships: [SymbolGraph.Relationship] = []
) -> SymbolGraph {
return SymbolGraph(
metadata: SymbolGraph.Metadata(
formatVersion: SymbolGraph.SemanticVersion(major: 0, minor: 6, patch: 0),
generator: "unit-test"
),
module: SymbolGraph.Module(
name: moduleName,
platform: platform
),
metadata: makeMetadata(),
module: makeModule(moduleName: moduleName, platform: platform),
symbols: symbols,
relationships: relationships
)
}

package func makeMetadata(major: Int = 0, minor: Int = 6, patch: Int = 0) -> SymbolGraph.Metadata {
SymbolGraph.Metadata(
formatVersion: SymbolGraph.SemanticVersion(major: major, minor: minor, patch: patch),
generator: "unit-test"
)
}

package func makeModule(moduleName: String, platform: SymbolGraph.Platform = .init()) -> SymbolGraph.Module {
SymbolGraph.Module(name: moduleName, platform: platform)
}

// MARK: Line List

package func makeLineList(
docComment: String,
startOffset: SymbolGraph.LineList.SourceRange.Position = defaultSymbolPosition,
url: URL = defaultSymbolURL
) -> SymbolGraph.LineList {
SymbolGraph.LineList(
// Create a `LineList/Line` for each line of the doc comment and calculate a realistic range for each line.
docComment.components(separatedBy: .newlines)
.enumerated()
.map { lineOffset, line in
SymbolGraph.LineList.Line(
text: line,
range: SymbolGraph.LineList.SourceRange(
start: .init(line: startOffset.line + lineOffset, character: startOffset.character),
end: .init(line: startOffset.line + lineOffset, character: startOffset.character + line.count)
)
)
},
// We want to include the file:// scheme here
uri: url.absoluteString
)
}

package func makeMixins(_ mixins: [any Mixin]) -> [String: any Mixin] {
[String: any Mixin](
mixins.map { (type(of: $0).mixinKey, $0) },
uniquingKeysWith: { old, _ in old /* Keep the first encountered value */ }
)
}

// MARK: Symbol

package func makeSymbol(
id: String,
language: SourceLanguage = .swift,
kind kindID: SymbolGraph.Symbol.KindIdentifier,
pathComponents: [String],
docComment: String? = nil,
accessLevel: SymbolGraph.Symbol.AccessControl = .init(rawValue: "public"), // Defined internally in SwiftDocC
location: (position: SymbolGraph.LineList.SourceRange.Position, url: URL)? = (defaultSymbolPosition, defaultSymbolURL),
signature: SymbolGraph.Symbol.FunctionSignature? = nil,
otherMixins: [any Mixin] = []
) -> SymbolGraph.Symbol {
precondition(!pathComponents.isEmpty, "Need at least one path component to name the symbol")

var mixins = otherMixins // Earlier mixins are prioritized if there are duplicates
if let location {
mixins.append(SymbolGraph.Symbol.Location(uri: location.url.absoluteString /* we want to include the file:// scheme */, position: location.position))
}
if let signature {
mixins.append(signature)
}

return SymbolGraph.Symbol(
identifier: SymbolGraph.Symbol.Identifier(precise: id, interfaceLanguage: language.id),
names: makeSymbolNames(name: pathComponents.first!),
pathComponents: pathComponents,
docComment: docComment.map {
makeLineList(
docComment: $0,
startOffset: location?.position ?? defaultSymbolPosition,
url: location?.url ?? defaultSymbolURL
)
},
accessLevel: accessLevel,
kind: makeSymbolKind(kindID),
mixins: makeMixins(mixins)
)
}

package func makeSymbolNames(name: String) -> SymbolGraph.Symbol.Names {
SymbolGraph.Symbol.Names(
title: name,
navigator: [.init(kind: .identifier, spelling: name, preciseIdentifier: nil)],
subHeading: [.init(kind: .identifier, spelling: name, preciseIdentifier: nil)],
prose: nil
)
}

package func makeSymbolKind(_ kindID: SymbolGraph.Symbol.KindIdentifier) -> SymbolGraph.Symbol.Kind {
var documentationNodeKind: DocumentationNode.Kind {
switch kindID {
case .associatedtype: .associatedType
case .class: .class
case .deinit: .deinitializer
case .enum: .enumeration
case .case: .enumerationCase
case .func: .function
case .operator: .operator
case .`init`: .initializer
case .ivar: .instanceVariable
case .macro: .macro
case .method: .instanceMethod
case .namespace: .namespace
case .property: .instanceProperty
case .protocol: .protocol
case .snippet: .snippet
case .struct: .structure
case .subscript: .instanceSubscript
case .typeMethod: .typeMethod
case .typeProperty: .typeProperty
case .typeSubscript: .typeSubscript
case .typealias: .typeAlias
case .union: .union
case .var: .globalVariable
case .module: .module
case .extension: .extension
case .dictionary: .dictionary
case .dictionaryKey: .dictionaryKey
case .httpRequest: .httpRequest
case .httpParameter: .httpParameter
case .httpResponse: .httpResponse
case .httpBody: .httpBody
default: .unknown
}
}
return SymbolGraph.Symbol.Kind(parsedIdentifier: kindID, displayName: documentationNodeKind.name)
}
}

// MARK: Constants

private let defaultSymbolPosition = SymbolGraph.LineList.SourceRange.Position(line: 11, character: 17) // an arbitrary non-zero start position
private let defaultSymbolURL = URL(fileURLWithPath: "/Users/username/path/to/SomeFile.swift")

// MARK: - JSON strings

extension XCTestCase {
public func makeSymbolGraphString(moduleName: String, symbols: String = "", relationships: String = "", platform: String = "") -> String {
return """
{
Expand Down
53 changes: 12 additions & 41 deletions Tests/SwiftDocCTests/Infrastructure/AutoCapitalizationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,28 @@ class AutoCapitalizationTests: XCTestCase {

// MARK: Test helpers

private let start = SymbolGraph.LineList.SourceRange.Position(line: 7, character: 6) // an arbitrary non-zero start position
private let symbolURL = URL(fileURLWithPath: "/path/to/SomeFile.swift")

private func makeSymbolGraph(docComment: String, parameters: [String]) -> SymbolGraph {
makeSymbolGraph(
docComment: docComment,
sourceLanguage: .swift,
parameters: parameters.map { ($0, nil) },
returnValue: .init(kind: .typeIdentifier, spelling: "ReturnValue", preciseIdentifier: "return-value-id")
)
}

private func makeSymbolGraph(
docComment: String?,
sourceLanguage: SourceLanguage,
parameters: [(name: String, externalName: String?)],
returnValue: SymbolGraph.Symbol.DeclarationFragments.Fragment
) -> SymbolGraph {
let uri = symbolURL.absoluteString // we want to include the file:// scheme here
func makeLineList(text: String) -> SymbolGraph.LineList {
return .init(text.splitByNewlines.enumerated().map { lineOffset, line in
.init(text: line, range: .init(start: .init(line: start.line + lineOffset, character: start.character),
end: .init(line: start.line + lineOffset, character: start.character + line.count)))
}, uri: uri)
}

return makeSymbolGraph(
moduleName: "ModuleName",
symbols: [
.init(
identifier: .init(precise: "symbol-id", interfaceLanguage: sourceLanguage.id),
names: .init(title: "functionName(...)", navigator: nil, subHeading: nil, prose: nil),
makeSymbol(
id: "symbol-id",
kind: .func,
pathComponents: ["functionName(...)"],
docComment: docComment.map { makeLineList(text: $0) },
accessLevel: .public, kind: .init(parsedIdentifier: .func, displayName: "Function"),
mixins: [
SymbolGraph.Symbol.Location.mixinKey: SymbolGraph.Symbol.Location(uri: uri, position: start),

SymbolGraph.Symbol.FunctionSignature.mixinKey: SymbolGraph.Symbol.FunctionSignature(
parameters: parameters.map {
.init(name: $0.name, externalName: $0.externalName, declarationFragments: [], children: [])
},
returns: [returnValue]
)
]
docComment: docComment,
signature: .init(
parameters: parameters.map {
.init(name: $0, externalName: nil, declarationFragments: [], children: [])
},
returns: [
.init(kind: .typeIdentifier, spelling: "ReturnValue", preciseIdentifier: "return-value-id")
]
)
)
]
)
}


// MARK: End-to-end integration tests

func testParametersCapitalization() throws {
Expand Down
37 changes: 9 additions & 28 deletions Tests/SwiftDocCTests/Infrastructure/AutomaticCurationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class AutomaticCurationTests: XCTestCase {
JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph(
moduleName: "ModuleName",
symbols: [
makeSymbol(identifier: containerID, kind: .class, pathComponents: ["SomeClass"]),
makeSymbol(identifier: memberID, kind: kind, pathComponents: ["SomeClass", "someMember"]),
makeSymbol(id: containerID, kind: .class, pathComponents: ["SomeClass"]),
makeSymbol(id: memberID, kind: kind, pathComponents: ["SomeClass", "someMember"]),
],
relationships: [
.init(source: memberID, target: containerID, kind: .memberOf, targetFallback: nil),
Expand Down Expand Up @@ -64,15 +64,17 @@ class AutomaticCurationTests: XCTestCase {
// func someFunction() { }
// }
makeSymbol(
identifier: extensionID,
id: extensionID,
kind: .extension,
// The extension has the path component of the extended type
pathComponents: ["Something"],
// Specify the extended symbol's symbol kind
swiftExtension: .init(extendedModule: "ExtendedModule", typeKind: nonExtensionKind, constraints: [])
otherMixins: [
SymbolGraph.Symbol.Swift.Extension(extendedModule: "ExtendedModule", typeKind: nonExtensionKind, constraints: [])
]
),
// No matter what type `ExtendedModule.Something` is, always add a function in the extension
makeSymbol(identifier: memberID, kind: .func, pathComponents: ["Something", "someFunction()"]),
makeSymbol(id: memberID, kind: .func, pathComponents: ["Something", "someFunction()"]),
],
relationships: [
.init(source: extensionID, target: containerID, kind: .extensionTo, targetFallback: "ExtendedModule.Something"),
Expand Down Expand Up @@ -790,8 +792,8 @@ class AutomaticCurationTests: XCTestCase {
let exampleDocumentation = Folder(name: "CatalogName.docc", content: [
JSONFile(name: "ModuleName.symbols.json",
content: makeSymbolGraph(moduleName: "ModuleName", symbols: [
makeSymbol(identifier: containerID, kind: .class, pathComponents: ["SomeClass"]),
makeSymbol(identifier: memberID, kind: kind, pathComponents: ["SomeClass", "someMember"]),
makeSymbol(id: containerID, kind: .class, pathComponents: ["SomeClass"]),
makeSymbol(id: memberID, kind: kind, pathComponents: ["SomeClass", "someMember"]),
], relationships: [
.init(source: memberID, target: containerID, kind: .memberOf, targetFallback: nil),
])),
Expand Down Expand Up @@ -837,24 +839,3 @@ class AutomaticCurationTests: XCTestCase {
}
}
}

private func makeSymbol(
identifier: String,
kind: SymbolGraph.Symbol.KindIdentifier,
pathComponents: [String],
swiftExtension: SymbolGraph.Symbol.Swift.Extension? = nil
) -> SymbolGraph.Symbol {
var mixins = [String: Mixin]()
if let swiftExtension {
mixins[SymbolGraph.Symbol.Swift.Extension.mixinKey] = swiftExtension
}
return SymbolGraph.Symbol(
identifier: .init(precise: identifier, interfaceLanguage: SourceLanguage.swift.id),
names: .init(title: pathComponents.last!, navigator: nil, subHeading: nil, prose: nil),
pathComponents: pathComponents,
docComment: nil,
accessLevel: .public,
kind: .init(parsedIdentifier: kind, displayName: "Kind Display Name"),
mixins: mixins
)
}
Loading

0 comments on commit 610d732

Please sign in to comment.