Skip to content

Commit 81e3bf5

Browse files
Avoid appending coverage info concurrently. (#703)
* Avoid appending coverage info concurrently. rdar://116872208 * Enhance ConvertActionTests to check the correct number of coverage info structs are consumed when one of the documentationCoverageOptions is set. --------- Co-authored-by: Pat Shaughnessy <[email protected]>
1 parent c7dad6b commit 81e3bf5

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ public struct DocumentationConverter: DocumentationConverterProtocol {
323323
context: context
324324
)
325325
if coverageFilterClosure(coverageEntry) {
326-
coverageInfo.append(coverageEntry)
326+
resultsGroup.async(queue: resultsSyncQueue) {
327+
coverageInfo.append(coverageEntry)
328+
}
327329
}
328330
case .none:
329331
break

Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1584,13 +1584,44 @@ class ConvertActionTests: XCTestCase {
15841584
}
15851585
}
15861586

1587+
/// An empty implementation of `ConvertOutputConsumer` that purposefully does nothing except
1588+
/// to pass the number of documentation coverage info structs received to the given handler
1589+
struct TestDocumentationCoverageConsumer: ConvertOutputConsumer {
1590+
1591+
let coverageConsumeHandler: (Int) -> Void
1592+
1593+
init(coverageConsumeHandler: @escaping (Int) -> Void) {
1594+
self.coverageConsumeHandler = coverageConsumeHandler
1595+
}
1596+
1597+
func consume(renderNode: RenderNode) throws { }
1598+
func consume(problems: [Problem]) throws { }
1599+
func consume(assetsInBundle bundle: DocumentationBundle) throws {}
1600+
func consume(linkableElementSummaries: [LinkDestinationSummary]) throws {}
1601+
func consume(indexingRecords: [IndexingRecord]) throws {}
1602+
func consume(assets: [RenderReferenceType: [RenderReference]]) throws {}
1603+
func consume(benchmarks: Benchmark) throws {}
1604+
1605+
// Call the handler with the number of coverage items consumed here
1606+
func consume(documentationCoverageInfo: [CoverageDataEntry]) throws {
1607+
coverageConsumeHandler(documentationCoverageInfo.count)
1608+
}
1609+
}
1610+
15871611
func testMetadataIsOnlyWrittenToOutputFolderWhenDocumentationCoverage() throws {
15881612

1589-
// An empty documentation bundle
1613+
// An empty documentation bundle, except for a single symbol graph file
1614+
// containing 8 symbols.
15901615
let bundle = Folder(name: "unit-test.docc", content: [
15911616
InfoPlist(displayName: "TestBundle", identifier: "com.test.example"),
1617+
CopyOfFile(original: symbolGraphFile, newName: "MyKit.symbols.json"),
15921618
])
15931619

1620+
// Count the number of coverage info structs consumed by each test below,
1621+
// using TestDocumentationCoverageConsumer and this handler.
1622+
var coverageInfoCount = 0
1623+
let coverageInfoHandler = { count in coverageInfoCount += count }
1624+
15941625
// Check that they're nothing is written for `.noCoverage`
15951626
do {
15961627
let testDataProvider = try TestFileSystem(folders: [bundle, Folder.emptyHTMLTemplateDirectory])
@@ -1612,6 +1643,10 @@ class ConvertActionTests: XCTestCase {
16121643
let result = try action.perform(logHandle: .standardOutput)
16131644

16141645
XCTAssertFalse(testDataProvider.fileExists(atPath: result.outputs[0].appendingPathComponent("documentation-coverage.json").path))
1646+
1647+
// Rerun the convert and test no coverage info structs were consumed
1648+
let _ = try action.converter.convert(outputConsumer: TestDocumentationCoverageConsumer(coverageConsumeHandler: coverageInfoHandler))
1649+
XCTAssertEqual(coverageInfoCount, 0)
16151650
}
16161651

16171652
// Check that JSON is written for `.brief`
@@ -1635,6 +1670,11 @@ class ConvertActionTests: XCTestCase {
16351670
let result = try action.perform(logHandle: .standardOutput)
16361671

16371672
XCTAssertTrue(testDataProvider.fileExists(atPath: result.outputs[0].appendingPathComponent("documentation-coverage.json").path))
1673+
1674+
// Rerun the convert and test one coverage info structs was consumed for each symbol page (8)
1675+
coverageInfoCount = 0
1676+
let _ = try action.converter.convert(outputConsumer: TestDocumentationCoverageConsumer(coverageConsumeHandler: coverageInfoHandler))
1677+
XCTAssertEqual(coverageInfoCount, 8)
16381678
}
16391679

16401680
// Check that JSON is written for `.detailed`
@@ -1658,6 +1698,11 @@ class ConvertActionTests: XCTestCase {
16581698
let result = try action.perform(logHandle: .standardOutput)
16591699

16601700
XCTAssertTrue(testDataProvider.fileExists(atPath: result.outputs[0].appendingPathComponent("documentation-coverage.json").path))
1701+
1702+
// Rerun the convert and test one coverage info structs was consumed for each symbol page (8)
1703+
coverageInfoCount = 0
1704+
let _ = try action.converter.convert(outputConsumer: TestDocumentationCoverageConsumer(coverageConsumeHandler: coverageInfoHandler))
1705+
XCTAssertEqual(coverageInfoCount, 8)
16611706
}
16621707
}
16631708

0 commit comments

Comments
 (0)