Swift Test

This documentation was generated with the assistance of AI. Please report any inaccuracies.

Run a Swift project’s test suite through whichever runner its layout calls for — SwiftPM’s swift test for a package, xcodebuild test for an Xcode app/workspace project — scoped to whatever the caller asked for, and report a compact pass/fail summary. This is a read-only reporting skill: it runs tests and summarizes the outcome, it does not fix failures or modify source/test code unless asked to as an explicit follow-up.

Purpose

iru-swift-test turns a loosely-specified request ("run FooTests", "just testBar", "everything") into the correct swift test --filter/--skip or xcodebuild -only-testing:/-skip-testing: invocation, detects package vs. app from the repository layout, parses whichever result format that runner produces (console output for swift test, an .xcresult bundle via xcresulttool for xcodebuild test), and — critically — detects the "selector matched zero tests" case that both runners otherwise hide behind a passing, exit-0 result. It is the Swift/Apple-platforms equivalent of iru-java-test/iru-typescript-test/iru-android-test.

Its Known quirks now record that a macOS desktop app scaffolded by iru-setup-macos-app (Automatic signing, empty DEVELOPMENT_TEAM) tests with a plain xcodebuild test -destination 'platform=macOS' — Xcode signs with "Sign to Run Locally", so no signing overrides are added (verified on Xcode 27.1 for both the SwiftUI/MVVM and AppKit/MVC scaffolds); only a project naming a real team whose certificate is missing needs ad-hoc overrides (unverified).

For an iru-setup-apple-app multiplatform project it understands the per-platform schemes (<AppName>-iOS, -macOS, -watchOS, -visionOS, each running CoreTests plus that platform’s unit and UI test targets, with XcodeGen’s Core package scheme ignored): a selector under Targets/<Platform>/ runs only that platform’s scheme, a Packages/Core-only selector runs the first platform scheme (iOS, macOS, watchOS, visionOS order), and the whole suite runs every platform scheme, reported per scheme. Simulator destinations are resolved to the newest installed runtime the selected Xcode supports and always pinned with OS=, because device names repeat across runtimes and xcodebuild otherwise silently picks one.

A leading ui word in the selector asks for a UI run under the catalog’s UI contract (see Building user interfaces): the component-capture and XCUITest screen/flow tests, their screenshots under .ui-screenshots/ and their accessibility audits, reading the level from .ui-quality.yml. A platform with no installed runtime — typically visionOS — is compile-checked against generic/platform=visionOS Simulator and reported "unverified (no visionOS simulator runtime installed)", never as passed and never downloaded (verified on Xcode 27.1 for the iOS, UIKit iOS, watchOS and visionOS scaffold schemes).

When .ui-quality.yml says accessibility: none (opted out entirely) or off (rules apply, checks not run), accessibility checks are not applicable: they are skipped and reported as "accessibility: opted out" (none) or "accessibility checks off" (off) — never "unverified" and never a failure.

Plugin

Shipped by the iru-swift plugin of the ai-catalog marketplace (install with /plugin install iru-swift@ai-catalog; see Plugins).

Invocation

/iru-swift-test
/iru-swift-test <selector>

Inputs

Argument Required Description Default

selector

No

A positional argument naming what to run: a bare test/method name, a suite/class name, a Suite.method or Suite/method pair, or a target-qualified form. Mapped to --filter/-only-testing: syntax per the tables below.

None — omitting it runs the full suite.

kind

No

key: value override of the package-vs-app auto-detection (Step 1) — package or app. Used when a repository has both a Package.swift and an .xcodeproj/.xcworkspace and the caller already knows which to exercise.

Auto-detected from the repository’s files.

scheme

No

key: value — the Xcode scheme to test (app path only).

Auto-detected via xcodebuild -list: with per-platform schemes, picked by the selector’s scope (every platform scheme for the whole suite); otherwise the sole scheme, or the one not containing "Tests"/"UITests" when several exist.

destination

No

key: value — an xcodebuild destination string, e.g. platform=macOS or platform=iOS Simulator,name=iPhone 18 Pro,OS=27.0 (app path only).

Inferred from the scheme suffix or the project: platform=macOS for a macOS scheme; otherwise the first iPhone (iPad for an iPad-only target), Apple Watch or Apple Vision Pro device of the newest supported installed runtime, always with OS=; platform=macOS when the target platform can’t be determined.

workspace / project

No

key: value — path to a .xcworkspace/.xcodeproj to disambiguate when more than one exists (app path only).

None — xcodebuild -list auto-resolves the sole project/workspace when only one exists.

ui (selector prefix)

No

A leading ui word (ui, ui OrderRowTests, ui MyAppUITests-iOS/OrdersScreenUITests) selects a UI run; whatever follows is an ordinary selector. A bare ui is a full run that clears .ui-screenshots/ first; a scoped one keeps it.

Not a UI run

lock

No

key: value — held when the caller (iru-swift-code-one-task-group) already holds .simulator-test.lock, so a ui run doesn’t take it.

The skill takes the lock itself for an app UI run

These key: value overrides are only honored when the calling skill/prompt passes them explicitly; otherwise this skill infers everything itself in Steps 1–3.

Package selector mapping (swift test):

What the user wants Flag Example

Everything

(omit --filter)

swift test

One test/method name

--filter '<name>'

swift test --filter 'testFoo'

One suite/class

--filter '<Suite>'

swift test --filter 'FooTests'

One method in one suite

--filter '<Suite>[./]<method>'

