Repository navigation
This release makes Xcode projects whose packages apply a build tool plugin testable, and stops the tool from printing a score it did not earn. Before, xcodebuild stopped at Validate plug-in waiting for a trust prompt a headless build cannot answer: every mutant came back unviable and the run printed 100%. Now plugin and macro validation is skipped, a project that does not build stops the run with the compiler error, a run where no mutant could be tested shows n/a, and a build that runs out of time stops the run instead of counting its mutants as detected. A new --xcodebuild-arg flag passes extra arguments to every xcodebuild call. Thanks to @mifkata for the report and for testing every fix on a real project (#176).
Bug fixes
Xcode projects with package plugins (#176)
- Every
build-for-testingpasses-skipPackagePluginValidationand-skipMacroValidation. A project whose packages apply a build tool plugin, such as SwiftLint or the OpenAPI generator, no longer stalls atValidate plug-in - Every source the tool writes into the sandbox is wrapped in
// swiftlint:disable alland// swiftlint:enable all. A SwiftLint build tool plugin used to lint the schematized code and fail the build on its generated identifiers, which made every mutant unviable. The integrity check compares the files without the wrapper
No score that was not earned (#176)
- When the schematized build produces nothing, the project is built once without mutations. If that build fails, the run exits
1with the compiler output, which--keep-logsalso writes tobaseline.log. Before, every mutant was reported unviable - A run where no mutant could be tested has no mutation score. It shows
n/ain the console, HTML and Markdown reports, and--min-scoreand--max-score-dropfail on it. Before, it printed 100% - A fallback build, or a warm or per-mutant build of an incompatible mutant, that does not finish within
--build-timeoutstops the run withBuild did not finish within Ns. Raise --build-timeout.It used to record its mutants asTimeout, which counts as detected, so a project whose fallback builds all ran out of time reported 100%
Timed-out Xcode builds (#176)
- A timed-out build no longer leaves its sandbox behind.
SWBBuildServiceand theswift-frontendprocesses it starts run in their own process groups, so the signal sent toxcodebuild's group missed them, and they kept writing into the sandbox after the tool had removed it (225 MB to 1.3 GB per run). They are now stopped and killed first
Cache
- The cache records the extra
xcodebuildarguments, so changing them tests every mutant again - The cache format is now version 4. Caches written by older versions can hold false
unviableverdicts from the plugin validation, which would never be tested again. They are discarded once, with the usual warning
New
Extra xcodebuild arguments (#176)
--xcodebuild-arg <arg>, repeatable, and thexcodebuild-argskey in.swift-mutation-testing.ymlpass arguments to everyxcodebuildcall of a run: the shared build, the test runs, the fallback builds, and the builds and tests of incompatible mutants- Arguments the tool sets itself, such as the actions,
-scheme,-destinationor-derivedDataPath, are refused with exit code1. So is the flag on a package, since SwiftPM builds take noxcodebuildargument - The command line replaces the file's list as a whole, and an argument that takes a value is two items: the flag, then its value
swift-mutation-testing --scheme App --destination "platform=macOS" \
--xcodebuild-arg -allowProvisioningUpdates \
--xcodebuild-arg OTHER_SWIFT_FLAGS='$(inherited) -DMUTATION_TESTING'xcodebuild-args:
- -allowProvisioningUpdates
- -xcconfig
- Config/Mutation.xcconfigUpgrading
- The first run after upgrading tests every mutant again. Cache format 4 discards the older caches once
- A project that does not build now ends the run with
1. Before, it finished with every mutant unviable and a 100% score - A build timeout now ends the run with
1. If a fallback or incompatible-mutant build needs more time than the default 120 s, raise--build-timeout - The score can be
n/a. A quality gate with--min-scoreor--max-score-dropfails when no mutant could be tested, and a baseline written from such a run has no score
Test coverage
- 1509 tests in 181 suites
- 9583 of 9586 source lines covered
- 3775 of 3783 regions covered (99.79%)
This repository's own score on the default tier is still 99.8%, from the self-run of 2026-10-13: 1672 mutants, 98.8% over every operator. That is the README badge.
Known issues
- The Xcode path does not validate a baseline; a failing suite there still makes every mutant look killed
Signed-Releasesand reproducible build provenance are not in place- A mutant that hangs pays the targeted timeout and then the full one
Requirements
- macOS 15+
- Swift 6.2+ (Xcode 26)
- Xcode project or workspace with a valid scheme and test target, or an SPM package with a test target
Installation
See the Installation Guide for Homebrew, pre-built binary, and build from source instructions.
What's Changed
- Update the pre-commit hook versions by @ericodx in #173
- Move the campaign and benchmark scripts under .github/scripts by @ericodx in #174
- chore: move SECURITY.md to the repository root by @ericodx in #175
- Skip package plugin validation, pass extra xcodebuild arguments and stop when the unmutated build fails by @ericodx in #177
Full Changelog: v1.6.1...v1.6.2