11import { ChildProcess } from "child_process" ;
22import * as path from "path" ;
33import { ApplicationManagerBase } from "../application-manager-base" ;
4- import { hook } from "../../helpers" ;
4+ import { hook , sleep } from "../../helpers" ;
55import { cache } from "../../decorators" ;
66import { IOS_LOG_PREDICATE } from "../../constants" ;
77import {
@@ -64,12 +64,26 @@ export class MacCatalystApplicationManager extends ApplicationManagerBase {
6464
6565 public async stopApplication (
6666 appData : Mobile . IApplicationData ,
67+ ) : Promise < void > {
68+ const executablePath = this . getExecutablePath ( ) ;
69+ await this . signalApplication ( executablePath , "TERM" , appData ) ;
70+ if ( await this . waitForApplicationExit ( executablePath ) ) {
71+ return ;
72+ }
73+ await this . signalApplication ( executablePath , "KILL" , appData ) ;
74+ await this . waitForApplicationExit ( executablePath ) ;
75+ }
76+
77+ private async signalApplication (
78+ executablePath : string ,
79+ signal : string ,
80+ appData : Mobile . IApplicationData ,
6781 ) : Promise < void > {
6882 try {
6983 // Anchored so it never matches our own log stream process.
7084 await this . $childProcess . spawnFromEvent (
7185 "pkill" ,
72- [ "-f" , `^${ this . getExecutablePath ( ) } $` ] ,
86+ [ `- ${ signal } ` , "-f" , `^${ executablePath } $` ] ,
7387 "close" ,
7488 ) ;
7589 } catch ( err ) {
@@ -80,6 +94,32 @@ export class MacCatalystApplicationManager extends ApplicationManagerBase {
8094 }
8195 }
8296
97+ // Returning before the old instance dies makes open -n spawn a duplicate.
98+ private async waitForApplicationExit (
99+ executablePath : string ,
100+ ) : Promise < boolean > {
101+ for ( let attempt = 0 ; attempt < 40 ; attempt ++ ) {
102+ if ( ! ( await this . isApplicationRunning ( executablePath ) ) ) {
103+ return true ;
104+ }
105+ await sleep ( 50 ) ;
106+ }
107+ return false ;
108+ }
109+
110+ private async isApplicationRunning ( executablePath : string ) : Promise < boolean > {
111+ try {
112+ await this . $childProcess . spawnFromEvent (
113+ "pgrep" ,
114+ [ "-f" , `^${ executablePath } $` ] ,
115+ "close" ,
116+ ) ;
117+ return true ;
118+ } catch ( err ) {
119+ return false ;
120+ }
121+ }
122+
83123 public async getDebuggableApps ( ) : Promise <
84124 Mobile . IDeviceApplicationInformation [ ]
85125 > {
0 commit comments