mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
437465f37b
The loop that #1874 is investigated with could not tell the truth about itself. It classified every non-`passed` iteration as a stall, which after #2035 gave the looped test an XCTSkipIf meant an environment flip would report a 100% stall rate; it captured cadence only for failures, though an absorbed episode now passes; and it read its logs with shell pipelines whose exit status means "did this match", so an iteration that legitimately matched nothing killed the job before it could be summarized. scripts/diagnose-1874-iteration.ts reads one iteration: xcodebuild's own verdict, the `type-all` duration, and the cadence worth keeping. A nonzero exit outranks a green measured test — in `pair` mode the neighbour or the runner can fail while the measured test passes — and a run that produced no verdict is named as ours rather than counted as a stall. The workflow gains the #1781 lane declaration it never had. Its kill criterion names #2080, which the loop can now serve rather than merely claim to: the looped test is a dispatch input, so the fill route that #2080 traces loops the same way. One test pins the contract the script cannot check about itself — that the workflow hands it the status xcodebuild returned rather than a literal. Closes #1874. Both filed symptoms are resolved. `smoke:form-input` was root-caused and fixed in #2035: the fixture's placeholder was identical to the value every suite filled, so `fill` could never be verified on the penalized route — deterministic, not a flake, and only visible under load because that route is gated on a penalized XCTest channel. The targeted XCTest is mitigated by the progress-aware commit budget, with 200 consecutive green loop iterations across two dispatches. The issue's remaining question — why the input pipeline throttles — is answered by the second dispatch, and the premise was wrong: it does not. Posting 17 characters took 484 ms and the commit was observed on the first poll, inside an iteration whose `type-all` measured 14334 ms. The ~12.6 s went to accessibility round-trips before any character was posted, which is #1105's path, not the input pipeline's.
133 lines
5.1 KiB
YAML
133 lines
5.1 KiB
YAML
name: 1874 Diagnose
|
|
|
|
# Under the lane rule in #1781:
|
|
#
|
|
# - Catches: iOS text-entry commit episodes on CI hardware — the dispatched test looped on both
|
|
# arches, with and without the neighbour test that shares the simulator. An absorbed episode
|
|
# surfaces as a slow pass, so the cadence is kept for passes too.
|
|
# - Evidence: the 2026-08-21 dispatch passed 50/50 and still caught an episode (`type-all`
|
|
# 2487 ms across 5 polls) that no PR lane would report; it does not reproduce locally.
|
|
# - Cost: manual dispatch and the `diagnose/1874-commit-stall` branch only — never a PR or `main`.
|
|
# One dispatch is four macOS slots, each a runner build plus `iterations` iterations of
|
|
# simulator time.
|
|
# - Kill criterion: delete when #2080 closes. It is the last open text-entry question this loop
|
|
# is the CI reproduction path for, and nothing else reads these artifacts.
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
iterations:
|
|
description: 'Loop iterations per job'
|
|
required: false
|
|
default: '25'
|
|
test:
|
|
description: 'RunnerTests method to loop'
|
|
required: false
|
|
default: testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden
|
|
push:
|
|
branches:
|
|
- diagnose/1874-commit-stall
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
stall-loop:
|
|
name: ${{ matrix.arch }} ${{ matrix.mode }} stall loop
|
|
runs-on: macos-26
|
|
timeout-minutes: 90
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
mode: [isolated, pair]
|
|
arch: [x86_64, arm64]
|
|
env:
|
|
IOS_RUNTIME_VERSION: '26.2'
|
|
AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH: ${{ github.workspace }}/.tmp/ios-runner-derived
|
|
AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS: '1'
|
|
AGENT_DEVICE_XCUITEST_ARCHS: ${{ matrix.arch }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup toolchain
|
|
uses: ./.github/actions/setup-node-pnpm
|
|
|
|
- name: Restore and build iOS XCTest runner
|
|
uses: ./.github/actions/setup-apple-runner-build
|
|
with:
|
|
derived-path: ${{ env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH }}
|
|
cache-key-prefix: ios-runner-prebuilt
|
|
cache-key-suffix: -ios-${{ env.IOS_RUNTIME_VERSION }}-1874-${{ matrix.arch }}
|
|
gate: swift-runner-ios
|
|
xcuitest-platform: ios
|
|
xcuitest-destination: generic/platform=iOS Simulator
|
|
|
|
- name: Boot iOS test simulator
|
|
id: ios-simulator
|
|
uses: ./.github/actions/boot-ios-test-simulator
|
|
with:
|
|
runtime-version: ${{ env.IOS_RUNTIME_VERSION }}
|
|
preferred-device-name: iPhone 17 Pro
|
|
|
|
- name: Stall loop
|
|
env:
|
|
ITERATIONS_INPUT: ${{ github.event.inputs.iterations }}
|
|
TEST_INPUT: ${{ github.event.inputs.test }}
|
|
run: |
|
|
set -euo pipefail
|
|
XCTESTRUN_PATH="$(find "$AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH/Build/Products" -maxdepth 1 -name '*.xctestrun' -print -quit)"
|
|
test -n "$XCTESTRUN_PATH"
|
|
UDID="${{ steps.ios-simulator.outputs.simulator-udid }}"
|
|
ITER="${ITERATIONS_INPUT:-25}"
|
|
TEST_NAME="${TEST_INPUT:-testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden}"
|
|
mkdir -p .tmp
|
|
|
|
if [ "${{ matrix.mode }}" = "pair" ]; then
|
|
EXTRA_ONLY_TESTING=(-only-testing:AgentDeviceRunnerUITests/RunnerTests/testBareDelayedTypeFailsWhenTappedInputDisappearsMidCommand)
|
|
else
|
|
EXTRA_ONLY_TESTING=()
|
|
fi
|
|
|
|
sys_prof="$(system_profiler SPHardwareDataType 2>/dev/null | grep -E 'Chip|Cores|Memory' || true)"
|
|
echo "HOST: $sys_prof" | tee stall-summary.txt
|
|
|
|
pass=0; skipped=0; failed=0; noresult=0; runfailed=0
|
|
for i in $(seq 1 "$ITER"); do
|
|
LOG=".tmp/stall-run-$i.log"
|
|
set +e
|
|
xcodebuild test-without-building \
|
|
-xctestrun "$XCTESTRUN_PATH" \
|
|
-destination "platform=iOS Simulator,id=$UDID" \
|
|
-only-testing:"AgentDeviceRunnerUITests/RunnerTests/$TEST_NAME" \
|
|
${EXTRA_ONLY_TESTING[@]+"${EXTRA_ONLY_TESTING[@]}"} \
|
|
> "$LOG" 2>&1
|
|
rc=$?
|
|
set -e
|
|
verdict="$(node --experimental-strip-types scripts/diagnose-1874-iteration.ts \
|
|
"$LOG" "$TEST_NAME" "$i" "$rc")"
|
|
case "$verdict" in
|
|
passed) pass=$((pass+1)) ;;
|
|
skipped) skipped=$((skipped+1)) ;;
|
|
no-result) noresult=$((noresult+1)) ;;
|
|
run-failed) runfailed=$((runfailed+1)) ;;
|
|
*) failed=$((failed+1)) ;;
|
|
esac
|
|
done
|
|
|
|
echo "RESULT arch=${{ matrix.arch }} mode=${{ matrix.mode }}: $pass passed, $skipped skipped, $failed failed, $noresult no-result, $runfailed run-failed of $ITER" | tee -a stall-summary.txt
|
|
|
|
- name: Upload stall evidence
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: stall-evidence-${{ matrix.arch }}-${{ matrix.mode }}
|
|
include-hidden-files: true
|
|
path: |
|
|
stall-summary.txt
|
|
.tmp/stall-evidence-*.log
|
|
if-no-files-found: ignore
|