@@ -21,6 +21,7 @@ import {
2121 repairMacOsRunnerProductsIfNeeded ,
2222 isExpectedRunnerRepairFailure ,
2323} from './runner-macos-products.ts' ;
24+ import { parseXmlDocumentSync , visitXmlPlistEntries , type XmlNode } from './xml.ts' ;
2425
2526const DEFAULT_IOS_RUNNER_APP_BUNDLE_ID = 'com.callstack.agentdevice.runner' ;
2627const XCTEST_DEVICE_SET_BASE_NAME = 'XCTestDevices' ;
@@ -31,6 +32,13 @@ const RUNNER_DERIVED_ROOT = path.join(os.homedir(), '.agent-device', 'ios-runner
3132const XCTEST_DEVICE_SET_LOCK_TIMEOUT_MS = 30_000 ;
3233const XCTEST_DEVICE_SET_LOCK_POLL_MS = 100 ;
3334const XCTEST_DEVICE_SET_LOCK_OWNER_GRACE_MS = 5_000 ;
35+ const XCTESTRUN_PRODUCT_REFERENCE_KEYS = new Set ( [
36+ 'ProductPaths' ,
37+ 'DependentProductPaths' ,
38+ 'TestHostPath' ,
39+ 'TestBundlePath' ,
40+ 'UITargetAppPath' ,
41+ ] ) ;
3442
3543const runnerXctestrunBuildLocks = new Map < string , Promise < unknown > > ( ) ;
3644export const runnerPrepProcesses = new Set < ExecBackgroundResult [ 'child' ] > ( ) ;
@@ -1150,45 +1158,28 @@ function collectXctestrunProductReferenceValuesFromTarget(
11501158}
11511159
11521160function resolveXctestrunProductReferencesFromXml ( contents : string ) : string [ ] {
1153- const arrayPathKeys = [ 'ProductPaths' , 'DependentProductPaths' ] ;
1154- const stringPathKeys = [ 'TestHostPath' , 'TestBundlePath' , 'UITargetAppPath' ] ;
1155- return Array . from (
1156- new Set ( [
1157- ...arrayPathKeys . flatMap ( ( key ) => extractPlistArrayStringValues ( contents , key ) ) ,
1158- ...stringPathKeys . flatMap ( ( key ) => extractPlistStringValues ( contents , key ) ) ,
1159- ] ) ,
1160- ) ;
1161+ return collectXctestrunXmlProductReferenceValues ( parseXmlDocumentSync ( contents ) ) ;
11611162}
11621163
1163- function extractPlistStringValues ( contents : string , key : string ) : string [ ] {
1164- const pattern = new RegExp ( `<key>${ key } </key>\\s*<string>([\\s\\S]*?)</string>` , 'g' ) ;
1164+ function collectXctestrunXmlProductReferenceValues ( nodes : XmlNode [ ] ) : string [ ] {
11651165 const values = new Set < string > ( ) ;
1166- let match : RegExpExecArray | null ;
1167- while ( ( match = pattern . exec ( contents ) ) !== null ) {
1168- const value = match [ 1 ] ?. trim ( ) ;
1169- if ( value ) {
1170- values . add ( value ) ;
1166+ visitXmlPlistEntries ( nodes , ( key , valueNode ) => {
1167+ if ( ! XCTESTRUN_PRODUCT_REFERENCE_KEYS . has ( key ) ) {
1168+ return ;
11711169 }
1172- }
1173- return Array . from ( values ) ;
1174- }
1175-
1176- function extractPlistArrayStringValues ( contents : string , key : string ) : string [ ] {
1177- // Best-effort XML extraction only. Prefer the plutil JSON path on macOS.
1178- const blockPattern = new RegExp ( `<key>${ key } </key>\\s*<array>([\\s\\S]*?)</array>` , 'g' ) ;
1179- const stringPattern = / < s t r i n g > ( [ \s \S ] * ?) < \/ s t r i n g > / g;
1180- const values = new Set < string > ( ) ;
1181- let match : RegExpExecArray | null ;
1182- while ( ( match = blockPattern . exec ( contents ) ) !== null ) {
1183- const block = match [ 1 ] ?? '' ;
1184- let stringMatch : RegExpExecArray | null ;
1185- while ( ( stringMatch = stringPattern . exec ( block ) ) !== null ) {
1186- const value = stringMatch [ 1 ] ?. trim ( ) ;
1187- if ( value ) {
1188- values . add ( value ) ;
1170+ if ( valueNode . name === 'string' && valueNode . text ) {
1171+ values . add ( valueNode . text ) ;
1172+ return ;
1173+ }
1174+ if ( valueNode . name !== 'array' ) {
1175+ return ;
1176+ }
1177+ for ( const child of valueNode . children ) {
1178+ if ( child . name === 'string' && child . text ) {
1179+ values . add ( child . text ) ;
11891180 }
11901181 }
1191- }
1182+ } ) ;
11921183 return Array . from ( values ) ;
11931184}
11941185
0 commit comments