Repository: cpisciotta/xcbeautify
Pull request #387 (2025-02-22 19:55:56)

Add SymLinkCaptureGroup

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cpisciotta/xcbeautify/pull/387?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.24%. Comparing base [(`b1059ef`)](https://app.codecov.io/gh/cpisciotta/xcbeautify/commit/b1059efed7bf662dbd31658bdd1c95d6ca169a75?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Charles+Pisciotta) to head [(`4438600`)](https://app.codecov.io/gh/cpisciotta/xcbeautify/commit/4438600dcb701a1906561394386d4c78907d886e?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     #387      +/-   ##
==========================================
- Coverage   91.35%   91.24%   -0.12%     
==========================================
  Files          18       18              
  Lines        2487     2500      +13     
==========================================
+ Hits         2272     2281       +9     
- Misses        215      219       +4     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cpisciotta/xcbeautify/pull/387?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 8c5eb71..16500b9 100644
--- a/Sources/XcbeautifyLib/CaptureGroups.swift
+++ b/Sources/XcbeautifyLib/CaptureGroups.swift
@@ -1321,6 +1321,24 @@ struct ScanDependenciesCaptureGroup: CaptureGroup {
     }
 }
 
+struct SymLinkCaptureGroup: CaptureGroup {
+    static let outputType: OutputType = .task
+
+    static let regex = XCRegex(pattern: #"^SymLink (.+) \(in target '(.*)' from project '(.*)'\)$"#)
+
+    let filePath: 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
+        self.target = target
+        self.project = project
+    }
+}
+
 struct TestsRunCompletionCaptureGroup: CaptureGroup {
     static let outputType: OutputType = .test
 
diff --git a/Sources/XcbeautifyLib/Formatter.swift b/Sources/XcbeautifyLib/Formatter.swift
index a32cd82..16896df 100644
--- a/Sources/XcbeautifyLib/Formatter.swift
+++ b/Sources/XcbeautifyLib/Formatter.swift
@@ -194,6 +194,8 @@ package struct Formatter {
             return renderer.formatSigning(group: group)
         case let group as SymbolReferencedFromCaptureGroup:
             return renderer.formatSymbolReferencedFrom(group: group)
+        case let group as SymLinkCaptureGroup:
+            return renderer.formatSymLink(group: group)
         case let group as TestCaseMeasuredCaptureGroup:
             return renderer.formatTestCaseMeasured(group: group)
         case let group as TestCasePassedCaptureGroup:
diff --git a/Sources/XcbeautifyLib/Parser.swift b/Sources/XcbeautifyLib/Parser.swift
index eb1a366..ec2cda7 100644
--- a/Sources/XcbeautifyLib/Parser.swift
+++ b/Sources/XcbeautifyLib/Parser.swift
@@ -100,6 +100,7 @@ package final class Parser {
         SigningCaptureGroup.self,
         SwiftDriverJobDiscoveryEmittingModuleCaptureGroup.self,
         SwiftDriverJobDiscoveryCompilingCaptureGroup.self,
+        SymLinkCaptureGroup.self,
         ExecutedWithoutSkippedCaptureGroup.self,
         ExecutedWithSkippedCaptureGroup.self,
         TestSuiteAllTestsPassedCaptureGroup.self,
diff --git a/Sources/XcbeautifyLib/Renderers/OutputRendering.swift b/Sources/XcbeautifyLib/Renderers/OutputRendering.swift
index cbf9a77..eb30877 100644
--- a/Sources/XcbeautifyLib/Renderers/OutputRendering.swift
+++ b/Sources/XcbeautifyLib/Renderers/OutputRendering.swift
@@ -73,6 +73,7 @@ protocol OutputRendering {
     func formatScanDependencies(group: ScanDependenciesCaptureGroup) -> String?
     func formatShellCommand(group: ShellCommandCaptureGroup) -> String?
     func formatSigning(group: SigningCaptureGroup) -> String
+    func formatSymLink(group: SymLinkCaptureGroup) -> String?
     func formatSymbolReferencedFrom(group: SymbolReferencedFromCaptureGroup) -> String
     func formatTargetCommand(command: String, group: any TargetCaptureGroup) -> String
     func formatTestCaseMeasured(group: TestCaseMeasuredCaptureGroup) -> String
@@ -538,6 +539,10 @@ extension OutputRendering {
         return colored ? Symbol.error + " " + errorMessage.f.Red : Symbol.asciiError + " " + errorMessage
     }
 
+    func formatSymLink(group: SymLinkCaptureGroup) -> String? {
+        nil
+    }
+
     func formatSymbolReferencedFrom(group: SymbolReferencedFromCaptureGroup) -> String {
         colored ? Symbol.error + " " + group.wholeError.f.Red : Symbol.asciiError + " " + group.wholeError
     }


Test patch:
diff --git a/Tests/XcbeautifyLibTests/CaptureGroupTests.swift b/Tests/XcbeautifyLibTests/CaptureGroupTests.swift
index 44c97a3..6e2f478 100644
--- a/Tests/XcbeautifyLibTests/CaptureGroupTests.swift
+++ b/Tests/XcbeautifyLibTests/CaptureGroupTests.swift
@@ -97,6 +97,15 @@ final class CaptureGroupTests: XCTestCase {
         XCTAssertNotNil(SwiftDriverJobDiscoveryEmittingModuleCaptureGroup.regex.captureGroups(for: input))
     }
 
+    func testMatchSymLink() throws {
+        let input = "SymLink /Backyard-Birds/Build/Products/Debug/PackageFrameworks/LayeredArtworkLibrary.framework/Resources Versions/Current/Resources (in target 'Some Target_Value' from project 'LayeredArtworkLibrary')"
+        let groups = try XCTUnwrap(SymLinkCaptureGroup.regex.captureGroups(for: input))
+        XCTAssertEqual(groups.count, 3)
+        XCTAssertEqual(groups[0], "/Backyard-Birds/Build/Products/Debug/PackageFrameworks/LayeredArtworkLibrary.framework/Resources Versions/Current/Resources")
+        XCTAssertEqual(groups[1], "Some Target_Value")
+        XCTAssertEqual(groups[2], "LayeredArtworkLibrary")
+    }
+
     func testMkDirCaptureGroup() throws {
         let input = "MkDir /Backyard-Birds/Build/Products/Debug/Widgets.appex/Contents (in target \'Widgets\' from project \'Backyard Birds\')"
         XCTAssertNotNil(MkDirCaptureGroup.regex.captureGroups(for: input))
diff --git a/Tests/XcbeautifyLibTests/ParserTests.swift b/Tests/XcbeautifyLibTests/ParserTests.swift
index 0414b91..63e1084 100644
--- a/Tests/XcbeautifyLibTests/ParserTests.swift
+++ b/Tests/XcbeautifyLibTests/ParserTests.swift
@@ -168,6 +168,14 @@ final class ParserTests: XCTestCase {
         XCTAssertEqual(captureGroup.project, "LayeredArtworkLibraryProject")
     }
 
+    func testMatchSymLink() throws {
+        let input = "SymLink /Backyard-Birds/Build/Products/Debug/PackageFrameworks/LayeredArtworkLibrary.framework/Versions/Current A (in target 'LayeredArtworkLibrary_Target' from project 'LayeredArtworkLibrary_Project')"
+        let captureGroup = try XCTUnwrap(parser.parse(line: input) as? SymLinkCaptureGroup)
+        XCTAssertEqual(captureGroup.filePath, "/Backyard-Birds/Build/Products/Debug/PackageFrameworks/LayeredArtworkLibrary.framework/Versions/Current A")
+        XCTAssertEqual(captureGroup.target, "LayeredArtworkLibrary_Target")
+        XCTAssertEqual(captureGroup.project, "LayeredArtworkLibrary_Project")
+    }
+
     func testMatchExplicitDependency() throws {
         let input = #"        ➜ Explicit dependency on target 'BackyardBirdsData_BackyardBirdsData' in project 'Backyard Birds Data'"#
         let captureGroup = try XCTUnwrap(parser.parse(line: input) as? ExplicitDependencyCaptureGroup)
diff --git a/Tests/XcbeautifyLibTests/ParsingTests.swift b/Tests/XcbeautifyLibTests/ParsingTests.swift
index dafa177..ce85a1e 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, 58)
+        XCTAssertEqual(uncapturedOutput, 49)
         #else
-        XCTAssertEqual(uncapturedOutput, 74)
+        XCTAssertEqual(uncapturedOutput, 65)
         #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, 1924)
+        XCTAssertEqual(uncapturedOutput, 1636)
         #else
-        XCTAssertEqual(uncapturedOutput, 2492)
+        XCTAssertEqual(uncapturedOutput, 2204)
         #endif
     }
 
diff --git a/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift
index f496f2d..d777db0 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/AzureDevOpsPipelinesRendererTests.swift
@@ -471,6 +471,11 @@ final class AzureDevOpsPipelinesRendererTests: XCTestCase {
         XCTAssertNil(formatted)
     }
 
+    func testSymLink() {
+        let formatted = logFormatted("SymLink /Backyard-Birds/Build/Products/Debug/PackageFrameworks/BackyardBirdsData.framework/BackyardBirdsData Versions/Current/BackyardBirdsData (in target 'BackyardBirdsData' from project 'BackyardBirdsData')")
+        XCTAssertNil(formatted)
+    }
+
     func testSymbolReferencedFrom() {
         let formatted = logFormatted("  \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")
         XCTAssertEqual(formatted, "##vso[task.logissue type=error]  \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")
diff --git a/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift
index 2155239..c3f0b4b 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift
@@ -468,6 +468,11 @@ final class GitHubActionsRendererTests: XCTestCase {
         XCTAssertNil(formatted)
     }
 
+    func testSymLink() {
+        let formatted = logFormatted("SymLink /Backyard-Birds/Build/Products/Debug/PackageFrameworks/BackyardBirdsData.framework/Resources Versions/Current/Resources (in target 'BackyardBirdsData' from project 'BackyardBirdsData')")
+        XCTAssertNil(formatted)
+    }
+
     func testSymbolReferencedFrom() {
         let formatted = logFormatted("  \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")
         XCTAssertEqual(formatted, "::error ::  \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")
diff --git a/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift
index b0096a3..f3007c8 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/TeamCityRendererTests.swift
@@ -496,6 +496,11 @@ final class TeamCityRendererTests: XCTestCase {
         XCTAssertNil(formatted)
     }
 
+    func testSymLink() {
+        let formatted = noColoredFormatted("SymLink /Backyard-Birds/Build/Products/Debug/PackageFrameworks/BackyardBirdsData.framework/Versions/Current A (in target 'BackyardBirdsData' from project 'BackyardBirdsData')")
+        XCTAssertNil(formatted)
+    }
+
     func testSymbolReferencedFrom() {
         let formatted = noColoredFormatted("  \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")
         XCTAssertEqual(formatted, "[x]   \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")
diff --git a/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift
index a509522..c35d110 100644
--- a/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift
+++ b/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift
@@ -468,6 +468,11 @@ final class TerminalRendererTests: XCTestCase {
         XCTAssertNil(formatted)
     }
 
+    func testSymLink() {
+        let formatted = noColoredFormatted("SymLink /Backyard-Birds/Build/Products/Debug/PackageFrameworks/LayeredArtworkLibrary.framework/LayeredArtworkLibrary Versions/Current/LayeredArtworkLibrary (in target 'LayeredArtworkLibrary' from project 'LayeredArtworkLibrary')")
+        XCTAssertNil(formatted)
+    }
+
     func testSymbolReferencedFrom() {
         let formatted = noColoredFormatted("  \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")
         XCTAssertEqual(formatted, "[x]   \"NetworkBusiness.ImageDownloadManager.saveImage(image: __C.UIImage, needWatermark: Swift.Bool, params: [Swift.String : Any], downloadHandler: (Swift.Bool) -> ()?) -> ()\", referenced from:")