Repository: cpisciotta/xcbeautify
Pull request #389 (2025-02-22 20:29:51)

Add ValidateCaptureGroup

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cpisciotta/xcbeautify/pull/389?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Charles+Pisciotta) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 91.29%. Comparing base [(`4f74582`)](https://app.codecov.io/gh/cpisciotta/xcbeautify/commit/4f74582022b8fa4e0fccc14d86446ffee1b38196?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Charles+Pisciotta) to head [(`85343a5`)](https://app.codecov.io/gh/cpisciotta/xcbeautify/commit/85343a5eeb310d773b1212893ab80611a31c6f46?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Charles+Pisciotta).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #389      +/-   ##
==========================================
- Coverage   91.40%   91.29%   -0.11%     
==========================================
  Files          18       18              
  Lines        2500     2516      +16     
==========================================
+ Hits         2285     2297      +12     
- Misses        215      219       +4     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cpisciotta/xcbeautify/pull/389?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Charles+Pisciotta).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Charles+Pisciotta).


Patch:
diff --git a/Sources/XcbeautifyLib/CaptureGroups.swift b/Sources/XcbeautifyLib/CaptureGroups.swift
index 16500b9..abfa8f1 100644
--- a/Sources/XcbeautifyLib/CaptureGroups.swift
+++ b/Sources/XcbeautifyLib/CaptureGroups.swift
@@ -1447,6 +1447,26 @@ struct TouchCaptureGroup: CaptureGroup {
     }
 }
 