swift test --filter 'FooTests/testBar'

One target/module

--filter '<Target>\.'

swift test --filter 'FooPackageTests\.'

Several disjoint names

repeat --filter '<pattern>' (OR’d, not AND’d)

swift test --filter testPasses --filter 'example\(\)'

Excluding a pattern

--skip '<regex>'

swift test --skip 'SlowTests'

App selector mapping (xcodebuild test):

What the user wants Flag Example

Everything

(omit -only-testing:)

xcodebuild test -scheme Demo -destination 'platform=macOS'

One suite/class

-only-testing:<TestTarget>/<Suite>

-only-testing:DemoTests/FooTests

One method in one suite

-only-testing:<TestTarget>/<Suite>/<test>

-only-testing:DemoTests/FooTests/testBar

Whole test target/bundle

-only-testing:<TestTarget>

-only-testing:DemoTests

Several disjoint scopes

repeat -only-testing:<…​>

-only-testing:DemoTests/FooTests -only-testing:DemoTests/BarTests

Excluding a scope

-skip-testing:<TestTarget>/<Suite>/<test>

-skip-testing:DemoTests/SlowTests

Outputs

  • A plain-text summary in the conversation — never a written file.

  • Package path: totals parsed from the console’s Executed <N> tests, with <F> failures…​/Test run with <N> tests…​ lines (XCTest and Swift Testing both run in one swift test invocation).

  • App path: totals from xcresulttool get test-results summary (passedTests/failedTests/totalTestCount/result).

  • With several per-platform schemes, results reported per scheme; a scheme without a simulator runtime (typically visionOS) listed as "unverified (no visionOS simulator runtime installed)" with its build-for-testing result.

  • On failures: each failing test’s name plus the first line of its assertion/expectation message — for the app path, also the .xcresult bundle path for full detail instead of dumping it.

  • "Selector matched no tests" reported explicitly and distinctly from a clean pass, for the zero-match case both runners otherwise hide behind exit code 0.

  • UI run: .ui-screenshots/<kind>/<Id>/<state>--<direction>--<theme>[--<variant>].png (via UI_SCREENSHOTS_DIR, with captures recovered from the .xcresult through xcresulttool export attachments when the test process couldn’t write them), and a summary with the screenshot count per component/screen/flow, malformed names, accessibility issues (count and rule ids, and whether they failed the run) at the .ui-quality.yml level, whether the directory was cleared or kept, and every scheme reported unverified (no simulator runtime, no desktop session or permission) with its reason.

  • No source or test code is ever modified.

Execution flow

flowchart TD A["Start /iru-swift-test\n(optional selector, or key: value overrides)"] --> B["Step 1: detect package vs app\n(find Package.swift / project.yml /\nProject.swift / *.xcodeproj / *.xcworkspace)"] B --> C{"Kind?"} C -- "package only" --> D["Verify swift --version"] C -- "app (project/workspace present)" --> E["Verify xcodebuild -version\n& full Xcode install"] C -- "project.yml/Project.swift only,\nnot yet generated" --> F["Stop: run xcodegen/tuist\ngenerate first"] C -- "nothing found" --> G["Stop: no Swift package\nor Xcode project found"] D --> H["Step 2: map selector to\nswift test --filter / --skip"] E --> I["Step 3: resolve scheme(s) by scope\n(per-platform schemes) and OS-pinned\ndestination; map selector to\n-only-testing: / -skip-testing:"] I -- "no simulator runtime\n(e.g. visionOS)" --> I2["build-for-testing against generic\nvisionOS Simulator; report scheme\nunverified, other schemes still run"] H --> J["Step 4: run swift test"] I --> K["Step 4: rm -rf .xcresult,\nrun xcodebuild test\n-resultBundlePath (once per scheme)"] J --> L["Step 5: parse console output\n(XCTest + Swift Testing lines)"] K --> M["Step 5: parse .xcresult via\nxcresulttool get test-results\nsummary / tests"] L --> N{"Zero-match case?\n(no Executed / Test run lines)"} M --> O{"Zero-match case?\n(totalTestCount == 0)"} N -- yes --> P["Step 6: report\nselector matched no tests"] O -- yes --> P N -- no --> Q{"Failures?"} O -- no --> R{"Failures?"} Q -- yes --> S["Step 6: list failing tests +\nfirst line of message\n(+ .xcresult path for app)"] R -- yes --> S Q -- no --> T["Step 6: report all passed, briefly"] R -- no --> T A -. "ui selector" .-> U["UI run: clear or keep .ui-screenshots/,\nsimulator lock, run, recover captures from .xcresult,\nvalidate names, collect accessibility issues"] U --> T

Dependencies

Invokes

None — iru-swift-test is a leaf skill; it only runs swift test/xcodebuild test commands and reports on their output, it does not call any other skill in this catalog.

Invoked by

  • Swift Code One Task Group — Step 3.4 (and the ui run of Step 3.7, with lock: held), once per group validation pass, via the iru-gate-runner agent, alongside iru-check-license (3.1), iru-swift-docc (3.2), and iru-swift-coverage (3.5).

  • Swift Generate All Tests — Step 5b (full ui run) and Step 7, via the iru-gate-runner agent, to run the newly generated tests after iru-swift-coverage measures the baseline.

  • iru-gate-runner — both iru-swift-code-one-task-group and iru-swift-generate-all-tests always invoke this skill from inside a iru-gate-runner sub-agent they spawn, not inline.

Source

SKILL.md on GitHub — the file this page was generated from.