Skip to content

Commit 951ec29

Browse files
author
Dimitar Tachev
authored
Merge pull request #122 from telerik/tachev/fix-restart-freeze
fix: enforce 3 seconds between app restarts in order to avoid frozen simulators on fast restarts
2 parents 79cc924 + 10d6d2f commit 951ec29

File tree

4 files changed

+85
-5
lines changed

4 files changed

+85
-5
lines changed

lib/declarations.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
///<reference path="./.d.ts"/>
22
"use strict";
33

4+
5+
interface IDictionary<T> {
6+
[key: string]: T
7+
}
8+
9+
interface IDeferPromise<T> {
10+
isRejected(): boolean;
11+
isPending(): boolean;
12+
getResult(): any;
13+
promise: Promise<T>;
14+
resolve(value?: T | PromiseLike<T>): void;
15+
reject(reason?: any): void;
16+
isResolved(): boolean;
17+
}
18+
419
interface IiPhoneSimulator {
520
run(applicationPath: string, applicationIdentifier: string, options: IOptions): Promise<string>;
621
printDeviceTypes(): Promise<void>;
@@ -42,10 +57,6 @@ interface ISimctl {
4257
getAppContainer(deviceId: string, applicationIdentifier: string): Promise<string>;
4358
}
4459

45-
interface IDictionary<T> {
46-
[key: string]: T;
47-
}
48-
4960
interface ISimulator extends INameGetter {
5061
getDevices(): Promise<IDevice[]>;
5162
getSdks(): Promise<ISdk[]>;

lib/iphone-simulator-xcode-simctl.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,25 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I
8585
return this.simctl.uninstall(deviceId, appIdentifier, { skipError: true });
8686
}
8787

88+
private static startingApps: IDictionary<Promise<string>> = {};
89+
private static stoppingApps: IDictionary<Promise<void>> = {};
90+
8891
public async startApplication(deviceId: string, appIdentifier: string, options: IOptions): Promise<string> {
92+
const startingAppKey: string = deviceId + appIdentifier;
93+
if (XCodeSimctlSimulator.startingApps[startingAppKey]) {
94+
return XCodeSimctlSimulator.startingApps[startingAppKey];
95+
}
96+
97+
const deferPromise = utils.deferPromise<string>();
98+
XCodeSimctlSimulator.startingApps[startingAppKey] = deferPromise.promise;
99+
// let the app start for 3 seconds in order to avoid a frozen simulator
100+
// when the app is killed on splash screen
101+
setTimeout(() => {
102+
delete XCodeSimctlSimulator.startingApps[startingAppKey];
103+
deferPromise.resolve();
104+
}, 3000);
105+
106+
89107
// simctl launch command does not launch the process immediately and we have to wait a little bit,
90108
// just to ensure all related processes and services are alive.
91109
const launchResult = await this.simctl.launch(deviceId, appIdentifier, options);
@@ -94,6 +112,18 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I
94112
}
95113

96114
public async stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): Promise<void> {
115+
const appKey: string = deviceId + appIdentifier;
116+
if (XCodeSimctlSimulator.stoppingApps[appKey]) {
117+
return XCodeSimctlSimulator.stoppingApps[appKey];
118+
}
119+
120+
const deferPromise = utils.deferPromise<void>();
121+
XCodeSimctlSimulator.stoppingApps[appKey] = deferPromise.promise;
122+
123+
if (XCodeSimctlSimulator.startingApps[appKey]) {
124+
await XCodeSimctlSimulator.startingApps[appKey];
125+
}
126+
97127
try {
98128
let pid = this.getPid(deviceId, bundleExecutable);
99129
while (pid) {
@@ -108,6 +138,11 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I
108138

109139
await this.simctl.terminate(deviceId, appIdentifier);
110140
utils.sleep(0.5);
141+
142+
delete XCodeSimctlSimulator.stoppingApps[appKey];
143+
deferPromise.resolve();
144+
145+
return deferPromise.promise;
111146
}
112147

113148
private getPid(deviceId: string, bundleExecutable: string): string {

lib/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,37 @@ export function getCurrentEpochTime(): number {
1313
export function sleep(seconds: number): void {
1414
childProcess.execSync(`sleep ${seconds}`);
1515
}
16+
17+
export function deferPromise<T>(): IDeferPromise<T> {
18+
let resolve: (value?: T | PromiseLike<T>) => void;
19+
let reject: (reason?: any) => void;
20+
let isResolved = false;
21+
let isRejected = false;
22+
let promise: Promise<T>;
23+
let result: T | PromiseLike<T>;
24+
25+
promise = new Promise<T>((innerResolve, innerReject) => {
26+
resolve = (value?: T | PromiseLike<T>) => {
27+
isResolved = true;
28+
result = value;
29+
30+
return innerResolve(value);
31+
};
32+
33+
reject = (reason?: any) => {
34+
isRejected = true;
35+
36+
return innerReject(reason);
37+
};
38+
});
39+
40+
return {
41+
promise,
42+
resolve,
43+
reject,
44+
isResolved: () => isResolved,
45+
isRejected: () => isRejected,
46+
isPending: () => !isResolved && !isRejected,
47+
getResult: () => result
48+
};
49+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ios-sim-portable",
3-
"version": "4.0.9",
3+
"version": "4.1.0",
44
"description": "",
55
"main": "./lib/ios-sim.js",
66
"scripts": {

0 commit comments

Comments
 (0)