--- title: Legacy XCTest runner (run new-Xcode UI tests on macOS 12) namespace: personal updated: '2026-10-14' tags: - xctest - ui-tests - macos - duckduckgo - virfield --- # Legacy XCTest runner Runs DDG macOS UI tests built with the current Xcode (27.x) on a Mac that can only run an old Xcode, e.g. the MBP 2014 node (Monterey 12.6.8, Intel, Xcode 13.4.1). Location: `~/Developer/legacy-xctest-runner/` (not committed anywhere). ## Why it is needed - New Xcode's `XCTest.framework` and `UI Tests-Runner.app` require macOS 14+. - The test bundle itself targets the app's deployment target (12.3), so it can run if hosted by the old Xcode's runner. - The bundle imports 2 Swift symbols missing from Xcode 13 XCTest: `XCUIElement.typeKey(_:modifierFlags:)` (mangled under `XCUIAutomation`) and `XCTIssue.init(…severity:)`. `XCTestCompat.swift` forwards them. ## How it works - `package.sh ` — collects `DuckDuckGo Review.app`, `tests-server`, `UI Tests.xctest` + runner files. - `run.sh [--gui] [result-name]` on the target Mac: - builds `XCTestCompat.framework` (re-exports old XCTest + libXCTestSwiftSupport, adds forwarders); - hosts the bundle in Xcode 13's `XCTRunner.app`, `install_name_tool` repoints the bundle's XCTest/libXCTestSwiftSupport imports at XCTestCompat; - repeats `macOS UI Tests CI` scheme pre-actions (clean-app review, suppress Move-to-Applications, start tests-server); - `xcodebuild test-without-building` with `UITests.xctestrun` (FormatVersion 1), then kills tests-server and the app (old XCTest doesn't terminate it). - `--gui`: over SSH xcodebuild can't reach the runner; relaunches itself via a Terminal `.command` in the console session, waits, closes finished windows. Build on the host (spell flags out — zsh doesn't word-split `$var`): ```bash xcodebuild build-for-testing -workspace DuckDuckGo.xcworkspace -scheme "macOS UI Tests CI" -destination "platform=macOS,arch=x86_64" -derivedDataPath
ONLY_ACTIVE_ARCH=YES -skipPackagePluginValidation -skipMacroValidation xcodebuild build -workspace DuckDuckGo.xcworkspace -scheme "macOS Browser" -configuration Review -destination "platform=macOS,arch=x86_64" -derivedDataPath
ONLY_ACTIVE_ARCH=YES -skipPackagePluginValidation -skipMacroValidation ``` ## Target Mac prerequisites (once per boot volume, needs sudo) ```bash sudo DevToolsSecurity -enable sudo automationmodetool enable-automationmode-without-authentication ``` Without them testmanagerd shows an admin-password prompt on the display ("Timed out while enabling automation mode"). Virfield's node probe now reports `ui_testing` (`ready` / `devtools_disabled` / `automation_mode_requires_auth` / `no_console_session`); setup script `/Users/admin/Developer/virfield/deploy/node-ui-testing.sh`. ## Gotchas found - Repo `Logger` (`macOS/UITests/Common/UITests.swift`) calls private `-[XCTContext _recordActivityMessageWithFormat:]`; old XCTest lacks it → guard with `responds(to:)` (done on branch `alex/fix-save-credentials-tooltip-crash-macos12`). - Remote shell is zsh: `log` is a builtin — use `/usr/bin/log show`. - MBP has a different SSH host key per boot volume; use the Virfield lease's generated `ssh-config` (pins the booted volume's key). - Hover tracking in the app uses `.activeInKeyWindow`; popovers that open without focus don't get hover events. First used: 2026-10-12, reproducing Sentry crash `NSInternalInconsistencyException SaveCredentialsViewController.loadView` (`isfinite(newOrigin.x)` on macOS 12) — reproduced before fix on the MBP. See also [[virfield]].