Convert the cross-platform swift test suites from XCTest to Swift Testing#25828
Draft
jkmassel wants to merge 6 commits into
Draft
Convert the cross-platform swift test suites from XCTest to Swift Testing#25828jkmassel wants to merge 6 commits into
swift test suites from XCTest to Swift Testing#25828jkmassel wants to merge 6 commits into
Conversation
Collaborator
Generated by 🚫 Danger |
Contributor
|
| App Name | WordPress | |
| Configuration | Release-Alpha | |
| Build Number | 33419 | |
| Version | PR #25828 | |
| Bundle ID | org.wordpress.alpha | |
| Commit | a8840ed | |
| Installation URL | 7htd9uolc1nr8 |
Contributor
|
| App Name | Jetpack | |
| Configuration | Release-Alpha | |
| Build Number | 33419 | |
| Version | PR #25828 | |
| Bundle ID | com.jetpack.alpha | |
| Commit | a8840ed | |
| Installation URL | 2rgbqj4ntk770 |
WordPressFlux's Dispatcher asserts it is only called on the main thread. XCTest ran tests on the main thread; Swift Testing runs them off the main thread, so the suite is pinned to @mainactor. Also removes the dead XCTestManifests.swift, obsolete under Swift Testing.
DebouncerTests keep their Timer-based cases synchronous and spin the run loop, since Swift Testing's async waiting does not pump the run loop the way XCTest's wait(for:timeout:) did.
#25815 (KeystoneTests move) and #25825 (Foundation extensions) relocated 15 more XCTest suites into WordPressSharedTests after this branch was first written; convert them too so the swift test set stays entirely Swift Testing. NotificationCenterObserveOnceTests is pinned to @suite(.serialized) because its two tests share NotificationCenter.default and would race under Swift Testing's parallelism; QueueTests stays a final class because Queue is a value type it mutates.
jkmassel
force-pushed
the
jkmassel/swift-testing-migration
branch
from
July 23, 2026 22:31
bc3811a to
a8840ed
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Converts the last XCTest suites in the repo-root
swift testrun — the macOS cross-platform gate added in #25344 — to Swift Testing. That gate now runs 233 Swift Testing cases across 53 suites and zero XCTest cases.What changed
38 test files across the four targets that still had XCTest, plus one deletion:
GutenbergProcessorsTests(5),JetpackStatsWidgetsCoreTests(3),WordPressFluxTests(1),WordPressSharedTests(29).WordPressCoreTestswas already Swift Testing.WordPressFluxTests/XCTestManifests.swift— the LinuxallTests()manifest is dead under Swift Testing, and was already#if !canImport(ObjectiveC)(empty on Apple platforms).Most of it is mechanical:
XCTestCasesubclass →structsuite,func testX→@Test func,XCTAssert*→#expect/try #require/Issue.record,setUp→init. Four suites needed more than a rote swap.WordPressFluxTestsruns on@MainActornowFlux's
Dispatchertraps off the main thread (assertMainThread(),Dispatcher.swift:41). XCTest ran these on the main thread so they passed; Swift Testing runs on the cooperative pool, so the plain conversion aborted the entireswift testprocess withAssertion failed: Dispatcher should only be called from the main thread— a trap, not a single recorded failure. Pinning the suite to@MainActorrestores main-thread execution.DebouncerTestspumps the run loop instead of awaitingDebouncerschedules aTimeron the current run loop. XCTest'swait(for:timeout:)spun that run loop; Swift Testing'sawait/confirmationdon't, so a naive async port never fires the timer. These stay synchronous and spinRunLoop.current.run(mode:before:)until the callback lands or the deadline passes, wrapped inwithExtendedLifetime—Debouncer.deinitfires the pending callback on dealloc, so releasing it early corrupts the timing.testDebouncerRunsImmediatelyIfReleasedrelies on that same deinit behavior and stays a pure synchronous check.Smaller notes
WidgetDataCacheReaderTests'assert(…line:)helper becomessourceLocation: SourceLocation = #_sourceLocation, forwarded to#expect(…, sourceLocation:)/Issue.record(…, sourceLocation:)so failures still point at the call site.LazyTestsstays afinal class— its@Lazyproperty wrapper mutates and itstearDown(→deinit) resets static state, both wanting reference semantics.XCTAssertEqual(x.count, 0)andXCTAssertEqual(y, "")became#expect(x.isEmpty)/#expect(y.isEmpty): the== 0/== ""forms trip SwiftLint'sempty_count/empty_string, which theXCTAssertEqualargument form did not.xtestPerformancecase — XCTestmeasurehas no Swift Testing equivalent.The relocated suites (#25815, #25825)
After this branch was first written, #25815 (the KeystoneTests move) and #25825 (three Foundation extensions) relocated 15 more XCTest suites into
WordPressSharedTests. The branch is rebased on top of them and converts those too, so the gate stays entirely Swift Testing instead of re-gaining XCTest the moment it merges. Two needed care:NotificationCenterObserveOnceTestsis pinned to@Suite(.serialized)— its two tests shareNotificationCenter.defaultand one observes without a filter, so under Swift Testing's default parallelism it would catch the other's posts; andQueueTestsstays afinal classbecauseQueueis a value type it mutates across each test.The suite runs faster, too
Runner-reported execution time drops from ~1.6s to ~0.5s for the same 233 tests (one warm run: 1.56s → 0.52s). Swift Testing parallelizes by default, whereas
swift testruns XCTest serially within the bundle. The win is entirelyDebouncerTests: its four timer-based cases sleep ~1.4s in total and ran serially under XCTest (Executed 4 tests … in 1.414 seconds), which by itself was most of the before-run. Swift Testing overlaps them with each other and the other 229 tests, so the run collapses to about its single slowest test. Not the goal of the change, just a free improvement to the gate.Not in this PR
RichContentFormatterTests.swiftandWPUserAgentTests.swiftareexclude:d from the macOSWordPressSharedTeststarget (they don't build on macOS), so they're outside theswift testset and stay XCTest.swift-formatwarnings in the Gutenberg fixtures (column-0 multiline HTML strings) are left as-is; reformatting them is unrelated churn. This PR introduces no newswift-formatwarnings.Test plan
swift testfrom the repo root: 233 Swift Testing cases in 53 suites pass; the XCTest runner reportsExecuted 0 tests.rake lint[<changed files>]: 0 violations.swift-format linton the changed files: no new warnings — the three fully-rewritten suites (DebouncerTests,StringRankedSearchTests,URLIncrementalFilenameTests) went from 5/4/6 warnings to 0.Modules/Package.swift(WordPressUnitTests.xctestplan): the WordPress + Jetpack Build for Testing and Unit Tests jobs are green — the conversions run on iOS, not just macOS.