@@ -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 ) , / t a r g e t i O S a p p a p p e a r s t o h a v e c r a s h e d / ) ;
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+
723781test ( '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' ,
0 commit comments