Files
obsidian-vault/personal/tech/legacy-xctest-runner.md
T

119 lines
12 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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]].