diff --git a/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift b/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift index 75d6e59309..a1dba6a3d1 100644 --- a/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift +++ b/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift @@ -97,9 +97,7 @@ public final class DiagnosticEngine { } public func finalize() { - workQueue.async { [weak self] in - // If the engine isn't around then return early - guard let self = self else { return } + workQueue.sync { for consumer in self.consumers.sync({ $0.values }) { try? consumer.finalize() } diff --git a/Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift b/Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift index 0b9c6226ae..3b3f1553bf 100644 --- a/Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift +++ b/Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift @@ -177,6 +177,10 @@ public struct DocumentationConverter: DocumentationConverterProtocol { mutating public func convert( outputConsumer: OutputConsumer ) throws -> (analysisProblems: [Problem], conversionProblems: [Problem]) { + defer { + diagnosticEngine.finalize() + } + // Unregister the current file data provider and all its bundles // when running repeated conversions. if let dataProvider = self.currentDataProvider { @@ -397,8 +401,6 @@ public struct DocumentationConverter: DocumentationConverterProtocol { context.linkResolutionMismatches.reportGatheredMismatchesIfEnabled() - diagnosticEngine.finalize() - return (analysisProblems: context.problems, conversionProblems: conversionProblems) } diff --git a/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift b/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift index da017c79f9..6976c0e45d 100644 --- a/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift +++ b/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift @@ -2323,6 +2323,47 @@ class ConvertActionTests: XCTestCase { XCTAssert(error is ErrorsEncountered, "Unexpected error type thrown by \(ConvertAction.self)") } } + + func testWritesDiagnosticFileWhenThrowingError() throws { + let bundle = Folder(name: "unit-test.docc", content: [ + InfoPlist(displayName: "TestBundle", identifier: "com.test.example"), + CopyOfFile(original: symbolGraphFile, newName: "MyKit.symbols.json"), + TextFile(name: "Article.md", utf8Content: """ + Bad title + + This article has a malformed title and can't be analyzed, so it + produces one warning. + """), + incompleteSymbolGraphFile, + ]) + + let testDataProvider = try TestFileSystem(folders: [bundle, Folder.emptyHTMLTemplateDirectory]) + let targetDirectory = URL(fileURLWithPath: testDataProvider.currentDirectoryPath) + .appendingPathComponent("target", isDirectory: true) + + let diagnosticFile = try createTemporaryDirectory().appendingPathComponent("test-diagnostics.json") + + var action = try ConvertAction( + documentationBundleURL: bundle.absoluteURL, + outOfProcessResolver: nil, + analyze: true, + targetDirectory: targetDirectory, + htmlTemplateDirectory: Folder.emptyHTMLTemplateDirectory.absoluteURL, + emitDigest: false, + currentPlatforms: nil, + dataProvider: testDataProvider, + fileManager: testDataProvider, + temporaryDirectory: createTemporaryDirectory(), + diagnosticLevel: "error", + diagnosticFilePath: diagnosticFile + ) + + XCTAssertFalse(FileManager.default.fileExists(atPath: diagnosticFile.path), "Diagnostic file doesn't exist before") + XCTAssertThrowsError(try action.performAndHandleResult()) { error in + XCTAssert(error is ErrorsEncountered, "Unexpected error type thrown by \(ConvertAction.self)") + } + XCTAssertTrue(FileManager.default.fileExists(atPath: diagnosticFile.path), "Diagnostic file exist after") + } // Verifies setting convert inherit docs flag func testConvertInheritDocsOption() throws {