@@ -128,6 +128,7 @@ const mockDefaultReinstallOpsIos = vi.mocked(defaultReinstallOps.ios);
128128const mockDefaultReinstallOpsAndroid = vi . mocked ( defaultReinstallOps . android ) ;
129129
130130beforeEach ( ( ) => {
131+ vi . useRealTimers ( ) ;
131132 mockDispatch . mockReset ( ) ;
132133 mockDispatch . mockResolvedValue ( { } ) ;
133134 mockResolveTargetDevice . mockReset ( ) ;
@@ -2135,7 +2136,9 @@ test('perf samples Apple cpu and memory metrics on iOS simulator app sessions',
21352136 expect ( cpu ?. matchedProcesses ) . toEqual ( [ 'ExampleSimExec' ] ) ;
21362137} ) ;
21372138
2138- test ( 'perf degrades Apple cpu and memory metrics on physical iOS devices' , async ( ) => {
2139+ test ( 'perf samples Apple cpu and memory metrics on physical iOS devices' , async ( ) => {
2140+ vi . useFakeTimers ( ) ;
2141+ vi . setSystemTime ( new Date ( '2026-04-01T10:00:00.000Z' ) ) ;
21392142 const sessionStore = makeSessionStore ( ) ;
21402143 const sessionName = 'perf-session-ios-device' ;
21412144 sessionStore . set ( sessionName , {
@@ -2148,6 +2151,91 @@ test('perf degrades Apple cpu and memory metrics on physical iOS devices', async
21482151 } ) ,
21492152 appBundleId : 'com.example.device' ,
21502153 } ) ;
2154+ let exportCount = 0 ;
2155+ mockRunCmd . mockImplementation ( async ( _cmd , args ) => {
2156+ if (
2157+ args [ 0 ] === 'devicectl' &&
2158+ args [ 1 ] === 'device' &&
2159+ args [ 2 ] === 'info' &&
2160+ args [ 3 ] === 'apps'
2161+ ) {
2162+ const outputIndex = args . indexOf ( '--json-output' ) ;
2163+ fs . writeFileSync (
2164+ args [ outputIndex + 1 ] ! ,
2165+ JSON . stringify ( {
2166+ result : {
2167+ apps : [
2168+ {
2169+ bundleIdentifier : 'com.example.device' ,
2170+ name : 'Example Device App' ,
2171+ url : 'file:///private/var/containers/Bundle/Application/ABC123/ExampleDevice.app/' ,
2172+ } ,
2173+ ] ,
2174+ } ,
2175+ } ) ,
2176+ ) ;
2177+ return { stdout : '' , stderr : '' , exitCode : 0 } ;
2178+ }
2179+ if (
2180+ args [ 0 ] === 'devicectl' &&
2181+ args [ 1 ] === 'device' &&
2182+ args [ 2 ] === 'info' &&
2183+ args [ 3 ] === 'processes'
2184+ ) {
2185+ const outputIndex = args . indexOf ( '--json-output' ) ;
2186+ fs . writeFileSync (
2187+ args [ outputIndex + 1 ] ! ,
2188+ JSON . stringify ( {
2189+ result : {
2190+ runningProcesses : [
2191+ {
2192+ executable :
2193+ 'file:///private/var/containers/Bundle/Application/ABC123/ExampleDevice.app/ExampleDeviceApp' ,
2194+ processIdentifier : 4001 ,
2195+ } ,
2196+ ] ,
2197+ } ,
2198+ } ) ,
2199+ ) ;
2200+ return { stdout : '' , stderr : '' , exitCode : 0 } ;
2201+ }
2202+ if ( args [ 0 ] === 'xctrace' && args [ 1 ] === 'record' ) {
2203+ vi . setSystemTime ( new Date ( Date . now ( ) + 1000 ) ) ;
2204+ return { stdout : '' , stderr : '' , exitCode : 0 } ;
2205+ }
2206+ if ( args [ 0 ] === 'xctrace' && args [ 1 ] === 'export' ) {
2207+ const outputIndex = args . indexOf ( '--output' ) ;
2208+ exportCount += 1 ;
2209+ fs . writeFileSync (
2210+ args [ outputIndex + 1 ] ! ,
2211+ [
2212+ '<?xml version="1.0"?>' ,
2213+ '<trace-query-result>' ,
2214+ '<node xpath="//trace-toc[1]/run[1]/data[1]/table[7]">' ,
2215+ '<schema name="activity-monitor-process-live">' ,
2216+ '<col><mnemonic>start</mnemonic></col>' ,
2217+ '<col><mnemonic>process</mnemonic></col>' ,
2218+ '<col><mnemonic>cpu-total</mnemonic></col>' ,
2219+ '<col><mnemonic>memory-real</mnemonic></col>' ,
2220+ '<col><mnemonic>pid</mnemonic></col>' ,
2221+ '</schema>' ,
2222+ '<row>' ,
2223+ '<start-time fmt="00:00.123">123</start-time>' ,
2224+ '<process fmt="ExampleDeviceApp (4001)"><pid fmt="4001">4001</pid></process>' ,
2225+ exportCount === 1
2226+ ? '<duration-on-core fmt="100.00 ms">100000000</duration-on-core>'
2227+ : '<duration-on-core fmt="350.00 ms">350000000</duration-on-core>' ,
2228+ '<size-in-bytes fmt="8.00 MiB">8388608</size-in-bytes>' ,
2229+ '<pid fmt="4001">4001</pid>' ,
2230+ '</row>' ,
2231+ '</node>' ,
2232+ '</trace-query-result>' ,
2233+ ] . join ( '' ) ,
2234+ ) ;
2235+ return { stdout : '' , stderr : '' , exitCode : 0 } ;
2236+ }
2237+ return { stdout : '' , stderr : '' , exitCode : 0 } ;
2238+ } ) ;
21512239
21522240 const response = await handleSessionCommands ( {
21532241 req : {
@@ -2167,13 +2255,14 @@ test('perf degrades Apple cpu and memory metrics on physical iOS devices', async
21672255 if ( ! response ?. ok ) throw new Error ( 'Expected perf response to succeed for physical iOS session' ) ;
21682256 const memory = ( response . data ?. metrics as any ) ?. memory ;
21692257 const cpu = ( response . data ?. metrics as any ) ?. cpu ;
2170- expect ( memory ?. available ) . toBe ( false ) ;
2171- expect ( memory ?. reason ) . toMatch ( / n o t y e t i m p l e m e n t e d f o r p h y s i c a l i O S d e v i c e s / i) ;
2172- expect ( cpu ?. available ) . toBe ( false ) ;
2173- expect ( cpu ?. reason ) . toMatch ( / n o t y e t i m p l e m e n t e d f o r p h y s i c a l i O S d e v i c e s / i) ;
2258+ expect ( memory ?. available ) . toBe ( true ) ;
2259+ expect ( memory ?. residentMemoryKb ) . toBe ( 8192 ) ;
2260+ expect ( cpu ?. available ) . toBe ( true ) ;
2261+ expect ( cpu ?. usagePercent ) . toBe ( 25 ) ;
2262+ expect ( cpu ?. matchedProcesses ) . toEqual ( [ 'ExampleDeviceApp' ] ) ;
21742263} ) ;
21752264
2176- test ( 'perf reports physical iOS cpu and memory as unsupported even without an app bundle id' , async ( ) => {
2265+ test ( 'perf reports physical iOS cpu and memory as unavailable without an app bundle id' , async ( ) => {
21772266 const sessionStore = makeSessionStore ( ) ;
21782267 const sessionName = 'perf-session-ios-device-no-bundle' ;
21792268 sessionStore . set ( sessionName , {
@@ -2207,9 +2296,9 @@ test('perf reports physical iOS cpu and memory as unsupported even without an ap
22072296 const memory = ( response . data ?. metrics as any ) ?. memory ;
22082297 const cpu = ( response . data ?. metrics as any ) ?. cpu ;
22092298 expect ( memory ?. available ) . toBe ( false ) ;
2210- expect ( memory ?. reason ) . toMatch ( / n o t y e t i m p l e m e n t e d f o r p h y s i c a l i O S d e v i c e s / i) ;
2299+ expect ( memory ?. reason ) . toMatch ( / n o a p p l e a p p b u n d l e i d i s a s s o c i a t e d w i t h t h i s s e s s i o n / i) ;
22112300 expect ( cpu ?. available ) . toBe ( false ) ;
2212- expect ( cpu ?. reason ) . toMatch ( / n o t y e t i m p l e m e n t e d f o r p h y s i c a l i O S d e v i c e s / i) ;
2301+ expect ( cpu ?. reason ) . toMatch ( / n o a p p l e a p p b u n d l e i d i s a s s o c i a t e d w i t h t h i s s e s s i o n / i) ;
22132302} ) ;
22142303
22152304test ( 'open URL on existing iOS session clears stale app bundle id' , async ( ) => {
0 commit comments