Skip to content

Commit 1f700f7

Browse files
committed
fixing names
1 parent 77b0960 commit 1f700f7

7 files changed

Lines changed: 130 additions & 129 deletions

File tree

Sources/DocumentationHarness/CodeBlock.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import Foundation
3131

3232
/// Represents a code block extracted from documentation
33-
package struct CodeBlock {
33+
package struct CodeBlock: Sendable {
3434
/// The raw Swift code content
3535
internal let code: String
3636
/// Line number where this code block starts in the source file

Sources/DocumentationHarness/CodeBlockType.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@
2929

3030
import Foundation
3131

32-
internal enum CodeBlockType {
32+
internal enum CodeBlockType: Sendable {
3333
case example
34-
@available(*, unavailable)
34+
@available(
35+
*, unavailable,
36+
message: "Parsing Package.swift manifests as documentation code blocks is unsupported."
37+
)
3538
case packageManifest
3639
case shellCommand
3740
}

Sources/DocumentationHarness/DocumentationTestHarness.swift

Lines changed: 0 additions & 88 deletions
This file was deleted.

Sources/DocumentationHarness/DocumentationValidator.swift

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,45 +28,61 @@
2828
//
2929

3030
package import Foundation
31+
import SwiftParser
32+
import SwiftSyntax
33+
import Testing
3134

32-
package protocol DocumentationValidator {
33-
func validateFile(at fileURL: URL) throws -> [ValidationResult]
34-
}
35+
/// Test harness for extracting and validating Swift code examples from documentation
36+
package struct DocumentationValidator: Validator {
37+
/// Swift code validator instance
38+
private let codeValidator: any SyntaxValidator
39+
private let codeBlocksFrom: CodeBlockExtractor
3540

36-
private let privateDefaultPathExtensions = ["md"]
37-
extension DocumentationValidator {
38-
/// Default file extensions for documentation files
39-
package static var defaultPathExtensions: [String] {
40-
privateDefaultPathExtensions
41+
/// Creates a new documentation test harness
42+
/// - Parameters:
43+
/// - codeValidator: Validator for Swift code syntax (defaults to CodeSyntaxValidator)
44+
/// - fileSearcher: File system searcher (defaults to FileManager.default)
45+
/// - codeBlocksFrom: Function to extract code blocks from content
46+
package init(
47+
codeValidator: any SyntaxValidator = CodeSyntaxValidator(),
48+
codeBlocksFrom: @escaping CodeBlockExtractor = CodeBlockExtraction.callAsFunction(_:)
49+
) {
50+
self.codeValidator = codeValidator
51+
self.codeBlocksFrom = codeBlocksFrom
4152
}
4253

43-
/// Validates all Swift code examples found in documentation files
44-
/// - Parameters:
45-
/// - relativePaths: Array of relative paths to search for documentation
46-
/// - projectRoot: Root URL of the project
47-
/// - pathExtensions: File extensions to search for (defaults to ["md"])
48-
/// - Returns: Array of validation results for all code blocks found
49-
/// - Throws: FileSearchError if file operations fail
50-
package func validate(
51-
relativePaths: [String],
52-
atProjectRoot projectRoot: URL,
53-
withPathExtensions pathExtensions: [String] = Self.defaultPathExtensions,
54-
using fileSearcher: any FileSearcher = FileManager.default
55-
) throws -> [ValidationResult] {
56-
let documentationFiles = try relativePaths.flatMap { docPath in
57-
let absolutePath = projectRoot.appendingPathComponent(docPath)
58-
return try fileSearcher.findDocumentationFiles(
59-
in: absolutePath,
60-
pathExtensions: pathExtensions
54+
/// Validates all Swift code examples in a specific documentation file
55+
/// - Parameter fileURL: URL of the file to validate
56+
/// - Returns: Array of validation results for code blocks in the file
57+
/// - Throws: Error if file cannot be read or parsed
58+
package func validateFile(at fileURL: URL) throws -> [ValidationResult] {
59+
// let fullPath = try resolveFilePath(filePath)
60+
let content = try String(contentsOf: fileURL)
61+
62+
let codeBlocks = try codeBlocksFrom(content)
63+
var results: [ValidationResult] = []
64+
65+
for (index, codeBlock) in codeBlocks.enumerated() {
66+
results.append(
67+
validateCodeBlock(fileURL.codeBlock(codeBlock, at: index))
6168
)
6269
}
63-
var allResults: [ValidationResult] = []
6470

65-
for filePath in documentationFiles {
66-
let results = try validateFile(at: filePath)
67-
allResults.append(contentsOf: results)
68-
}
71+
return results
72+
}
6973

70-
return allResults
74+
/// Validates a single code block
75+
private func validateCodeBlock(
76+
_ parameters: CodeBlockValidationParameters
77+
) -> ValidationResult {
78+
guard case .example = parameters.codeBlock.blockType else {
79+
return ValidationResult(
80+
parameters: parameters,
81+
testType: .skipped,
82+
error: nil
83+
)
84+
}
85+
// Test compilation and basic execution
86+
return codeValidator.validateSyntax(from: parameters)
7187
}
7288
}

Sources/DocumentationHarness/PackageValidator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ import Foundation
6464
}
6565

6666
guard process.terminationStatus == 0 else {
67-
return
67+
throw .packageValidationFailed
6868
}
6969

70-
throw .packageValidationFailed
70+
return
7171
}
7272
}
7373

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// Validator.swift
3+
// SyntaxKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2025 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
package import Foundation
31+
32+
package protocol Validator {
33+
func validateFile(at fileURL: URL) throws -> [ValidationResult]
34+
}
35+
36+
private let privateDefaultPathExtensions = ["md"]
37+
extension Validator {
38+
/// Default file extensions for documentation files
39+
package static var defaultPathExtensions: [String] {
40+
privateDefaultPathExtensions
41+
}
42+
43+
/// Validates all Swift code examples found in documentation files
44+
/// - Parameters:
45+
/// - relativePaths: Array of relative paths to search for documentation
46+
/// - projectRoot: Root URL of the project
47+
/// - pathExtensions: File extensions to search for (defaults to ["md"])
48+
/// - Returns: Array of validation results for all code blocks found
49+
/// - Throws: FileSearchError if file operations fail
50+
package func validate(
51+
relativePaths: [String],
52+
atProjectRoot projectRoot: URL,
53+
withPathExtensions pathExtensions: [String] = Self.defaultPathExtensions,
54+
using fileSearcher: any FileSearcher = FileManager.default
55+
) throws -> [ValidationResult] {
56+
let documentationFiles = try relativePaths.flatMap { docPath in
57+
let absolutePath = projectRoot.appendingPathComponent(docPath)
58+
return try fileSearcher.findDocumentationFiles(
59+
in: absolutePath,
60+
pathExtensions: pathExtensions
61+
)
62+
}
63+
var allResults: [ValidationResult] = []
64+
65+
for filePath in documentationFiles {
66+
let results = try validateFile(at: filePath)
67+
allResults.append(contentsOf: results)
68+
}
69+
70+
return allResults
71+
}
72+
}

Tests/SyntaxDocTests/DocumentationExampleTests.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import Testing
55
/// Integration tests that validate all code examples in DocC documentation
66
@Suite("Documentation Code Examples")
77
internal struct DocumentationExampleTests {
8+
private let testHarness = DocumentationValidator()
9+
810
/// Test harness that extracts and validates Swift code examples from documentation
911
@Test("All documentation code examples compile and execute correctly")
1012
internal func validateAllDocumentationExamples() throws {
11-
let testHarness = DocumentationTestHarness()
1213
let results = try testHarness.validate(
1314
relativePaths: Settings.docPaths,
1415
atProjectRoot: Settings.projectRoot
@@ -40,7 +41,6 @@ internal struct DocumentationExampleTests {
4041

4142
@Test("Quick Start Guide examples work correctly")
4243
internal func validateQuickStartGuideExamples() throws {
43-
let testHarness = DocumentationTestHarness()
4444
let quickStartFile = try Settings.resolveFilePath(
4545
"Sources/SyntaxKit/Documentation.docc/Tutorials/Quick-Start-Guide.md"
4646
)
@@ -56,7 +56,6 @@ internal struct DocumentationExampleTests {
5656

5757
@Test("Creating Macros tutorial examples work correctly")
5858
internal func validateMacroTutorialExamples() throws {
59-
let testHarness = DocumentationTestHarness()
6059
let macroTutorialFile = try Settings.resolveFilePath(
6160
"Sources/SyntaxKit/Documentation.docc/Tutorials/Creating-Macros-with-SyntaxKit.md"
6261
)
@@ -71,7 +70,6 @@ internal struct DocumentationExampleTests {
7170

7271
@Test("Enum Generator examples work correctly")
7372
internal func validateEnumGeneratorExamples() throws {
74-
let testHarness = DocumentationTestHarness()
7573
let enumExampleFile = try Settings.resolveFilePath(
7674
"Sources/SyntaxKit/Documentation.docc/Examples/EnumGenerator.md"
7775
)

0 commit comments

Comments
 (0)