Skip to content

Commit 2d7ecfd

Browse files
Ensure stopApplication works correctly
stopApplication method calls killall command, which does not terminate the process immediately. So trying to restart the application, which is stopApplication followed by immediate startApplication call, fails with some strange error, that the app has started, but has exited since then. In order to fix this, add a timeout after stop is called, in order to ensure the app is dead. However in newer Xcode's there's a `xcrun simctl terminate` command, which really kills it. So check the version of Xcode and in case it is 8 or later, use the new command instead of killall. In this scenario we do not have to add some magic timeout as the command works correctly. However the xcrun simctl terminate command requires application identifier, while the killall command works with application name. So I've added another paramter to the stopApplication definition, which should be passed from CLI when calling this method - the applicationIdentifier will be used when Xcode 8 or later is installed, appName will be used for earlier versions.
1 parent 75d9d06 commit 2d7ecfd

File tree

5 files changed

+34
-14
lines changed

5 files changed

+34
-14
lines changed

lib/declarations.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ interface IDevice {
2828

2929
interface ISimctl {
3030
launch(deviceId: string, applicationIdentifier: string): string;
31+
terminate(deviceId: string, appIdentifier: string): string;
3132
install(deviceId: string, applicationPath: string): void;
3233
uninstall(deviceId: string, applicationIdentifier: string, opts?: any): void;
3334
notifyPost(deviceId: string, notification: string): void;
@@ -49,7 +50,7 @@ interface ISimulator extends INameGetter {
4950
installApplication(deviceId: string, applicationPath: string): void;
5051
uninstallApplication(deviceId: string, appIdentifier: string): void;
5152
startApplication(deviceId: string, appIdentifier: string): string;
52-
stopApplication(deviceId: string, appIdentifier: string): string;
53+
stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): string;
5354
printDeviceLog(deviceId: string, launchResult?: string): any;
5455
getDeviceLogProcess(deviceId: string): any;
5556
startSimulator(): void;

lib/iphone-simulator-xcode-simctl.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,26 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I
8787
return this.simctl.launch(deviceId, appIdentifier);
8888
}
8989

90-
public stopApplication(deviceId: string, cfBundleExecutable: string): string {
90+
public stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): string {
9191
try {
92-
return childProcess.execSync(`killall ${cfBundleExecutable}`, { skipError: true });
92+
let xcodeMajorVersion: number = null;
93+
try {
94+
const xcodeVersion = xcode.getXcodeVersionData();
95+
xcodeMajorVersion = +xcodeVersion.major;
96+
} catch(err) {
97+
// Ignore the error.
98+
}
99+
100+
if (xcodeMajorVersion && xcodeMajorVersion < 8) {
101+
// Xcode 7.x does not have support for `xcrun simctl terminate` command
102+
const resultOfKill = childProcess.execSync(`killall ${bundleExecutable}`, { skipError: true });
103+
// killall command does not terminate the processes immediately and we have to wait a little bit,
104+
// just to ensure all related processes and services are dead.
105+
utils.sleep(0.5);
106+
return resultOfKill;
107+
} else {
108+
return this.simctl.terminate(deviceId, appIdentifier);
109+
}
93110
} catch (e) {
94111
}
95112
}
@@ -157,11 +174,11 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I
157174
// startSimulaltor doesn't always finish immediately, and the subsequent
158175
// install fails since the simulator is not running.
159176
// Give it some time to start before we attempt installing.
160-
utils.sleep(1000);
177+
utils.sleep(1);
161178
}
162179
}
163180

164181
private killSimulator(): void {
165182
childProcess.execSync("pkill -9 -f Simulator");
166183
}
167-
}
184+
}

lib/simctl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export class Simctl implements ISimctl {
2727
return result;
2828
}
2929

30+
public terminate(deviceId: string, appIdentifier: string): string {
31+
return this.simctlExec("terminate", [deviceId, appIdentifier]);
32+
}
33+
3034
public install(deviceId: string, applicationPath: string): void {
3135
return this.simctlExec("install", [deviceId, applicationPath]);
3236
}

lib/utils.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as childProcess from "./child-process";
2+
13
export function stringify(arr: string[], delimiter?: string): string {
24
delimiter = delimiter || ", ";
35
return arr.join(delimiter);
@@ -8,10 +10,6 @@ export function getCurrentEpochTime(): number {
810
return dateTime.getTime();
911
}
1012

11-
export function sleep(ms: number): void {
12-
let startTime = getCurrentEpochTime();
13-
let currentTime = getCurrentEpochTime();
14-
while ((currentTime - startTime) < ms) {
15-
currentTime = getCurrentEpochTime();
16-
}
17-
}
13+
export function sleep(seconds: number): void {
14+
childProcess.execSync(`sleep ${seconds}`);
15+
}

lib/xcode.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ export function getPathFromXcodeSelect(): string {
66

77
export function getXcodeVersionData(): IXcodeVersionData {
88
let rawData = childProcess.execSync("xcodebuild -version");
9-
let lines = rawData.split("\n");
9+
let lines = rawData.toString().split("\n");
1010
let parts = lines[0].split(" ")[1].split(".");
1111
return {
1212
major: parts[0],
1313
minor: parts[1],
1414
build: lines[1].split("Build version ")[1]
1515
}
16-
}
16+
}

0 commit comments

Comments
 (0)