Skip to content

Commit d2d9d1f

Browse files
committed
fix: do not wait 60 seconds for debugger port when the app is crashed
1 parent 871b343 commit d2d9d1f

File tree

6 files changed

+111
-51
lines changed

6 files changed

+111
-51
lines changed

lib/common/mobile/ios/device/ios-device.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ export class IOSDevice extends IOSDeviceBase {
6060
}
6161
}
6262

63-
protected async getDebugSocketCore(appId: string, projectName: string): Promise<net.Socket> {
64-
await super.attachToDebuggerFoundEvent(projectName);
63+
protected async getDebugSocketCore(appId: string): Promise<net.Socket> {
6564
await this.$iOSSocketRequestExecutor.executeAttachRequest(this, constants.AWAIT_NOTIFICATION_TIMEOUT_SECONDS, appId);
6665
const port = await super.getDebuggerPort(appId);
6766
const deviceId = this.deviceInfo.identifier;

lib/common/mobile/ios/ios-device-base.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ export abstract class IOSDeviceBase implements Mobile.IiOSDevice {
2222
return this.cachedSockets[appId];
2323
}
2424

25-
this.cachedSockets[appId] = await this.getDebugSocketCore(appId, projectName);
25+
await this.attachToDebuggerFoundEvent(appId, projectName);
26+
await this.applicationManager.startApplication({ appId, projectName });
27+
this.cachedSockets[appId] = await this.getDebugSocketCore(appId);
2628

2729
if (this.cachedSockets[appId]) {
2830
this.cachedSockets[appId].on("close", async () => {
@@ -38,11 +40,11 @@ export abstract class IOSDeviceBase implements Mobile.IiOSDevice {
3840
);
3941
}
4042

41-
protected abstract async getDebugSocketCore(appId: string, projectName: string): Promise<net.Socket>;
43+
protected abstract async getDebugSocketCore(appId: string): Promise<net.Socket>;
4244

43-
protected async attachToDebuggerFoundEvent(projectName: string): Promise<void> {
45+
protected async attachToDebuggerFoundEvent(appId: string, projectName: string): Promise<void> {
4446
await this.startDeviceLogProcess(projectName);
45-
await this.$iOSDebuggerPortService.attachToDebuggerPortFoundEvent();
47+
await this.$iOSDebuggerPortService.attachToDebuggerPortFoundEvent(appId);
4648
}
4749

4850
protected async getDebuggerPort(appId: string): Promise<number> {

lib/common/mobile/ios/simulator/ios-simulator-device.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,8 @@ export class IOSSimulator extends IOSDeviceBase implements Mobile.IiOSDevice {
5353
return this.$iOSSimulatorLogProvider.startLogProcess(this.simulator.id, options);
5454
}
5555

56-
protected async getDebugSocketCore(appId: string, projectName: string): Promise<net.Socket> {
56+
protected async getDebugSocketCore(appId: string): Promise<net.Socket> {
5757
let socket: net.Socket;
58-
await super.attachToDebuggerFoundEvent(projectName);
5958
const attachRequestMessage = this.$iOSNotification.getAttachRequest(appId, this.deviceInfo.identifier);
6059
await this.$iOSEmulatorServices.postDarwinNotification(attachRequestMessage, this.deviceInfo.identifier);
6160
const port = await super.getDebuggerPort(appId);

lib/definitions/ios-debugger-port-service.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface IIOSDebuggerPortData {
1212
interface IIOSDebuggerPortStoredData {
1313
port: number;
1414
timer?: NodeJS.Timer;
15+
error?: Error;
1516
}
1617

1718
interface IIOSDebuggerPortService {
@@ -24,5 +25,5 @@ interface IIOSDebuggerPortService {
2425
* Attaches on DEBUGGER_PORT_FOUND event and stores the port
2526
* @returns {Promise<void>}
2627
*/
27-
attachToDebuggerPortFoundEvent(): Promise<void>;
28+
attachToDebuggerPortFoundEvent(appId: string): Promise<void>;
2829
}

lib/services/ios-debugger-port-service.ts

+32-7
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,40 @@ import { APPLICATION_RESPONSE_TIMEOUT_SECONDS } from "../constants";
44

55
export class IOSDebuggerPortService implements IIOSDebuggerPortService {
66
public static DEBUG_PORT_LOG_REGEX = /NativeScript debugger has opened inspector socket on port (\d+?) for (.*)[.]/;
7+
public static APP_CRASH_LOG_REGEX = /Fatal JavaScript exception \- application has been terminated/;
78
private mapDebuggerPortData: IDictionary<IIOSDebuggerPortStoredData> = {};
9+
private currentAppId: string;
810

911
constructor(private $logParserService: ILogParserService,
1012
private $iOSNotification: IiOSNotification,
1113
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
1214
private $logger: ILogger) { }
1315

14-
public getPort(data: IIOSDebuggerPortInputData): Promise<number> {
15-
return new Promise((resolve, reject) => {
16+
public async getPort(data: IIOSDebuggerPortInputData): Promise<number> {
17+
return new Promise<number>((resolve, reject) => {
1618
const key = `${data.deviceId}${data.appId}`;
1719
const retryInterval = 500;
1820
let retryCount = Math.max(APPLICATION_RESPONSE_TIMEOUT_SECONDS * 1000 / retryInterval, 10);
1921

2022
const interval = setInterval(() => {
21-
let port = this.getPortByKey(key);
23+
const port = this.getPortByKey(key);
2224
if (port || retryCount === 0) {
2325
clearInterval(interval);
2426
resolve(port);
2527
} else {
26-
port = this.getPortByKey(key);
27-
retryCount--;
28+
if (this.mapDebuggerPortData[key] && this.mapDebuggerPortData[key].error) {
29+
clearInterval(interval);
30+
reject(this.mapDebuggerPortData[key].error);
31+
} else {
32+
retryCount--;
33+
}
2834
}
2935
}, retryInterval);
3036
});
3137
}
3238

33-
public async attachToDebuggerPortFoundEvent(): Promise<void> {
39+
public async attachToDebuggerPortFoundEvent(appId: string): Promise<void> {
40+
this.currentAppId = appId;
3441
this.attachToDebuggerPortFoundEventCore();
3542
this.attachToAttachRequestEvent();
3643
}
@@ -43,6 +50,23 @@ export class IOSDebuggerPortService implements IIOSDebuggerPortService {
4350
name: "debugPort",
4451
platform: this.$devicePlatformsConstants.iOS.toLowerCase()
4552
});
53+
this.$logParserService.addParseRule({
54+
regex: IOSDebuggerPortService.APP_CRASH_LOG_REGEX,
55+
handler: this.handleAppCrash.bind(this),
56+
name: "appCrash",
57+
platform: this.$devicePlatformsConstants.iOS.toLowerCase()
58+
});
59+
}
60+
private handleAppCrash(matches: RegExpMatchArray, deviceId: string): void {
61+
const data = {
62+
port: 0,
63+
appId: this.currentAppId,
64+
deviceId,
65+
error: new Error("The application has been terminated.")
66+
};
67+
68+
this.clearTimeout(data);
69+
this.setData(data, { port: data.port, error: data.error });
4670
}
4771

4872
private handlePortFound(matches: RegExpMatchArray, deviceId: string): void {
@@ -73,7 +97,7 @@ export class IOSDebuggerPortService implements IIOSDebuggerPortService {
7397
}
7498

7599
private getPortByKey(key: string): number {
76-
if (this.mapDebuggerPortData[key]) {
100+
if (this.mapDebuggerPortData[key] && this.mapDebuggerPortData[key].port) {
77101
return this.mapDebuggerPortData[key].port;
78102
}
79103

@@ -89,6 +113,7 @@ export class IOSDebuggerPortService implements IIOSDebuggerPortService {
89113

90114
this.mapDebuggerPortData[key].port = storedData.port;
91115
this.mapDebuggerPortData[key].timer = storedData.timer;
116+
this.mapDebuggerPortData[key].error = storedData.error;
92117
}
93118

94119
private clearTimeout(data: IIOSDebuggerPortData): void {

test/services/ios-debugger-port-service.ts

+69-35
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,48 @@ function getMultilineDebuggerPortMessage(port: number) {
9090
2018-04-20 09:45:51.260951+0300 localhost nglog[17917]: NativeScript debugger has opened inspector socket on port ${port} for ${appId}.`;
9191
}
9292

93+
function getAppCrashMessage() {
94+
return `***** Fatal JavaScript exception - application has been terminated. *****`;
95+
}
96+
97+
function getMultilineAppCrashMessage() {
98+
return `Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: ***** Fatal JavaScript exception - application has been terminated. *****
99+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: Native stack trace:
100+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 1 0x1013ef370 NativeScript::reportFatalErrorBeforeShutdown(JSC::ExecState*, JSC::Exception*, bool)
101+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 2 0x10141fdec NativeScript::FFICallback<NativeScript::ObjCMethodCallback>::ffiClosureCallback(ffi_cif*, void*, void**, void*)
102+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 3 0x101e84494 ffi_closure_SYSV_inner
103+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 4 0x101e881b4 .Ldo_closure
104+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 5 0x183760c3c <redacted>
105+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 6 0x1837601b8 <redacted>
106+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 7 0x18375ff14 <redacted>
107+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 8 0x1837dd84c <redacted>
108+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 9 0x183696f38 _CFXNotificationPost
109+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 10 0x184107bbc <redacted>
110+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 11 0x18d3da2f0 <redacted>
111+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 12 0x18d3a75e0 <redacted>
112+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 13 0x18d9d7b1c <redacted>
113+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 14 0x18d3a6dd0 <redacted>
114+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 15 0x18d3a6c6c <redacted>
115+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 16 0x18d3a5afc <redacted>
116+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 17 0x18e03b84c <redacted>
117+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 18 0x18d3a51ec <redacted>
118+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 19 0x18de20ac8 <redacted>
119+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 20 0x18df6ebf8 _performActionsWithDelayForTransitionContext
120+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 21 0x18d3a4c0c <redacted>
121+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 22 0x18d3a45a8 <redacted>
122+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 23 0x18d3a15e0 <redacted>
123+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 24 0x18d3a1330 <redacted>
124+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 25 0x185fcf470 <redacted>
125+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 26 0x185fd7d6c <redacted>
126+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 27 0x1830c0a60 <redacted>
127+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 28 0x1830c8170 <redacted>
128+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 29 0x186003878 <redacted>
129+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 30 0x18600351c <redacted>
130+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 31 0x186003ab8 <redacted>
131+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: JavaScript stack trace:
132+
Mar 20 15:13:23 iOS-Team-iPad-2-Mini-Black hwJs(NativeScript)[3946] <Notice>: 1 @file:///app/vendor.js:12552:56`;
133+
}
134+
93135
describe("iOSDebuggerPortService", () => {
94136
let injector: IInjector, iOSDebuggerPortService: IIOSDebuggerPortService, deviceLogProvider: Mobile.IDeviceLogProvider;
95137
let clock: sinon.SinonFakeTimers = null;
@@ -108,71 +150,63 @@ describe("iOSDebuggerPortService", () => {
108150
function emitDeviceLog(message: string) {
109151
deviceLogProvider.emit(DEVICE_LOG_EVENT_NAME, message, device.deviceInfo.identifier);
110152
}
111-
112-
function emitStartingIOSApplicationEvent() {
113-
device.applicationManager.emit("STARTING_IOS_APPLICATION", {
114-
appId: appId,
115-
deviceId: device.deviceInfo.identifier
116-
});
117-
}
118-
119153
describe("getPort", () => {
120-
const testCases = [
154+
const testCases: { name: string, emittedPort?: number, crashApp?: boolean, expectedError?: string }[] = [
121155
{
122156
name: `should return null when ${DEBUGGER_PORT_FOUND_EVENT_NAME} event is not emitted`,
123-
emittedPort: null,
124-
emitStartingIOSApplicationEvent: false
157+
emittedPort: null
125158
},
126159
{
127160
name: `should return default port when ${DEBUGGER_PORT_FOUND_EVENT_NAME} event is emitted`,
128-
emittedPort: 18181,
129-
emitStartingIOSApplicationEvent: false
161+
emittedPort: 18181
130162
},
131163
{
132164
name: `should return random port when ${DEBUGGER_PORT_FOUND_EVENT_NAME} event is emitted`,
133-
emittedPort: 65432,
134-
emitStartingIOSApplicationEvent: false
135-
},
136-
{
137-
name: `should return default port when ${DEBUGGER_PORT_FOUND_EVENT_NAME} and STARTING_IOS_APPLICATION events are emitted`,
138-
emittedPort: 18181,
139-
emitStartingIOSApplicationEvent: true
165+
emittedPort: 65432
140166
},
141167
{
142-
name: `should return random port when ${DEBUGGER_PORT_FOUND_EVENT_NAME} and STARTING_IOS_APPLICATION events are emitted`,
143-
emittedPort: 12345,
144-
emitStartingIOSApplicationEvent: true
168+
name: `should reject when the app crashes`,
169+
expectedError: "The application has been terminated.",
170+
crashApp: true
145171
}
146172
];
147173

148174
_.each(testCases, testCase => {
149175
it(testCase.name, async () => {
150-
await iOSDebuggerPortService.attachToDebuggerPortFoundEvent();
151-
if (testCase.emitStartingIOSApplicationEvent) {
152-
emitStartingIOSApplicationEvent();
153-
}
176+
await iOSDebuggerPortService.attachToDebuggerPortFoundEvent(appId);
154177
if (testCase.emittedPort) {
155178
emitDeviceLog(getDebuggerPortMessage(testCase.emittedPort));
179+
} else if (testCase.crashApp) {
180+
emitDeviceLog(getAppCrashMessage());
156181
}
157182

158183
const promise = iOSDebuggerPortService.getPort({ deviceId: deviceId, appId: appId });
159184
clock.tick(70000);
160-
const port = await promise;
161-
assert.deepEqual(port, testCase.emittedPort);
185+
let port = 0;
186+
try {
187+
port = await promise;
188+
assert.deepEqual(port, testCase.emittedPort);
189+
} catch (err) {
190+
assert.deepEqual(err.message, testCase.expectedError);
191+
}
162192
});
163193
it(`${testCase.name} for multiline debugger port message.`, async () => {
164-
await iOSDebuggerPortService.attachToDebuggerPortFoundEvent();
165-
if (testCase.emitStartingIOSApplicationEvent) {
166-
emitStartingIOSApplicationEvent();
167-
}
194+
await iOSDebuggerPortService.attachToDebuggerPortFoundEvent(appId);
168195
if (testCase.emittedPort) {
169196
emitDeviceLog(getMultilineDebuggerPortMessage(testCase.emittedPort));
197+
} else if (testCase.crashApp) {
198+
emitDeviceLog(getMultilineAppCrashMessage());
170199
}
171200

172201
const promise = iOSDebuggerPortService.getPort({ deviceId: deviceId, appId: appId });
173202
clock.tick(70000);
174-
const port = await promise;
175-
assert.deepEqual(port, testCase.emittedPort);
203+
let port = 0;
204+
try {
205+
port = await promise;
206+
assert.deepEqual(port, testCase.emittedPort);
207+
} catch (err) {
208+
assert.deepEqual(err.message, testCase.expectedError);
209+
}
176210
});
177211
});
178212
});

0 commit comments

Comments
 (0)