Skip to content

Commit 72f1af0

Browse files
committed
test: expose iOS simulator hangs
1 parent 99889cb commit 72f1af0

5 files changed

Lines changed: 140 additions & 19 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,6 @@ jobs:
5353
IOS_TEST_TIMEOUT_MS: "600000"
5454
IOS_TEST_INACTIVITY_TIMEOUT_MS: "180000"
5555
IOS_LOG_JUNIT: "1"
56+
IOS_TEST_VERBOSE_SPECS: "1"
5657
IOS_SIMCTL_QUERY_TIMEOUT_MS: "10000"
5758
run: npm run test:ios

HANDOFF.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,43 @@ During this thread `agent-device` was version `0.18.0`; the npm package is
110110

111111
## Current Verified State
112112

113+
Update from 2026-06-29 17:34 EDT:
114+
115+
- Simulator-only rule remains active. Do not use physical iPhone/iPad devices.
116+
- Draft PR #46 branch is still
117+
`codex/rn-module-fabric-turbomodule-worklets` and cleanly separated from the
118+
original Node-API/direct-engine `refactor` goal.
119+
- GitHub Actions run `28402087730` on `99889cb0` kept setup, dependency
120+
install, FFI boundary check, V8 download, libffi build, metadata generation,
121+
NativeScript build, CLI build, and macOS tests green.
122+
- The previous iOS simulator `ApiTests.js Appearance`
123+
`UILabel.appearance().textColor` readback assertion did not reappear, so the
124+
stable backend runtime key fix appears to have cleared that blocker.
125+
- Current iOS simulator blocker:
126+
- Full-suite iOS launched `TestRunner`, printed `Application Start!`, then
127+
five skipped-spec markers, and later failed because post-timeout
128+
diagnostics threw while running:
129+
`xcrun simctl spawn <udid> ps -axo pid,ppid,stat,etime,command`.
130+
- The failure log therefore did not identify the hanging Jasmine spec.
131+
- Current test-runner fix:
132+
- iOS simulator post-timeout diagnostics now treat `simctl log show` and
133+
`simctl spawn ... ps` failures as warning text instead of throwing away
134+
the underlying timeout context.
135+
- CI now sets `IOS_TEST_VERBOSE_SPECS=1` so the next authoritative iOS
136+
simulator run names each spec start/done and should expose the real
137+
hanging spec.
138+
- Local verification:
139+
- `node packages/react-native/test/ios-runner-diagnostics.test.js` passed.
140+
- Runtime RN JS tests passed:
141+
`for f in packages/react-native/test/*.test.js; do node "$f" || exit 1; done`.
142+
- `git diff --check` passed.
143+
- `npm run check:ffi-boundaries` passed.
144+
- Not done:
145+
- Commit/push the iOS runner diagnostics patch and watch fresh PR CI.
146+
- Use the named hanging spec from CI to continue fixing the runtime/RN
147+
primitive, not by adding retries/timers or simulator-only shims.
148+
- If CI turns green, resume the dedicated simulator-only RNS parity sweep.
149+
113150
Update from 2026-06-29 16:55 EDT:
114151

115152
- Simulator-only rule remains active. Do not use physical iPhone/iPad devices.

PROGRESS.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,40 @@ TypeScript/UI worklets.
1717

1818
## Latest Update - 2026-06-29
1919

20+
### 2026-06-29 17:34 EDT - iOS simulator hang diagnostics
21+
22+
- Goal:
23+
- Keep PR #46 on the RN module / generic runtime primitive branch and stay
24+
simulator-only. No physical devices were used.
25+
- CI finding:
26+
- GitHub Actions run `28402087730` on `99889cb0` kept setup, dependency
27+
install, FFI boundary check, V8 download, libffi build, metadata
28+
generation, NativeScript build, CLI build, and macOS tests green.
29+
- The previous iOS `ApiTests.js Appearance`
30+
`UILabel.appearance().textColor` failure did not reappear, which points to
31+
the stable runtime-expando key fix clearing that blocker.
32+
- The current iOS simulator run still hung after `Application Start!` and
33+
five skipped-spec markers, but the failure was obscured when post-timeout
34+
diagnostics threw on `xcrun simctl spawn ... ps`.
35+
- Changes:
36+
- Made iOS runner post-timeout simulator log and process snapshot collection
37+
best-effort. Diagnostic failures now print warnings instead of replacing
38+
the actual test timeout context.
39+
- Enabled `IOS_TEST_VERBOSE_SPECS=1` in CI so the next iOS simulator run
40+
names each spec start/done and exposes the real hanging spec.
41+
- Added a source guard for the diagnostic behavior and CI verbose-spec flag.
42+
- Verification:
43+
- `node packages/react-native/test/ios-runner-diagnostics.test.js` passed.
44+
- Runtime RN JS tests passed:
45+
`for f in packages/react-native/test/*.test.js; do node "$f" || exit 1; done`.
46+
- `git diff --check` passed.
47+
- `npm run check:ffi-boundaries` passed.
48+
- Still next:
49+
- Commit/push this diagnostics patch and watch the fresh PR CI.
50+
- Use the named iOS simulator hanging spec to fix the runtime/RN primitive
51+
path directly.
52+
- If CI turns green, resume the dedicated simulator-only RNS parity sweep.
53+
2054
### 2026-06-29 16:55 EDT - stable runtime keys for object expandos
2155

2256
- Goal:
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const assert = require("assert");
2+
const fs = require("fs");
3+
const path = require("path");
4+
5+
const repoRoot = path.resolve(__dirname, "../../..");
6+
const runner = fs.readFileSync(path.join(repoRoot, "scripts/run-tests-ios.js"), "utf8");
7+
const ci = fs.readFileSync(path.join(repoRoot, ".github/workflows/ci.yml"), "utf8");
8+
9+
assert(
10+
runner.includes("function collectSimulatorProcessSnapshot(udid, options = {})") &&
11+
runner.includes("if (options.includeErrors)") &&
12+
runner.includes("WARNING: unable to collect simulator process snapshot") &&
13+
runner.includes("collectSimulatorProcessSnapshot(udid, { includeErrors: true })"),
14+
"iOS test runner diagnostics should report simctl ps failures without throwing away the test timeout context",
15+
);
16+
17+
assert(
18+
runner.includes("WARNING: unable to collect recent simulator logs") &&
19+
runner.includes("simctl exited ${result.status}"),
20+
"iOS test runner diagnostics should report simctl log collection failures without aborting diagnostics",
21+
);
22+
23+
assert(
24+
ci.includes('IOS_TEST_VERBOSE_SPECS: "1"'),
25+
"CI should emit per-spec iOS simulator logs so hung runtime specs are identifiable",
26+
);
27+
28+
console.log("iOS runner diagnostics tests passed");

scripts/run-tests-ios.js

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -967,22 +967,28 @@ function collectRecentSimulatorLogs(udid, pid) {
967967
? `processID == ${pid}`
968968
: 'process == "TestRunner"';
969969

970-
const result = run("xcrun", [
971-
"simctl",
972-
"spawn",
973-
udid,
974-
"log",
975-
"show",
976-
"--style",
977-
"compact",
978-
"--last",
979-
simulatorLogLookback,
980-
"--predicate",
981-
predicate
982-
]);
970+
let result;
971+
try {
972+
result = run("xcrun", [
973+
"simctl",
974+
"spawn",
975+
udid,
976+
"log",
977+
"show",
978+
"--style",
979+
"compact",
980+
"--last",
981+
simulatorLogLookback,
982+
"--predicate",
983+
predicate
984+
]);
985+
} catch (error) {
986+
return `WARNING: unable to collect recent simulator logs: ${error.message}`;
987+
}
983988

984989
if (result.status !== 0) {
985-
return "";
990+
const detail = (result.stderr || result.stdout || "").trim();
991+
return `WARNING: unable to collect recent simulator logs (simctl exited ${result.status}${detail ? `: ${detail}` : ""}).`;
986992
}
987993

988994
const text = result.stdout || "";
@@ -1065,11 +1071,26 @@ function readJunitFileState(udid) {
10651071
};
10661072
}
10671073

1068-
function collectSimulatorProcessSnapshot(udid) {
1069-
const result = run("xcrun", ["simctl", "spawn", udid, "ps", "-axo", "pid,ppid,stat,etime,command"], {
1070-
timeout: simctlQueryTimeoutMs
1071-
});
1074+
function collectSimulatorProcessSnapshot(udid, options = {}) {
1075+
let result;
1076+
try {
1077+
result = run("xcrun", ["simctl", "spawn", udid, "ps", "-axo", "pid,ppid,stat,etime,command"], {
1078+
timeout: simctlQueryTimeoutMs
1079+
});
1080+
} catch (error) {
1081+
if (options.includeErrors) {
1082+
return `WARNING: unable to collect simulator process snapshot: ${error.message}`;
1083+
}
1084+
1085+
return null;
1086+
}
1087+
10721088
if (result.status !== 0) {
1089+
if (options.includeErrors) {
1090+
const detail = (result.stderr || result.stdout || "").trim();
1091+
return `WARNING: unable to collect simulator process snapshot (simctl exited ${result.status}${detail ? `: ${detail}` : ""}).`;
1092+
}
1093+
10731094
return null;
10741095
}
10751096

@@ -1125,7 +1146,7 @@ function formatInactivityDiagnostics(udid, state, pid) {
11251146
}
11261147
sections.push(`--- App container state ---\n${junitSummaryLines.join("\n")}`);
11271148

1128-
const processSnapshot = collectSimulatorProcessSnapshot(udid);
1149+
const processSnapshot = collectSimulatorProcessSnapshot(udid, { includeErrors: true });
11291150
if (processSnapshot) {
11301151
sections.push(`--- Simulator process snapshot ---\n${processSnapshot}`);
11311152
}

0 commit comments

Comments
 (0)