Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ iOSInjectionProject/

# Compiled app, ready for notarization
product/
AgentNotes/
TestResults/
CLAUDE.md


.scannerwork/
Expand Down
186 changes: 126 additions & 60 deletions .swiftformat
Original file line number Diff line number Diff line change
@@ -1,69 +1,135 @@
--swiftversion 5.9
# old format options:
# --disable wrapMultilineStatementBraces
# --disable redundantSelf
# --disable preferForLoop

--swiftversion 6.2

--acronyms ID,URL,UUID
--allman false
--assetliterals visual-width
--beforemarks
--binarygrouping none
--categorymark "MARK: %c"
--classthreshold 0
--closingparen balanced
# --commas always
--conflictmarkers reject
--decimalgrouping none
--elseposition same-line
--emptybraces no-space
--enumthreshold 0
--exponentcase lowercase
--exponentgrouping disabled
--extensionacl on-extension
--extensionlength 0
--extensionmark "MARK: - %t + %c"
--fractiongrouping disabled
--allow-partial-wrapping true
--anonymous-for-each convert
--asset-literals visual-width
--async-capturing
--before-marks
--binary-grouping none
--blank-line-after-switch-case multiline-only
--call-site-paren default
--category-mark "MARK: %c"
--class-threshold 0
--closing-paren balanced
--closure-void remove
--complex-attributes preserve
--computed-var-attributes preserve
--conditional-assignment always
--conflict-markers reject
--date-format system
--decimal-grouping none
--default-test-suite-attributes
--doc-comments preserve
--else-position same-line
--empty-braces no-space
--enum-namespaces always
--enum-threshold 0
--equatable-macro none
--exponent-case lowercase
--exponent-grouping disabled
--extension-acl on-extension
--extension-mark "MARK: - %t + %c"
--extension-threshold 0
--file-macro "#file"
--fraction-grouping disabled
--fragment false
--funcattributes preserve
--groupedextension "MARK: %c"
--guardelse auto
--header ignore
--hexgrouping none
--hexliteralcase uppercase
--func-attributes preserve
--generic-types
--group-blank-lines true
--grouped-extension "MARK: %c"
--guard-else auto
--header "// new header text"
--hex-grouping none
--hex-literal-case uppercase
--ifdef indent
--importgrouping alpha
--import-grouping alpha
--indent 4
--indentcase false
--lifecycle
--indent-case false
--indent-strings false
--inferred-types always
--init-coder-nil false
--language-mode 0
--lifecycle
--line-after-marks true
--line-between-guards false
--linebreaks lf
--markextensions always
--marktypes always
--maxwidth none
--modifierorder
--nevertrailing compactMap,map,flatMap
--nospaceoperators
--nowrapoperators
--octalgrouping none
--operatorfunc spaced
--organizetypes actor,class,enum,struct
--patternlet inline
--mark-categories true
--mark-class-threshold 0
--mark-enum-threshold 0
--mark-extension-threshold 0
--mark-extensions always
--mark-struct-threshold 0
--mark-types always
--markdown-files ignore
--max-width none
--modifier-order
--never-trailing compactMap,flatMap,map
--nil-init remove
--no-space-operators
--no-wrap-operators
--non-complex-attributes
--octal-grouping none
--operator-func spaced
--organization-mode visibility
--organize-types actor,class,enum,struct
--pattern-let hoist
--prefer-synthesized-init-for-internal-structs never
--preserve-acronyms
--preserve-decls
--preserved-property-types Package
--property-types inferred
--ranges spaced
--redundanttype inferred
--redundant-async tests-only
--redundant-throws tests-only
--self remove
--selfrequired
--self-required
--semicolons never
--shortoptionals always
--smarttabs enabled
--stripunusedargs closure-only
--structthreshold 0
--tabwidth unspecified
--trailingclosures
--trimwhitespace always
--typeattributes preserve
--typemark "MARK: - %t"
--varattributes preserve
--voidtype void
--wraparguments before-first
--wrapcollections before-first
--wrapconditions preserve
--wrapparameters before-first
--wrapreturntype preserve
--xcodeindentation disabled
--yodaswap always
--disable redundantReturn,wrapMultilineStatementBraces,trailingCommas,preferKeyPath
--short-optionals always
--single-line-for-each ignore
--smart-tabs enabled
--some-any true
--sort-swiftui-properties none
--sorted-patterns
--stored-var-attributes preserve
--strip-unused-args closure-only
--struct-threshold 0
--swift-version 6.2
--tab-width unspecified
--throw-capturing
--timezone system
--trailing-closures
--trailing-commas always
--trim-whitespace always
--type-attributes preserve
--type-blank-lines remove
--type-body-marks preserve
--type-delimiter space-after
--type-mark "MARK: - %t"
--type-marks
--type-order
--url-macro none
--visibility-marks
--visibility-order
--void-type Void
--wrap-arguments before-first
--wrap-collections before-first
--wrap-conditions preserve
--wrap-effects preserve
--wrap-enum-cases always
--wrap-parameters before-first
--wrap-return-type preserve
--wrap-string-interpolation default
--wrap-ternary default
--wrap-type-aliases preserve
--xcode-indentation disabled
--xctest-symbols
--yoda-swap always
--disable docComments,docCommentsBeforeModifiers,fileHeader,opaqueGenericParameters,preferKeyPath,redundantReturn,trailingCommas,wrapMultilineStatementBraces
--enable isEmpty,privateStateVariables,propertyTypes,unusedPrivateDeclarations
8 changes: 6 additions & 2 deletions .swiftpm/xcode/xcshareddata/xcschemes/xcresultparser.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
argument = "/Users/alex/xcodebuild_result.xcresult"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "/Users/alex/Work/__OwnProjects/myGithub/xcresultparser/Tests/XcresultparserTests/TestAssets/parametrized.xcresult"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-d"
isEnabled = "NO">
Expand Down Expand Up @@ -120,7 +124,7 @@
</CommandLineArgument>
<CommandLineArgument
argument = "-o html"
isEnabled = "NO">
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-t FRBNetworking"
Expand All @@ -144,7 +148,7 @@
</CommandLineArgument>
<CommandLineArgument
argument = "-h"
isEnabled = "YES">
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "-o md"
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Version 2.0.0-beta - 2026-02-21
### CHANGES:
- Replace XCResultKit usage with direct `xcresulttool` and `xccov` parsing using local `Codable` models.
- Remove XCResultKit as a package dependency from `Package.swift`.
- Migrate JUnit, summary/test formatting, coverage formatting, and issues JSON paths to the new model/tooling layer.
- Keep output compatibility where possible while aligning with modern `xcresulttool` payloads.
- Raise the minimum supported platform to macOS 12.
- Keep expected failures as a distinct state in human-readable outputs, while emitting them as passing test cases in JUnit/Sonar XML for compatibility.
- Treat unknown `--coverage-targets` values as an error instead of silently producing empty coverage output.

