From ac99d112ed713fc066c81249d751b93571f3bbe2 Mon Sep 17 00:00:00 2001 From: Eduardo Nunes Date: Mon, 9 Nov 2026 15:44:19 -0300 Subject: [PATCH 1/2] feat: skip the Swift Testing tests a skip list names The `.coverageAttribution` trait asks the observer whether the skip list names the running test and, when it does, cancels it with `Test.cancel` before it starts, which reports it as skipped. `Test.cancel` arrived in Swift 6.3, so older compilers run the listed tests: a test a trait doesn't run is reported as passed. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- Fixtures/UITests/AppTests/SkipListTests.swift | 14 ++++- README.md | 22 ++++++-- .../CoverageAttributionTrait.swift | 19 ++++--- .../TestCoverageAttributionObserver.m | 18 +++++-- .../include/TestCoverageAttributionObserver.h | 6 +++ .../AttributionTests.swift | 12 ++++- .../SkipListTests.swift | 53 ++++++++++++++----- mise/tasks/test-skip-list | 2 +- 9 files changed, 113 insertions(+), 35 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 80a41a0..d08db4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ A Swift package that test targets link to record which coverage counters each te ## Layout - `Sources/TestCoverageAttributionObserver` - Objective-C, Foundation only. XCTest is looked up at run time (class, protocol and selectors by name), so nothing links XCTest. It reads each instrumented image's `__llvm_prf_cnts` directly and never calls the LLVM profile runtime. Registers its XCTest observer from a constructor when the test bundle loads, and hooks `-[XCUIApplication launch]`/`-activate` once the bundle starts, to list the app processes UI tests launch. Its constructor also reads the skip list (`TEST_COVERAGE_ATTRIBUTION_SKIP_FILE`) and hooks `+[XCTestCase defaultTestSuite]` to remove the listed tests; it lives in this file because `-ObjC` loads only archive members that define a class. -- `Sources/TestCoverageAttribution` - the Swift Testing trait (`.coverageAttribution`) that calls `test_coverage_attribution_scope_begin/end` around each test. It's kept but undocumented (`@_documentation(visibility: private)`, not in the README): test-scoped evidence and skipping are supported for XCTest and XCUITest only. +- `Sources/TestCoverageAttribution` - the Swift Testing trait (`.coverageAttribution`) that calls `test_coverage_attribution_scope_begin/end` around each test, and cancels the tests `test_coverage_attribution_is_skipped` finds in the skip list. Cancelling needs `Test.cancel`, so it's compiled only with Swift 6.3 or later; before, listed tests run, since a test a trait doesn't run is reported as passed. - `Tests/TestCoverageAttributionTests` - runs the fixtures with attribution on and parses what the observer wrote: `Records.swift` mirrors the `records.bin` layout and `FunctionTable.swift` maps counters to function names as Tuist's reader does. - `Fixtures/Example` - with SwiftPM, xcodebuild on macOS and xcodebuild on an iOS simulator. - `Fixtures/Concurrency` - the overlapping, nested-trait and failure cases, in a package of their own since Swift Testing runs suites in parallel across the process. diff --git a/Fixtures/UITests/AppTests/SkipListTests.swift b/Fixtures/UITests/AppTests/SkipListTests.swift index dd72fe1..bc0c99e 100644 --- a/Fixtures/UITests/AppTests/SkipListTests.swift +++ b/Fixtures/UITests/AppTests/SkipListTests.swift @@ -1,3 +1,4 @@ +import TestCoverageAttribution import Testing import XCTest @@ -44,7 +45,18 @@ final class UnlistedTests: XCTestCase { func testRuns() {} } -/// Listed too, but Swift Testing tests don't go through XCTest's suites, so it runs. +/// The trait cancels the listed tests from Swift 6.3 on, which reports them as skipped; before, +/// they run. +@Suite(.coverageAttribution) struct SwiftTestingTests { @Test func runs() {} + + @Test func skipped() {} + + @Test(arguments: [1, 2]) func skippedWithArguments(value _: Int) {} +} + +/// Listed too, but without the trait nothing reads the list, so it runs. +struct UntraitedSwiftTestingTests { + @Test func runs() {} } diff --git a/README.md b/README.md index a957315..86f37a8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ TestCoverageAttribution records which code each test executes. Xcode's code cove [Tuist](https://tuist.dev) uses it to collect per-test coverage evidence, which lets a run that skips tests reuse those tests' coverage from an earlier run, and to skip the tests a change can't affect. -Both work for XCTest tests, UI tests included. Swift Testing tests run as usual: they get no per-test evidence and are never skipped one by one. +Both work for XCTest tests, UI tests included. Swift Testing tests get them in suites that have the `.coverageAttribution` trait: evidence when a test doesn't run at the same time as another, and skipping with Swift 6.3 (Xcode 26.4) or later. ## Installation @@ -43,7 +43,19 @@ The package records on Apple platforms only. Elsewhere, such as Linux, it builds **UI tests** need nothing else either. The app a UI test drives runs in a process of its own, which the package can't observe, but Xcode profiles that process separately when code coverage is on. The package records which test launched which app process, so a reader such as Tuist can attribute the app's coverage to the test. See [`launches.tsv`](#output). -**Swift Testing** isn't supported. Swift Testing tests run as usual, and their coverage is in the run's report as always, but no test-scoped evidence is recorded for them and the skip list doesn't apply to them. Swift Testing has no observation center a library can join, it runs tests in parallel within a process by default, and a trait has no public way to skip a test. +**Swift Testing** needs the `.coverageAttribution` trait on each suite, because Swift Testing has no observation center a library can join. The trait applies to the suites nested in the one that has it. + +```swift +import TestCoverageAttribution +import Testing + +@Suite(.coverageAttribution) +struct DiscountTests { + @Test func appliesDiscount() { … } +} +``` + +Swift Testing runs tests in parallel within a process by default, and the counters can't tell tests that run at the same time apart: their records are marked overlapped, and a reader should give them no evidence. A test gets evidence when no other test runs at the same time, as in a run with parallelization off; `.serialized` only orders a suite's own tests. Tests in suites without the trait run as usual and get none. ## When it records @@ -68,7 +80,7 @@ Supported platforms are macOS and the iOS simulator. On a device, the process ca ## Skipping tests -A runner that knows which tests a change can't affect, such as Tuist's coverage-based selective testing, can have the package remove them before XCTest runs them. Set `TEST_COVERAGE_ATTRIBUTION_SKIP_FILE` to the absolute path of a UTF-8 file that lists them, one per line. A relative path is read from the test process's working directory, such as the simulator app's, where the file isn't. +A runner that knows which tests a change can't affect, such as Tuist's coverage-based selective testing, can have the package skip them before they run. Set `TEST_COVERAGE_ATTRIBUTION_SKIP_FILE` to the absolute path of a UTF-8 file that lists them, one per line. A relative path is read from the test process's working directory, such as the simulator app's, where the file isn't. ```text CheckoutTests/DiscountTests/testAppliesDiscount() @@ -85,7 +97,7 @@ TEST_RUNNER_TEST_COVERAGE_ATTRIBUTION_SKIP_FILE="$PWD/skip.txt" xcodebuild test - **Forgiving.** Blank lines and tests that don't exist are ignored. An unreadable file skips nothing. Skipping doesn't depend on `TEST_COVERAGE_ATTRIBUTION_DIR`. - **Where it applies.** `xcodebuild` passes the variable to the test process with the `TEST_RUNNER_` prefix: the app that hosts unit tests, and the runner of UI tests. It works with `test` and `test-without-building`, with `-only-testing`, and with parallel testing, where Xcode lists the tests, without the removed ones, before it hands them out to its clones. `swift test` reads it from the environment. - **What a skipped test looks like.** It's removed from the suite XCTest builds for its class, so it doesn't run and isn't in the result bundle. A class whose tests are all removed doesn't run its class `setUp` and `tearDown`. A class with tests left runs them as usual, class `setUp` included. -- **XCTest only.** Swift Testing tests in the same target run as usual, even when listed. +- **Swift Testing.** In a suite with the `.coverageAttribution` trait, a listed test is cancelled before it starts, every argument of a parameterized test included, and reported as skipped, whether tests run in parallel or not. The line names it as the records do: the Swift module, the innermost suite, and the test's display name when it declares one (`Applies a discount`), its function otherwise (`appliesDiscount()`). A display name with a `/` can't be listed. This needs Swift 6.3 (Xcode 26.4) or later: before, a trait can skip a test only by not running it, which reports it as passed, so the listed tests run. Listed tests in suites without the trait run too. ## Output @@ -119,7 +131,7 @@ The deltas are what turns counters into lines. In the image's coverage mapping ( ## Limitations -- **Swift Testing isn't attributed or skipped.** See [Usage](#usage). +- **Swift Testing needs the trait.** Tests in suites without it aren't attributed or skipped, tests that run at the same time get no evidence, and skipping them needs Swift 6.3. See [Usage](#usage). - **Skipping some of a class's tests uses a private method.** XCTest has no public way to remove tests from a class's suite, so the package uses `XCTestSuite`'s private `-setTests:`, which keeps the suite and the class `setUp` it runs. If a future Xcode removes it, a class with some tests listed runs all of them; a class with all of its tests listed is still skipped. A class that overrides `defaultTestSuite` without calling `super` isn't filtered. - **UI tests record which app process each test launched, not the app's counters.** The app's coverage comes from the profile Xcode writes for each app process (`-.profraw` beside the run's `Coverage.profdata`). The pid is read from `XCUIApplication`'s private `processID` property; if a future Xcode removes it, nothing is written. Processes the app starts itself, such as extensions, aren't listed. - **Images loaded late aren't observed.** An image loaded after the first test starts, such as a framework the tests `dlopen`, is missing from the records. diff --git a/Sources/TestCoverageAttribution/CoverageAttributionTrait.swift b/Sources/TestCoverageAttribution/CoverageAttributionTrait.swift index 5b407e2..f8a3950 100644 --- a/Sources/TestCoverageAttribution/CoverageAttributionTrait.swift +++ b/Sources/TestCoverageAttribution/CoverageAttributionTrait.swift @@ -4,14 +4,11 @@ import Testing /// Attributes the code each Swift Testing test executes to that test, by marking where each test -/// starts and ends: Swift Testing has no observation center a library can join. +/// starts and ends: Swift Testing has no observation center a library can join. It also skips the +/// tests the skip list names, by cancelling them before they run. /// -/// Kept, but hidden from the documentation and the README: test-scoped evidence and skipping are -/// supported for XCTest and XCUITest only. The trait has to be added to every suite, Swift Testing -/// runs tests in parallel within a process by default, which leaves overlapping tests without -/// evidence, and a trait has no public way to skip a test. Projects that use it anyway get records -/// as before. -@_documentation(visibility: private) +/// The trait has to be added to every suite, and Swift Testing runs tests in parallel within a +/// process by default, which leaves overlapping tests without evidence. public struct CoverageAttributionTrait: TestTrait, SuiteTrait, TestScoping { /// Whether a scope is already open around the running test case: a test in a suite nested in /// another that has the trait gets it once from each, and two scopes would mark it overlapped. @@ -37,6 +34,13 @@ public struct CoverageAttributionTrait: TestTrait, SuiteTrait, TestScoping { // The name the result bundle reports the test under, which is the test's identity // everywhere else: its display name when it declares one, its function otherwise. let name = test.displayName ?? components.last ?? test.name + #if compiler(>=6.3) + // `Test.cancel` reports the test as skipped. Before Swift 6.3 a trait can only + // skip a test by not running it, which reports it as passed, so it runs instead. + if test_coverage_attribution_is_skipped(module, suite, name) { + try Test.cancel("Listed in TEST_COVERAGE_ATTRIBUTION_SKIP_FILE", sourceLocation: test.sourceLocation) + } + #endif test_coverage_attribution_scope_begin(module, suite, name) defer { test_coverage_attribution_scope_end(module, suite, name) } try await Self.$isInScope.withValue(true) { @@ -50,7 +54,6 @@ public struct CoverageAttributionTrait: TestTrait, SuiteTrait, TestScoping { extension Trait where Self == CoverageAttributionTrait { /// See ``CoverageAttributionTrait``. - @_documentation(visibility: private) public static var coverageAttribution: Self { Self() } diff --git a/Sources/TestCoverageAttributionObserver/TestCoverageAttributionObserver.m b/Sources/TestCoverageAttributionObserver/TestCoverageAttributionObserver.m index 57fe4f3..116d61e 100644 --- a/Sources/TestCoverageAttributionObserver/TestCoverageAttributionObserver.m +++ b/Sources/TestCoverageAttributionObserver/TestCoverageAttributionObserver.m @@ -26,9 +26,9 @@ // launches.tsv one line per process a UI test launched, see record_launch() // // Skipping: $TEST_COVERAGE_ATTRIBUTION_SKIP_FILE (through xcodebuild, -// TEST_RUNNER_TEST_COVERAGE_ATTRIBUTION_SKIP_FILE), an absolute path, names a file of XCTest tests -// not to run, which are removed before XCTest runs them. See load_skip_list(). It works with or -// without recording. +// TEST_RUNNER_TEST_COVERAGE_ATTRIBUTION_SKIP_FILE), an absolute path, names a file of tests not to +// run: XCTest tests are removed before XCTest runs them, and the trait cancels Swift Testing tests. +// See load_skip_list(). It works with or without recording. #import #import "TestCoverageAttributionObserver.h" #include @@ -406,8 +406,9 @@ static bool is_class_suite(NSString *name) { // The listed tests are removed from +[XCTestCase defaultTestSuite], the suite XCTest builds for // each class, both when it lists the tests (parallel runs hand the listed tests out to their // clones) and when it runs them. A removed test isn't in the result bundle, and a class whose -// tests are all removed doesn't run its class setUp. Only XCTest is affected: Swift Testing tests -// don't go through XCTest's suites. +// tests are all removed doesn't run its class setUp. Swift Testing tests don't go through XCTest's +// suites: the `.coverageAttribution` trait asks test_coverage_attribution_is_skipped() and cancels +// the listed ones. static NSSet *skipped_tests; static IMP original_default_test_suite; @@ -437,6 +438,13 @@ static void load_skip_list(void) { if (tests.count > 0) skipped_tests = tests; } +// The list is read once, by the constructor, and never changes after: no lock is needed. +bool test_coverage_attribution_is_skipped(const char *module, const char *suite, const char *name) { + if (!skipped_tests || !module || !suite || !name) return false; + NSString *identifier = skip_identifier(@(module), @(suite), @(name)); + return identifier && [skipped_tests containsObject:identifier]; +} + // A class's suite holds its tests. When some are listed, the suite is kept and its tests replaced // through XCTestSuite's private -setTests:, since the suite is what runs the class setUp and // tearDown; an XCTest without it runs every test. When all are listed, an empty suite takes its diff --git a/Sources/TestCoverageAttributionObserver/include/TestCoverageAttributionObserver.h b/Sources/TestCoverageAttributionObserver/include/TestCoverageAttributionObserver.h index 69fb59c..2aede35 100644 --- a/Sources/TestCoverageAttributionObserver/include/TestCoverageAttributionObserver.h +++ b/Sources/TestCoverageAttributionObserver/include/TestCoverageAttributionObserver.h @@ -1,6 +1,8 @@ #ifndef TEST_COVERAGE_ATTRIBUTION_OBSERVER_H #define TEST_COVERAGE_ATTRIBUTION_OBSERVER_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -13,6 +15,10 @@ void test_coverage_attribution_scope_begin(const char *module, const char *suite /// Ends the scope `test_coverage_attribution_scope_begin` started and records it. void test_coverage_attribution_scope_end(const char *module, const char *suite, const char *name); +/// Whether the skip list (`TEST_COVERAGE_ATTRIBUTION_SKIP_FILE`) names the test, matched the way +/// XCTest tests are. +bool test_coverage_attribution_is_skipped(const char *module, const char *suite, const char *name); + #ifdef __cplusplus } #endif diff --git a/Tests/TestCoverageAttributionTests/AttributionTests.swift b/Tests/TestCoverageAttributionTests/AttributionTests.swift index f7b113e..27612a7 100644 --- a/Tests/TestCoverageAttributionTests/AttributionTests.swift +++ b/Tests/TestCoverageAttributionTests/AttributionTests.swift @@ -142,20 +142,26 @@ struct AttributionTests { } /// Runs one of the packages under `Fixtures`; with SwiftPM, only the tests `filter` matches. - /// `inspect` gets the output directory before it is removed. + /// `skipList` is the skip file's contents. `inspect` gets the output directory before it is + /// removed. /// `Concurrency` is a package of its own: Swift Testing runs suites in parallel across the /// process, so its tests would overlap the others. - private func runFixture( + func runFixture( _ runner: Runner, attributing: Bool, fixture name: String = "Example", filter: String? = nil, + skipList: String? = nil, inspect: (URL) throws -> Void = { _ in }, ) throws -> [Record] { let scratch = FileManager.default.temporaryDirectory.appending(path: "TestCoverageAttribution-\(UUID().uuidString)") defer { try? FileManager.default.removeItem(at: scratch) } let output = scratch.appending(path: "output") try FileManager.default.createDirectory(at: output, withIntermediateDirectories: true) + let skipFile = scratch.appending(path: "skip.txt") + if let skipList { + try skipList.write(to: skipFile, atomically: true, encoding: .utf8) + } let fixture = Self.packageRoot.appending(path: "Fixtures/\(name)") let build = Self.packageRoot.appending(path: ".build/fixtures/\(name)") @@ -164,12 +170,14 @@ struct AttributionTests { switch runner { case .swiftPM: environment["TEST_COVERAGE_ATTRIBUTION_DIR"] = attributing ? output.path() : nil + environment["TEST_COVERAGE_ATTRIBUTION_SKIP_FILE"] = skipList == nil ? nil : skipFile.path() arguments = [ "swift", "test", "--package-path", fixture.path(), "--scratch-path", build.appending(path: "swiftpm").path(), "--enable-code-coverage", ] + (filter.map { ["--filter", $0] } ?? []) case .xcodebuild, .iOSSimulator: environment["TEST_RUNNER_TEST_COVERAGE_ATTRIBUTION_DIR"] = attributing ? output.path() : nil + environment["TEST_RUNNER_TEST_COVERAGE_ATTRIBUTION_SKIP_FILE"] = skipList == nil ? nil : skipFile.path() let destination = runner == .iOSSimulator ? "id=\(try Self.iPhoneSimulator(fixture: fixture))" : "platform=macOS" arguments = [ "xcodebuild", "test", "-scheme", "\(name)-Package", "-destination", destination, diff --git a/Tests/TestCoverageAttributionTests/SkipListTests.swift b/Tests/TestCoverageAttributionTests/SkipListTests.swift index 0aa1aa0..c0ed63d 100644 --- a/Tests/TestCoverageAttributionTests/SkipListTests.swift +++ b/Tests/TestCoverageAttributionTests/SkipListTests.swift @@ -12,10 +12,12 @@ extension AttributionTests { /// The listed XCTest tests of a unit test target hosted in the app and of a UI test target /// don't run: they aren't in the result bundle, and a class whose tests are all listed doesn't - /// run its class `setUp`, which crashes. The tests left run, after their class `setUp`, and so - /// does a listed Swift Testing test. + /// run its class `setUp`, which crashes. The tests left run, after their class `setUp`. Built + /// with Swift 6.3 or later, the listed Swift Testing tests of a suite with the trait are + /// skipped, every argument of a parameterized one included; before, they run. A listed Swift + /// Testing test without the trait runs. @Test(arguments: SkipListRun.allCases) - func removesTheListedXCTestTestsBeforeTheyRun(run: SkipListRun) throws { + func skipsTheListedTests(run: SkipListRun) throws { let scratch = FileManager.default.temporaryDirectory.appending(path: "TestCoverageAttribution-\(UUID().uuidString)") defer { try? FileManager.default.removeItem(at: scratch) } try FileManager.default.createDirectory(at: scratch, withIntermediateDirectories: true) @@ -29,7 +31,9 @@ extension AttributionTests { AppTests/SkippedTests/testFirst() AppTests/SkippedTests/testSecond() - AppTests/SwiftTestingTests/runs() + AppTests/SwiftTestingTests/skipped() + AppTests/SwiftTestingTests/skippedWithArguments(value:) + AppTests/UntraitedSwiftTestingTests/runs() AppTests/UnlistedTests AppTests/MissingTests/testMissing() SkipUITests/PartlySkippedUITests/testSkipped() @@ -38,19 +42,44 @@ extension AttributionTests { let resultBundle = scratch.appending(path: "Tests.xcresult") try runSkipListScheme(run, skipList: skipList, resultBundle: resultBundle) + #if compiler(>=6.3) + let listedSwiftTesting = "Skipped" + #else + let listedSwiftTesting = "Passed" + #endif let results = try Self.testResults(in: resultBundle) - #expect(results.allSatisfy { $0.result == "Passed" }, "\(results)") - #expect(results.map(\.identifier).sorted() == (run == .shard ? [ - "AppTests/PartlySkippedTests/testRuns()", - "SkipUITests/PartlySkippedUITests/testLaunch()", + #expect(results.map(\.description).sorted() == (run == .shard ? [ + "AppTests/PartlySkippedTests/testRuns() Passed", + "SkipUITests/PartlySkippedUITests/testLaunch() Passed", ] : [ - "AppTests/PartlySkippedTests/testRuns()", - "AppTests/SwiftTestingTests/runs()", - "AppTests/UnlistedTests/testRuns()", - "SkipUITests/PartlySkippedUITests/testLaunch()", + "AppTests/PartlySkippedTests/testRuns() Passed", + "AppTests/SwiftTestingTests/runs() Passed", + "AppTests/SwiftTestingTests/skipped() \(listedSwiftTesting)", + "AppTests/SwiftTestingTests/skippedWithArguments(value:) \(listedSwiftTesting)", + "AppTests/UnlistedTests/testRuns() Passed", + "AppTests/UntraitedSwiftTestingTests/runs() Passed", + "SkipUITests/PartlySkippedUITests/testLaunch() Passed", ])) } + /// Built with Swift 6.3 or later, the trait cancels a listed Swift Testing test before it + /// starts, so it leaves no record; before, it runs. The list names it by its display name. + @Test func skipsTheListedSwiftTestingTests() throws { + let records = try runFixture( + .swiftPM, + attributing: true, + skipList: "ExampleTests/MultiplicationTests/Multiplying by zero\n", + ) + let names = records.filter { $0.kind == .swiftTesting }.map(\.name) + + #expect(names.contains("multiply()")) + #if compiler(>=6.3) + #expect(!names.contains("Multiplying by zero")) + #else + #expect(names.contains("Multiplying by zero")) + #endif + } + /// Without a skip list, every test runs, those the list would name included. @Test func runsEveryTestWithoutASkipList() throws { let fixture = Self.packageRoot.appending(path: "Fixtures/UITests") diff --git a/mise/tasks/test-skip-list b/mise/tasks/test-skip-list index 942d379..f5aca9e 100755 --- a/mise/tasks/test-skip-list +++ b/mise/tasks/test-skip-list @@ -2,4 +2,4 @@ #MISE description="Run the skip list's tests: hosted unit tests and UI tests on an iOS simulator, serially, in parallel and as a shard" set -euo pipefail -swift test --package-path "$MISE_PROJECT_ROOT" --filter 'removesTheListedXCTestTestsBeforeTheyRun|runsEveryTestWithoutASkipList' +swift test --package-path "$MISE_PROJECT_ROOT" --filter 'skipsTheListedTests|runsEveryTestWithoutASkipList' From a6aac944ea802c3f38589568cda1f7201f7239e9 Mon Sep 17 00:00:00 2001 From: Eduardo Nunes Date: Mon, 9 Nov 2026 15:54:45 -0300 Subject: [PATCH 2/2] test: run the SwiftPM Swift Testing skip test with the skip list's tests `mise run test-skip-list`, which CI runs on the oldest and newest Xcode, filtered on `skipsTheListedTests`, which doesn't match `skipsTheListedSwiftTestingTests`. The full suite runs only on Xcode 26.3 (Swift 6.2), so the Swift 6.3 cancel path under SwiftPM never ran in CI. Co-Authored-By: Claude Opus 5.5 --- mise/tasks/test-skip-list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mise/tasks/test-skip-list b/mise/tasks/test-skip-list index f5aca9e..562adf2 100755 --- a/mise/tasks/test-skip-list +++ b/mise/tasks/test-skip-list @@ -1,5 +1,5 @@ #!/bin/bash -#MISE description="Run the skip list's tests: hosted unit tests and UI tests on an iOS simulator, serially, in parallel and as a shard" +#MISE description="Run the skip list's tests: hosted unit tests and UI tests on an iOS simulator, serially, in parallel and as a shard, and Swift Testing skipping under SwiftPM" set -euo pipefail -swift test --package-path "$MISE_PROJECT_ROOT" --filter 'skipsTheListedTests|runsEveryTestWithoutASkipList' +swift test --package-path "$MISE_PROJECT_ROOT" --filter 'skipsTheListed|runsEveryTestWithoutASkipList'