Skip to content

Commit ccad2c3

Browse files
authored
MergeStrategy with ExtensionGraphAssociation Option for GraphCollector (#48)
* add MergeStrategy to GraphCollector - introduce ExtensionGraphAssociation option defining if extension graphs are merged with the extending or extended graph - add unit-test testing ExtensionGraphAssociation behavior * address feedback: drop MergeStrategy wrapper type + linearize unit test checks
1 parent 0a035c5 commit ccad2c3

File tree

2 files changed

+118
-2
lines changed

2 files changed

+118
-2
lines changed

Sources/SymbolKit/UnifiedSymbolGraph/GraphCollector.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,28 @@ public class GraphCollector {
2626
var graphSources: [String: [GraphKind]] = [:]
2727

2828
var extensionGraphs: [URL: SymbolGraph] = [:]
29+
30+
private let extensionGraphAssociationStrategy: ExtensionGraphAssociation
2931

30-
public init() {
32+
/// Initialize a new collector for merging ``SymbolGraph``s into ``UnifiedSymbolGraph``s.
33+
///
34+
/// - Parameter extensionGraphAssociationStrategy: Optionally specifiy how extension graphs are to be merged.
35+
public init(extensionGraphAssociationStrategy: ExtensionGraphAssociation = .extendedGraph) {
3136
self.unifiedGraphs = [:]
3237
self.graphSources = [:]
3338
self.extensionGraphs = [:]
39+
self.extensionGraphAssociationStrategy = extensionGraphAssociationStrategy
40+
}
41+
}
42+
43+
extension GraphCollector {
44+
/// Describes which graph an extension graph (named `[email protected]`)
45+
/// is merged with.
46+
public enum ExtensionGraphAssociation {
47+
/// Merge with the extending module
48+
case extendingGraph
49+
/// Merge with the extended module
50+
case extendedGraph
3451
}
3552
}
3653

@@ -42,7 +59,9 @@ extension GraphCollector {
4259
/// - url: The file name where the given symbol graph is located. Used to determine whether a symbol graph
4360
/// contains primary symbols or extensions.
4461
public func mergeSymbolGraph(_ inputGraph: SymbolGraph, at url: URL, forceLoading: Bool = false) {
45-
let (moduleName, isMainSymbolGraph) = Self.moduleNameFor(inputGraph, at: url)
62+
let (extendedModuleName, isMainSymbolGraph) = Self.moduleNameFor(inputGraph, at: url)
63+
64+
let moduleName = extensionGraphAssociationStrategy == .extendedGraph ? extendedModuleName : inputGraph.module.name
4665

4766
if !isMainSymbolGraph && !forceLoading {
4867
self.extensionGraphs[url] = inputGraph
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2022 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import XCTest
12+
import Foundation
13+
@testable import SymbolKit
14+
15+
class GraphCollectorTests: XCTestCase {
16+
/// Verify that extension graphs are merged with the correct main graph depending on
17+
/// the chosen strategy.
18+
func testExtensionGraphMergingStrategies() throws {
19+
let a = SymbolGraph(metadata: .init(formatVersion: .init(major: 1, minor: 0, patch: 0), generator: "unit-test"),
20+
module: .init(name: "A", platform: .init()),
21+
symbols: [
22+
.init(identifier: .init(precise: "s:AA", interfaceLanguage: "swift"),
23+
names: .init(title: "A", navigator: nil, subHeading: nil, prose: nil),
24+
pathComponents: ["A"],
25+
docComment: nil,
26+
accessLevel: .init(rawValue: "public"),
27+
kind: .init(parsedIdentifier: .class, displayName: "Class"),
28+
mixins: [:])
29+
],
30+
relationships: [])
31+
32+
let b = SymbolGraph(metadata: .init(formatVersion: .init(major: 1, minor: 0, patch: 0), generator: "unit-test"),
33+
module: .init(name: "B", platform: .init()),
34+
symbols: [
35+
.init(identifier: .init(precise: "s:BB", interfaceLanguage: "swift"),
36+
names: .init(title: "B", navigator: nil, subHeading: nil, prose: nil),
37+
pathComponents: ["B"],
38+
docComment: nil,
39+
accessLevel: .init(rawValue: "public"),
40+
kind: .init(parsedIdentifier: .class, displayName: "Class"),
41+
mixins: [:])
42+
],
43+
relationships: [])
44+
45+
let a_At_B = SymbolGraph(metadata: .init(formatVersion: .init(major: 1, minor: 0, patch: 0), generator: "unit-test"),
46+
module: .init(name: "A", platform: .init()),
47+
symbols: [
48+
.init(identifier: .init(precise: "s:BBAatB", interfaceLanguage: "swift"),
49+
names: .init(title: "AatB", navigator: nil, subHeading: nil, prose: nil),
50+
pathComponents: ["B", "AatB"],
51+
docComment: nil,
52+
accessLevel: .init(rawValue: "public"),
53+
kind: .init(parsedIdentifier: .class, displayName: "Class"),
54+
mixins: [:])
55+
],
56+
relationships: [])
57+
58+
59+
// test with default strategy (extension graphs get attached to extend**ed** graph
60+
var collector = GraphCollector()
61+
62+
collector.mergeSymbolGraph(a, at: .init(fileURLWithPath: "A.symbols.json"))
63+
collector.mergeSymbolGraph(b, at: .init(fileURLWithPath: "B.symbols.json"))
64+
collector.mergeSymbolGraph(a_At_B, at: .init(fileURLWithPath: "[email protected]"))
65+
66+
var (unifiedGraphs, _) = collector.finishLoading()
67+
68+
XCTAssertEqual(unifiedGraphs.count, 2)
69+
70+
var graphA = try XCTUnwrap(unifiedGraphs["A"])
71+
XCTAssertEqual(graphA.symbols.count, 1)
72+
XCTAssert(graphA.symbols.keys.contains("s:AA"))
73+
var graphB = try XCTUnwrap(unifiedGraphs["B"])
74+
XCTAssertEqual(graphB.symbols.count, 2)
75+
XCTAssert(graphB.symbols.keys.contains("s:BB"))
76+
XCTAssert(graphB.symbols.keys.contains("s:BBAatB"))
77+
78+
// test with extendingGraph association strategy (extension graphs get attached to extend**ing** graph
79+
collector = GraphCollector(extensionGraphAssociationStrategy: .extendingGraph)
80+
81+
collector.mergeSymbolGraph(a, at: .init(fileURLWithPath: "A.symbols.json"))
82+
collector.mergeSymbolGraph(b, at: .init(fileURLWithPath: "B.symbols.json"))
83+
collector.mergeSymbolGraph(a_At_B, at: .init(fileURLWithPath: "[email protected]"))
84+
85+
(unifiedGraphs, _) = collector.finishLoading()
86+
87+
XCTAssertEqual(unifiedGraphs.count, 2)
88+
89+
graphA = try XCTUnwrap(unifiedGraphs["A"])
90+
XCTAssertEqual(graphA.symbols.count, 2)
91+
XCTAssert(graphA.symbols.keys.contains("s:AA"))
92+
XCTAssert(graphA.symbols.keys.contains("s:BBAatB"))
93+
graphB = try XCTUnwrap(unifiedGraphs["B"])
94+
XCTAssertEqual(graphB.symbols.count, 1)
95+
XCTAssert(graphB.symbols.keys.contains("s:BB"))
96+
}
97+
}

0 commit comments

Comments
 (0)