## Version 1.9.4 - 2025-12-19
### CHANGES:
Previously, we were only ever parsing the first failure for a test and passing that to the JUnit representation.
Expand Down
44 changes: 16 additions & 28 deletions CommandlineTool/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ArgumentParser
import Foundation
import XcresultparserLib

private let marketingVersion = "1.9.4"
private let marketingVersion = "2.0.0-beta"

struct xcresultparser: ParsableCommand {
static let configuration = CommandConfiguration(
Expand All @@ -31,7 +31,7 @@ struct xcresultparser: ParsableCommand {
@Option(name: [.customShort("e"), .customLong("excluded-path")], help: "Specify which path names to exclude. You can use more than one -e option to specify a list of path patterns to exclude. This option only has effect, if the format is either 'cobertura' or 'xml' with the --coverage (-c) option for a code coverage report or if the format is one of 'warnings', 'errors' or 'warnings-and-errors'.")
var excludedPaths: [String] = []

@Option(name: .shortAndLong, help: "The fields in the summary. Default is all: errors|warnings|analyzerWarnings|tests|failed|skipped")
@Option(name: .shortAndLong, help: "The fields in the summary. Default is all: errors|warnings|analyzerWarnings|tests|failed|skipped|duration|date")
var summaryFields: String?

@Flag(name: .shortAndLong, help: "Whether to print coverage data.")
Expand Down Expand Up @@ -91,82 +91,70 @@ struct xcresultparser: ParsableCommand {
}

private func outputSonarXML(for xcresult: String) throws {
guard let converter = SonarCoverageConverter(
let converter = try SonarCoverageConverter(
with: URL(fileURLWithPath: xcresult),
projectRoot: projectRoot ?? "",
coverageTargets: coverageTargets,
excludedPaths: excludedPaths,
strictPathnames: strictPathnames == 1
) else {
throw ParseError.argumentError
}
)
let rslt = try converter.xmlString(quiet: quiet == 1)
writeToStdOut(rslt)
}

private func outputCoberturaXML(for xcresult: String) throws {
guard let converter = CoberturaCoverageConverter(
let converter = try CoberturaCoverageConverter(
with: URL(fileURLWithPath: xcresult),
projectRoot: projectRoot ?? "",
coverageTargets: coverageTargets,
excludedPaths: excludedPaths,
strictPathnames: strictPathnames == 1
) else {
throw ParseError.argumentError
}
)
let rslt = try converter.xmlString(quiet: quiet == 1)
writeToStdOut(rslt)
}

private func outputIssuesJSON(for xcresult: String, format: OutputFormat) throws {
guard let converter = IssuesJSON(
let converter = try IssuesJSON(
with: URL(fileURLWithPath: xcresult),
projectRoot: projectRoot ?? "",
excludedPaths: excludedPaths
) else {
throw ParseError.argumentError
}
)
let rslt = try converter.jsonString(format: format, quiet: quiet == 1)
writeToStdOut(rslt)
}

private func outputTargetNames(for xcresult: String) throws {
guard let converter = SonarCoverageConverter(
let converter = try SonarCoverageConverter(
with: URL(fileURLWithPath: xcresult),
projectRoot: projectRoot ?? "",
coverageTargets: coverageTargets,
coverageTargets: [],
strictPathnames: strictPathnames == 1
) else {
throw ParseError.argumentError
}
)
writeToStdOut(converter.targetsInfo)
}

private func outputJUnitXML(
for xcresult: String,
with format: TestReportFormat
) throws {
guard let junitXML = JunitXML(
let junitXML = try JunitXML(
with: URL(fileURLWithPath: xcresult),
projectRoot: projectRoot ?? "",
format: format
) else {
throw ParseError.argumentError
}
)
writeToStdOut(junitXML.xmlString)
}

private func outputDescription(for xcresult: String) throws {
guard let resultParser = XCResultFormatter(
let resultParser = try XCResultFormatter(
with: URL(fileURLWithPath: xcresult),
formatter: outputFormatter,
coverageTargets: coverageTargets,
failedTestsOnly: failedTestsOnly == 1,
summaryFields: summaryFields ?? "errors|warnings|analyzerWarnings|tests|failed|skipped",
summaryFields: summaryFields ?? "errors|warnings|analyzerWarnings|tests|failed|skipped|duration|date",
coverageReportFormat: CoverageReportFormat(string: coverageReportFormat)
) else {
throw ParseError.argumentError
}
)
writeToStdOutLn(resultParser.documentPrefix(title: "XCResults"))
writeToStdOutLn(resultParser.summary)
if noTestResult == 0 {
Expand Down
9 changes: 0 additions & 9 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading