Skip to content

Commit 8de4ddd

Browse files
authored
fix: tighten ios runner crash classification (#797)
1 parent 4f0886d commit 8de4ddd

2 files changed

Lines changed: 59 additions & 2 deletions

File tree

src/platforms/ios/__tests__/runner-client.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,64 @@ Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
720720
);
721721
});
722722

723+
test('parseRunnerResponse classifies explicit target app crashes from runner log tail', async () => {
724+
const logPath = writeRunnerLogTail(`
725+
AGENT_DEVICE_RUNNER_COMMAND_FAILED command=snapshot
726+
The application under test terminated unexpectedly.
727+
`);
728+
const response = new Response(
729+
JSON.stringify({
730+
ok: false,
731+
error: {
732+
code: 'COMMAND_FAILED',
733+
message: 'Runner error',
734+
},
735+
}),
736+
);
737+
const session = { ready: true };
738+
739+
await assert.rejects(
740+
() => parseRunnerResponse(response, session, logPath),
741+
(error: unknown) => {
742+
assert.ok(error instanceof AppError);
743+
assert.equal(error.code, 'IOS_TARGET_APP_CRASH');
744+
assert.equal(error.details?.runnerFailureReason, 'target_app_crash');
745+
assert.match(String(error.details?.hint), /target iOS app appears to have crashed/);
746+
assert.equal(isRetryableRunnerError(error), false);
747+
return true;
748+
},
749+
);
750+
});
751+
752+
test('parseRunnerResponse does not classify incidental XCTest crash text as target app crash', async () => {
753+
const logPath = writeRunnerLogTail(`
754+
XCTest runner recovered from a previous test note: the word crashed appeared in debug output.
755+
AGENT_DEVICE_RUNNER_COMMAND_FAILED command=snapshot error=fetch failed
756+
`);
757+
const response = new Response(
758+
JSON.stringify({
759+
ok: false,
760+
error: {
761+
code: 'COMMAND_FAILED',
762+
message: 'fetch failed',
763+
},
764+
}),
765+
);
766+
const session = { ready: true };
767+
768+
await assert.rejects(
769+
() => parseRunnerResponse(response, session, logPath),
770+
(error: unknown) => {
771+
assert.ok(error instanceof AppError);
772+
assert.equal(error.code, 'COMMAND_FAILED');
773+
assert.equal(error.details?.runnerFailureReason, undefined);
774+
assert.equal(error.details?.hint, undefined);
775+
assert.equal(isRetryableRunnerError(error), true);
776+
return true;
777+
},
778+
);
779+
});
780+
723781
test('parseRunnerResponse keeps ordinary runner failures generic without crash log evidence', async () => {
724782
const logPath = writeRunnerLogTail(
725783
'AGENT_DEVICE_RUNNER_COMMAND_FAILED command=type error=main thread execution timed out',

src/platforms/ios/runner-failure-diagnostics.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ function isTargetAppCrash(normalized: string): boolean {
8282
normalized.includes('process crashed') ||
8383
normalized.includes('the application under test') ||
8484
normalized.includes('terminated unexpectedly') ||
85-
(normalized.includes('exception type:') && normalized.includes('thread 0 crashed')) ||
86-
(normalized.includes('crashed') && normalized.includes('xctest'))
85+
(normalized.includes('exception type:') && normalized.includes('thread 0 crashed'))
8786
);
8887
}
8988

0 commit comments

Comments
 (0)