+struct ValidateCaptureGroup: CaptureGroup {
+    static var outputType: OutputType = .task
+
+    static var regex = XCRegex(pattern: #"^Validate (.+) \(in target '(.*)' from project '(.*)'\)$"#)
+
+    let filePath: String
+    let filename: String
+    let target: String
+    let project: String
+
+    init?(groups: [String]) {
+        assert(groups.count == 3)
+        guard let filePath = groups[safe: 0], let target = groups[safe: 1], let project = groups[safe: 2] else { return nil }
+        self.filePath = filePath
+        filename = filePath.lastPathComponent
+        self.target = target
+        self.project = project
+    }
+}
+
 struct WriteFileCaptureGroup: CaptureGroup {
     static let outputType: OutputType = .task
 
diff --git a/Sources/XcbeautifyLib/Formatter.swift b/Sources/XcbeautifyLib/Formatter.swift
index 16896df..4df5d9a 100644
--- a/Sources/XcbeautifyLib/Formatter.swift
+++ b/Sources/XcbeautifyLib/Formatter.swift
@@ -226,6 +226,8 @@ package struct Formatter {
             return renderer.formatWriteAuxiliaryFile(group: group)
         case let group as WriteFileCaptureGroup:
             return renderer.formatWriteFile(group: group)
+        case let group as ValidateCaptureGroup:
+            return renderer.formatValidate(group: group)
         case let group as XcodebuildErrorCaptureGroup:
             return renderer.formatError(group: group)
         case let group as SwiftDriverJobDiscoveryEmittingModuleCaptureGroup:
diff --git a/Sources/XcbeautifyLib/Parser.swift b/Sources/XcbeautifyLib/Parser.swift
index ec2cda7..4134184 100644
--- a/Sources/XcbeautifyLib/Parser.swift
+++ b/Sources/XcbeautifyLib/Parser.swift
@@ -56,6 +56,7 @@ package final class Parser {
         TestSuiteStartedCaptureGroup.self,
         TIFFutilCaptureGroup.self,
         TouchCaptureGroup.self,
+        ValidateCaptureGroup.self,
         WriteFileCaptureGroup.self,
         WriteAuxiliaryFileCaptureGroup.self,
         ParallelTestCasePassedCaptureGroup.self,
diff --git a/Sources/XcbeautifyLib/Renderers/OutputRendering.swift b/Sources/XcbeautifyLib/Renderers/OutputRendering.swift
index eb30877..fc69dbf 100644
--- a/Sources/XcbeautifyLib/Renderers/OutputRendering.swift
+++ b/Sources/XcbeautifyLib/Renderers/OutputRendering.swift
@@ -88,6 +88,7 @@ protocol OutputRendering {
     func formatTouch(group: TouchCaptureGroup) -> String
     func formatUIFailingTest(group: UIFailingTestCaptureGroup) -> String
     func formatUndefinedSymbolLocation(group: UndefinedSymbolLocationCaptureGroup) -> String
+    func formatValidate(group: ValidateCaptureGroup) -> String
     func formatWarning(group: GenericWarningCaptureGroup) -> String
     func formatWillNotBeCodesignWarning(group: WillNotBeCodeSignedCaptureGroup) -> String
     func formatWriteAuxiliaryFile(group: WriteAuxiliaryFileCaptureGroup) -> String?
@@ -414,6 +415,12 @@ extension OutputRendering {
         return colored ? "[\(target.f.Cyan)] \("Touching".s.Bold) \(filename)" : "[\(target)] Touching \(filename)"
     }
 
+    func formatValidate(group: ValidateCaptureGroup) -> String {
+        let target = group.target
+        let filename = group.filename
+        return colored ? "[\(target.f.Cyan)] \("Validate".s.Bold) \(filename)" : "[\(target)] Validate \(filename)"
+    }
+
     func formatWriteAuxiliaryFile(group: WriteAuxiliaryFileCaptureGroup) -> String? {
         let filename = group.filename
         let target = group.target


Test patch:
diff --git a/Tests/XcbeautifyLibTests/CaptureGroupTests.swift b/Tests/XcbeautifyLibTests/CaptureGroupTests.swift
index 6e2f478..c76c350 100644
--- a/Tests/XcbeautifyLibTests/CaptureGroupTests.swift
+++ b/Tests/XcbeautifyLibTests/CaptureGroupTests.swift
@@ -152,6 +152,15 @@ final class CaptureGroupTests: XCTestCase {
         XCTAssertEqual(groups[0], "/Applications/Xcode-16.0.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang")
     }
 
+    func testValidate() throws {
+        let input = "Validate /path/to/DerivedData/Build/Products/Debug-iphonesimulator/some.app (in target 'Target' from project 'Project')"
+        let groups = try XCTUnwrap(ValidateCaptureGroup.regex.captureGroups(for: input))
+        XCTAssertEqual(groups.count, 3)
+        XCTAssertEqual(groups[0], "/path/to/DerivedData/Build/Products/Debug-iphonesimulator/some.app")
+        XCTAssertEqual(groups[1], "Target")
+        XCTAssertEqual(groups[2], "Project")
+    }
+
     func testIndentedShellCommand() throws {
         let input = #"    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -Xlinker -reproducible -target arm64-apple-macos14.0 -dynamiclib -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk -O0 -L/Backyard-Birds/Build/Intermediates.noindex/EagerLinkingTBDs/Debug -L/Backyard-Birds/Build/Products/Debug -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib -F/Backyard-Birds/Build/Intermediates.noindex/EagerLinkingTBDs/Debug -F/Backyard-Birds/Build/Products/Debug/PackageFrameworks -F/Backyard-Birds/Build/Products/Debug -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -filelist /Backyard-Birds/Build/Intermediates.noindex/BackyardBirdsData.build/Debug/BackyardBirdsData\ product.build/Objects-normal/arm64/BackyardBirdsData.LinkFileList -install_name @rpath/BackyardBirdsData.framework/Versions/A/BackyardBirdsData -Xlinker -rpath -Xlinker /Backyard-Birds/Build/Products/Debug/PackageFrameworks -Xlinker -object_path_lto -Xlinker /Backyard-Birds/Build/Intermediates.noindex/BackyardBirdsData.build/Debug/BackyardBirdsData\ product.build/Objects-normal/arm64/BackyardBirdsData_lto.o -Xlinker -export_dynamic -Xlinker -no_deduplicate -fobjc-link-runtime -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -L/usr/lib/swift -Wl,-no_warn_duplicate_libraries -Xlinker -dependency_info -Xlinker /Backyard-Birds/Build/Intermediates.noindex/BackyardBirdsData.build/Debug/BackyardBirdsData\ product.build/Objects-normal/arm64/BackyardBirdsData_dependency_info.dat -o /Backyard-Birds/Build/Intermediates.noindex/BackyardBirdsData.build/Debug/BackyardBirdsData\ product.build/Objects-normal/arm64/Binary/BackyardBirdsData -Xlinker -add_ast_path -Xlinker /Backyard-Birds/Build/Intermediates.noindex/BackyardBirdsData.build/Debug/BackyardBirdsData.build/Objects-normal/arm64/BackyardBirdsData.swiftmodule"#
         let groups = try XCTUnwrap(NonPCHClangCommandCaptureGroup.regex.captureGroups(for: input))
diff --git a/Tests/XcbeautifyLibTests/ParserTests.swift b/Tests/XcbeautifyLibTests/ParserTests.swift
index 63e1084..b4c5cf4 100644
--- a/Tests/XcbeautifyLibTests/ParserTests.swift
+++ b/Tests/XcbeautifyLibTests/ParserTests.swift
@@ -394,4 +394,13 @@ final class ParserTests: XCTestCase {
             XCTAssertNil(parser.parse(line: input))
         }
     }
+
+    func testValidate() throws {
+        let input = #"Validate /Backyard-Birds/Build/Products/Debug/Backyard\ Birds.app (in target 'Backyard Birds Target' from project 'Backyard Birds')"#
+        let captureGroup = try XCTUnwrap(parser.parse(line: input) as? ValidateCaptureGroup)
+        XCTAssertEqual(captureGroup.filePath, #"/Backyard-Birds/Build/Products/Debug/Backyard\ Birds.app"#)
+        XCTAssertEqual(captureGroup.filename, #"Backyard\ Birds.app"#)
+        XCTAssertEqual(captureGroup.target, "Backyard Birds Target")
+        XCTAssertEqual(captureGroup.project, "Backyard Birds")
+    }
 }
diff --git a/Tests/XcbeautifyLibTests/ParsingTests.swift b/Tests/XcbeautifyLibTests/ParsingTests.swift
index ce85a1e..4fbabcb 100644
--- a/Tests/XcbeautifyLibTests/ParsingTests.swift
+++ b/Tests/XcbeautifyLibTests/ParsingTests.swift
@@ -26,9 +26,9 @@ final class ParsingTests: XCTestCase {
         // Update this magic number whenever `uncapturedOutput` is less than the current magic number.
         // There's a regression whenever `uncapturedOutput` is greater than the current magic number.
         #if os(macOS)
-        XCTAssertEqual(uncapturedOutput, 49)
+        XCTAssertEqual(uncapturedOutput, 48)
         #else
-        XCTAssertEqual(uncapturedOutput, 65)
+        XCTAssertEqual(uncapturedOutput, 64)
         #endif
     }
 
@@ -56,9 +56,9 @@ final class ParsingTests: XCTestCase {
         // Update this magic number whenever `uncapturedOutput` is less than the current magic number.
         // There's a regression whenever `uncapturedOutput` is greater than the current magic number.
         #if os(macOS)
-        XCTAssertEqual(uncapturedOutput, 1636)
+        XCTAssertEqual(uncapturedOutput, 1604)
         #else
-        XCTAssertEqual(uncapturedOutput, 2204)
+        XCTAssertEqual(uncapturedOutput, 2172)
         #endif
     }
 
diff --git a/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift
index d777db0..a74406a 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift
@@ -548,6 +548,11 @@ final class AzureDevOpsPipelinesRendererTests: XCTestCase {
         XCTAssertEqual(formatted, "##vso[task.logissue type=error;sourcepath=<unknown>;linenumber=0]    App crashed in <external symbol>")
     }
 
+    func testValidate() {
+        let formatted = logFormatted("Validate /path/to/DerivedData/Build/Products/Debug-iphonesimulator/some.app (in target 'Target' from project 'Project')")
+        XCTAssertEqual(formatted, "[Target] Validate some.app")
+    }
+
     func testWillNotBeCodeSigned() {
         let input = "FrameworkName will not be code signed because its settings don't specify a development team."
         let output = "##vso[task.logissue type=warning]FrameworkName will not be code signed because its settings don't specify a development team."
diff --git a/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift
index c3f0b4b..b688a0d 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift
@@ -253,6 +253,11 @@ final class GitHubActionsRendererTests: XCTestCase {
         XCTAssertEqual(logFormatted(input), output)
     }
 
+    func testValidate() {
+        let formatted = logFormatted(#"Validate /Backyard-Birds/Build/Products/Debug/Backyard\ Birds.app (in target 'Backyard Birds Target' from project 'Backyard Birds')"#)
+        XCTAssertEqual(formatted, #"[Backyard Birds Target] Validate Backyard\ Birds.app"#)
+    }
+
     func testFatalError() {
         let input = "fatal error: malformed or corrupted AST file: 'could not find file '/path/file.h' referenced by AST file' note: after modifying system headers, please delete the module cache at '/path/DerivedData/ModuleCache/M5WJ0FYE7N06'"
         let output = "::error ::fatal error: malformed or corrupted AST file: 'could not find file '/path/file.h' referenced by AST file' note: after modifying system headers, please delete the module cache at '/path/DerivedData/ModuleCache/M5WJ0FYE7N06'"
diff --git a/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift
index f3007c8..cef5d23 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift
@@ -566,6 +566,11 @@ final class TeamCityRendererTests: XCTestCase {
         XCTAssertEqual(formatted, "    ✖ <unknown>:0, App crashed in <external symbol>")
     }
 
+    func testValidate() {
+        let formatted = noColoredFormatted("Validate /path/to/DerivedData/Build/Products/Debug-iphonesimulator/some.app (in target 'Target' from project 'Project')")
+        XCTAssertEqual(formatted, "[Target] Validate some.app")
+    }
+
     func testWillNotBeCodeSigned() {
         let input = "FrameworkName will not be code signed because its settings don't specify a development team."
         let output = "##teamcity[message text=\'Codesign error|n|[!|] FrameworkName will not be code signed because its settings don|\'t specify a development team.\' status=\'WARNING\']\nCodesign error"
diff --git a/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift
index c35d110..100cf1b 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift
@@ -545,6 +545,11 @@ final class TerminalRendererTests: XCTestCase {
         XCTAssertEqual(formatted, "    ✖ <unknown>:0, App crashed in <external symbol>")
     }
 
+    func testValidate() {
+        let formatted = noColoredFormatted(#"Validate /Backyard-Birds/Build/Products/Debug/Backyard\ Birds.app (in target 'Backyard Birds Target' from project 'Backyard Birds')"#)
+        XCTAssertEqual(formatted, #"[Backyard Birds Target] Validate Backyard\ Birds.app"#)
+    }
+
     func testWillNotBeCodeSigned() {
         let input = "FrameworkName will not be code signed because its settings don't specify a development team."
         let output = "[!] FrameworkName will not be code signed because its settings don't specify a development team."