119 lines
12 KiB
Markdown
Executable File
119 lines
12 KiB
Markdown
Executable File
---
|
||
title: Legacy XCTest runner (run new-Xcode UI tests on macOS 12)
|
||
namespace: personal
|
||
updated: '2026-10-17'
|
||
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. macOS 12 + Xcode 13.4.1. Работает на двух типах целей:
|
||
|
||
- MBP 2014 node (Monterey 12.6.8, **Intel x86_64**), 2026-10-12;
|
||
- Tart VM из virfield, шаблон `monterey-xcode-13.4.1` (macOS 12.7.6, **arm64**, Xcode 13.4.1 в `/Applications/Xcode.app`), 2026-10-17.
|
||
|
||
Location: `~/Developer/legacy-xctest-runner/` (not committed anywhere).
|
||
macOS 12 **не входит в CI matrix** (PR: только macOS 27; полный прогон: 14, 15, 27), это чисто локальная проверка. Минимум приложения — macOS 12.3.
|
||
|
||
## 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
|
||
(так что `typeKey(XCUIKeyboardKey, …)` в тестах работает).
|
||
|
||
## How it works
|
||
|
||
- `package.sh <derived-data> <out.tgz>` — collects `DuckDuckGo Review.app`,
|
||
`tests-server`, `UI Tests.xctest` + runner files. Ждёт `Build/Products/Review` с
|
||
`DuckDuckGo Review.app`, `tests-server`, `UI Tests-Runner.app`.
|
||
- `run.sh [--gui] <TestClass[/testMethod]> [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;
|
||
- **режет runner до нативного слайса** (`lipo -thin`, см. ниже), добавлено 2026-10-17;
|
||
- 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.
|
||
- Env `SKIP_BUILD=1` (патч на VM, в локальный `run.sh` не внесён): пропустить `build_compat` и `assemble_runner`, если уже собраны. Нужен для запуска по одному тесту.
|
||
|
||
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 <dd> ONLY_ACTIVE_ARCH=YES -skipPackagePluginValidation -skipMacroValidation
|
||
xcodebuild build -workspace DuckDuckGo.xcworkspace -scheme "macOS Browser" -configuration Review -destination "platform=macOS,arch=x86_64" -derivedDataPath <dd> ONLY_ACTIVE_ARCH=YES -skipPackagePluginValidation -skipMacroValidation
|
||
```
|
||
|
||
Для Apple Silicon VM архитектура `arm64`. Подписанное на хосте приложение 2026-10-17 собирали так: `xcodebuild archive -scheme "macOS Browser Review" -configuration Review … EXCLUDED_ARCHS=x86_64 RELEASE_PRODUCT_NAME_OVERRIDE=DuckDuckGo` + `-exportArchive` с **копией** `macOS/scripts/assets/ExportOptions.plist` (Developer ID, `signingCertificate` = SHA-1 из keychain; tracked plist не править, как делает `archive.sh`). Нотаризация внутри VM не нужна. Профили `match Direct …review` уже лежат в `~/Library/Developer/Xcode/UserData/Provisioning Profiles`.
|
||
|
||
## 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`.
|
||
В Tart VM `monterey-xcode-13.4.1` Developer Mode уже включён, sudo без пароля; `automationmodetool` выполнить надо.
|
||
|
||
## Gotchas found
|
||
|
||
**Запуск**
|
||
|
||
- **arm64e: SIGKILL 137 на старте раннера (Apple Silicon).** `XCTRunner.app` из Xcode 13.4.1 — universal `x86_64 arm64 arm64e`, ядро выбирает arm64e: `exec_mach_imgact: not running binary "UI Tests-Runner" built against preview arm64e ABI`; xcodebuild: «Launched process exited during launch» / `Failed to install or launch the test runner`. Лечится `lipo -thin $(uname -m)` в `assemble_runner` (уже в `run.sh`). На Intel не проявлялось.
|
||
- `--gui` запускает себя из `.command` по **`$0`**; из SSH вызывать `run.sh` только по **абсолютному пути**, иначе `./run.sh: No such file or directory` в Terminal (cwd=home).
|
||
- `LEGACY_XCODE` из окружения SSH **не доходит** до Terminal `.command`: `xcrun: error: missing DEVELOPER_DIR path: /Applications/Xcode13.4.1.app`. Либо `sudo ln -sfn /Applications/Xcode.app /Applications/Xcode13.4.1.app`, либо править дефолт.
|
||
- Over SSH xcodebuild без `--gui` не достаёт раннер; remote shell — zsh: нет `timeout` (используй `perl -e 'alarm N; exec @ARGV'`), `$var` не делится на слова, `log` — builtin (`/usr/bin/log show`).
|
||
- Exit code `.command`-обёртки (`exit=0` в логах) **не равен** результату тестов. Смотреть `Executed N tests, with M failures` и `Test Case … passed|failed`. `Executed 0 tests, with 1 failure` = упал class `setUp`.
|
||
- Старый xcodebuild пишет `LLVM Profile Error: Failed to write file default.profraw: Read-only file system` — безвредно.
|
||
- Приложение: LSMinimumSystemVersion 12.3. arm64 VM требует arm64-сборку (Review app после `EXCLUDED_ARCHS=x86_64`, test bundle arm64).
|
||
|
||
**Тесты**
|
||
|
||
- **Старый XCTest не завершает приложение между тестами.** При запуске класса целиком каждый следующий тест стартует поверх предыдущего: `Failed to synthesize event: Unable to synthesize gesture request N 'click' because gesture request M 'Type 'n' key with modifiers ⌘' is still in progress`, `Timed out while evaluating UI query`, лавина падений без реальных assert (первый прогон: 49 «failures» на 25 тестах Duck.ai, 42 на 24 тестах поискового класса). Тот же тест по одному проходит за ~5 с. **Решение: один `xcodebuild` на каждый тест** со свежим `prepare_environment` (чистые defaults, перезапуск tests-server и приложения), `SKIP_BUILD=1` после первого. 88 тестов ≈ 25–30 мин. Цикл `each.sh` читает `tests-list.txt` (`Class/method`), пишет `summary-each.txt`, запускается из Terminal `.command`.
|
||
- **`UITests.setAutocompleteToggleBeforeTestcaseRuns(false)` на macOS 12 падает по таймауту** (`Failed to get matching snapshots: Timed out while evaluating UI query`, окно Settings) → тесты класса не стартуют (`Executed 0 tests, with 1 failure`, ~190 с). Затронуты все классы с этим `setUp`, включая `AddressBarKeyboardShortcutsTests`. Локальный обход без коммита: убрать вызов хелпера при сборке бандла и в `prepare_environment` писать `defaults write com.duckduckgo.macos.browser.review preferences.appearance.show-autocomplete-suggestions -bool false`.
|
||
- Duck.ai: ввод сразу после Shift+Enter теряется, пока text view не получил фокус (на macOS 12 в ~3 из 25 тестов) → повторять Cmd+A и ввод, пока содержимое не совпало.
|
||
- Редкий флейк: `Failed to compute hit point for (null)` → раннер падает при снятии скриншота (`Restarting after unexpected exit, crash, or test timeout`, итог `noresult`). Перезапуск одного теста помогает.
|
||
- Repo `Logger` (`macOS/UITests/Common/UITests.swift`) calls private
|
||
`-[XCTContext _recordActivityMessageWithFormat:]`; old XCTest lacks it → guard
|
||
with `responds(to:)` (в main уже есть).
|
||
- Hover tracking in the app uses `.activeInKeyWindow`; popovers that open without
|
||
focus don't get hover events.
|
||
- Текст Duck.ai omnibar (`AIChatOmnibarTextContainerViewController.textView`) **не виден в accessibility-дереве** (контейнер `…view` — одиночный `Other`), в том числе на macOS 27. Ключи шлются через `app.typeKey`, текст читается через Cmd+A, Cmd+C и `NSPasteboard`.
|
||
|
||
**Окружение / запись**
|
||
|
||
- `screencapture -v` на macOS 12 VM: `capture error The operation could not be completed` и по SSH, и из Terminal в GUI-сессии (нет Screen Recording TCC, SIP включён, выдать нельзя). На macOS 27 golden `uitest-27-v1` работает по обычному SSH. Для macOS 12 записи экрана нет — только логи и `.xcresult`.
|
||
- MBP has a different SSH host key per boot volume; use the Virfield lease's
|
||
generated `ssh-config` (pins the booted volume's key). Для Tart VM IP (192.168.64.x) доступен напрямую с хоста.
|
||
- Диск Tart: `samsung-tart` ≈214 ГБ, macOS 27 clone резервирует 140 ГБ, Monterey 120 ГБ — вместе не помещаются, см. [[virfield]].
|
||
|
||
## Находки на macOS 12 (PR 4548, address bar word selection)
|
||
|
||
2026-10-17, ветка `alex/bug-08b-addressbar-selection`: тесты Opt+Shift+←/→, Cmd+Shift+←/→, Shift+←/→, Cmd+A, delete/forward-delete в адресной строке (URL, поисковый запрос, суффикс) и Duck.ai.
|
||
|
||
- На macOS 12 после **Cmd+Shift+←** `selectionAffinity` остаётся прежним (на 27 становится `.upstream`) → Opt+Shift+→ не сжимала выделение слева. Исправлено явной установкой affinity в `AddressBarTextEditor` (`moveToBeginningOfLine/LeftEndOfLine/EndOfLine/RightEndOfLine…AndModifySelection`). Тот же класс проблемы — `selectAll`: affinity теперь явно `.downstream`.
|
||
|
||
## Результаты запусков
|
||
|
||
- 2026-10-12, MBP 2014 (Intel): воспроизведён Sentry crash `NSInternalInconsistencyException SaveCredentialsViewController.loadView` (`isfinite(newOrigin.x)` on macOS 12) до фикса.
|
||
- 2026-10-17, Tart VM macOS 12.7.6 arm64: по-тестовый прогон 88 новых тестов. Первый проход 85 passed / 2 failed (находка про Cmd+Shift+← affinity) / 1 noresult (флейк скриншота). После фикса три теста перепроверены — passed.
|
||
- Полный прогон на финальном коде: 85 passed / 3 failed. Все три — Duck.ai: `setUp` набирает prompt сразу после Shift+Enter, клавиши теряются, пока text view не взял фокус (`XCTAssertEqual failed: ("") is not equal to ("alpha beta gamma delta")`). В тесте добавлен retry (до 3 попыток Cmd+A и ввод), класс Duck.ai перегнан — 25/25 passed. Итого 88/88 на macOS 12.
|
||
- Те же тесты на macOS 27 (Xcode 27.0) — 88/88 с записью экрана; PR 4548 в duckduckgo/apple-browsers, CI UI Tests на 14/15/27 запущен вручную (workflow_dispatch).
|
||
|
||
See also [[virfield]].
|