@react-native-harness/platform-android 1.2.0 → 1.4.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/app-monitor.test.js +180 -102
- package/dist/__tests__/crash-diagnostics.test.d.ts +2 -0
- package/dist/__tests__/crash-diagnostics.test.d.ts.map +1 -0
- package/dist/__tests__/crash-diagnostics.test.js +119 -0
- package/dist/__tests__/crash-reporter.test.d.ts +2 -0
- package/dist/__tests__/crash-reporter.test.d.ts.map +1 -0
- package/dist/__tests__/crash-reporter.test.js +150 -0
- package/dist/__tests__/instance.test.js +133 -24
- package/dist/adb.d.ts +4 -0
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +42 -2
- package/dist/app-session.d.ts +17 -0
- package/dist/app-session.d.ts.map +1 -0
- package/dist/app-session.js +209 -0
- package/dist/crash-diagnostics.d.ts +40 -0
- package/dist/crash-diagnostics.d.ts.map +1 -0
- package/dist/crash-diagnostics.js +256 -0
- package/dist/crash-reporter.d.ts +12 -0
- package/dist/crash-reporter.d.ts.map +1 -0
- package/dist/crash-reporter.js +150 -0
- package/dist/device-state.d.ts +6 -0
- package/dist/device-state.d.ts.map +1 -0
- package/dist/device-state.js +142 -0
- package/dist/instance.d.ts +1 -1
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +32 -59
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/__tests__/crash-diagnostics.test.ts +144 -0
- package/src/__tests__/crash-reporter.test.ts +167 -0
- package/src/__tests__/instance.test.ts +174 -31
- package/src/adb.ts +61 -2
- package/src/app-session.ts +300 -0
- package/src/crash-diagnostics.ts +471 -0
- package/src/crash-reporter.ts +263 -0
- package/src/instance.ts +56 -91
- package/src/runner.ts +7 -3
- package/test/setup.ts +1 -0
- package/src/app-monitor.ts +0 -577
|
@@ -5,6 +5,19 @@ import * as adb from '../adb.js';
|
|
|
5
5
|
import * as avdConfig from '../avd-config.js';
|
|
6
6
|
import * as sharedPrefs from '../shared-prefs.js';
|
|
7
7
|
import { HarnessAppPathError, HarnessEmulatorConfigError } from '../errors.js';
|
|
8
|
+
const createLogcatProcess = (lines = []) => {
|
|
9
|
+
const process = {
|
|
10
|
+
nodeChildProcess: Promise.resolve({
|
|
11
|
+
kill: vi.fn(),
|
|
12
|
+
}),
|
|
13
|
+
[Symbol.asyncIterator]: async function* () {
|
|
14
|
+
for (const line of lines) {
|
|
15
|
+
yield line;
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
return process;
|
|
20
|
+
};
|
|
8
21
|
const harnessConfig = {
|
|
9
22
|
metroPort: DEFAULT_METRO_PORT,
|
|
10
23
|
};
|
|
@@ -19,6 +32,7 @@ describe('Android platform instance', () => {
|
|
|
19
32
|
beforeEach(async () => {
|
|
20
33
|
vi.restoreAllMocks();
|
|
21
34
|
vi.unstubAllEnvs();
|
|
35
|
+
vi.useRealTimers();
|
|
22
36
|
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorAvailable').mockResolvedValue('/tmp/android-sdk');
|
|
23
37
|
});
|
|
24
38
|
it('reuses a running emulator and does not shut it down on dispose', async () => {
|
|
@@ -32,6 +46,7 @@ describe('Android platform instance', () => {
|
|
|
32
46
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
33
47
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
34
48
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
49
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
35
50
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
36
51
|
vi.spyOn(sharedPrefs, 'clearHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
37
52
|
vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
|
|
@@ -68,6 +83,7 @@ describe('Android platform instance', () => {
|
|
|
68
83
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
69
84
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
70
85
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
86
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
71
87
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
72
88
|
vi.spyOn(sharedPrefs, 'clearHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
73
89
|
vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
|
|
@@ -113,6 +129,7 @@ describe('Android platform instance', () => {
|
|
|
113
129
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
114
130
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
115
131
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
132
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
116
133
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
117
134
|
await expect(getAndroidEmulatorPlatformInstance({
|
|
118
135
|
name: 'android',
|
|
@@ -152,6 +169,7 @@ describe('Android platform instance', () => {
|
|
|
152
169
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
153
170
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
154
171
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
172
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
155
173
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
156
174
|
await expect(getAndroidEmulatorPlatformInstance({
|
|
157
175
|
name: 'android',
|
|
@@ -196,6 +214,7 @@ describe('Android platform instance', () => {
|
|
|
196
214
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
197
215
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
198
216
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
217
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
199
218
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
200
219
|
await expect(getAndroidEmulatorPlatformInstance({
|
|
201
220
|
name: 'android',
|
|
@@ -238,6 +257,7 @@ describe('Android platform instance', () => {
|
|
|
238
257
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
239
258
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
240
259
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
260
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
241
261
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
242
262
|
await expect(getAndroidEmulatorPlatformInstance({
|
|
243
263
|
name: 'android',
|
|
@@ -315,7 +335,8 @@ describe('Android platform instance', () => {
|
|
|
315
335
|
activityName: '.MainActivity',
|
|
316
336
|
}, harnessConfig, init)).rejects.toBeInstanceOf(HarnessEmulatorConfigError);
|
|
317
337
|
});
|
|
318
|
-
it('
|
|
338
|
+
it('creates a real emulator app session when native crash detection is disabled', async () => {
|
|
339
|
+
vi.useFakeTimers();
|
|
319
340
|
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
320
341
|
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
321
342
|
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
@@ -324,7 +345,14 @@ describe('Android platform instance', () => {
|
|
|
324
345
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
325
346
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
326
347
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
348
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
327
349
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
350
|
+
const stopApp = vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
|
|
351
|
+
const startApp = vi.spyOn(adb, 'startApp').mockResolvedValue(undefined);
|
|
352
|
+
const getAppPid = vi.spyOn(adb, 'getAppPid').mockResolvedValue(4321);
|
|
353
|
+
const startLogcat = vi
|
|
354
|
+
.spyOn(adb, 'startLogcat')
|
|
355
|
+
.mockReturnValue(createLogcatProcess());
|
|
328
356
|
const instance = await getAndroidEmulatorPlatformInstance({
|
|
329
357
|
name: 'android',
|
|
330
358
|
device: {
|
|
@@ -341,14 +369,84 @@ describe('Android platform instance', () => {
|
|
|
341
369
|
activityName: '.MainActivity',
|
|
342
370
|
}, harnessConfigWithoutNativeCrashDetection, init);
|
|
343
371
|
const listener = vi.fn();
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
await
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
372
|
+
const appSession = await instance.createAppSession();
|
|
373
|
+
appSession.addListener(listener);
|
|
374
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
375
|
+
expect(startLogcat).toHaveBeenCalledWith('emulator-5554', [
|
|
376
|
+
'logcat',
|
|
377
|
+
'-v',
|
|
378
|
+
'threadtime',
|
|
379
|
+
'-b',
|
|
380
|
+
'crash',
|
|
381
|
+
'--uid=10234',
|
|
382
|
+
'-T',
|
|
383
|
+
'01-01 00:00:00.000',
|
|
384
|
+
]);
|
|
385
|
+
expect(startLogcat.mock.invocationCallOrder[0]).toBeLessThan(startApp.mock.invocationCallOrder[0]);
|
|
386
|
+
await expect(appSession.getState()).resolves.toEqual({
|
|
387
|
+
status: 'running',
|
|
388
|
+
pid: 4321,
|
|
389
|
+
});
|
|
390
|
+
expect(getAppPid).toHaveBeenCalled();
|
|
391
|
+
expect(listener).not.toHaveBeenCalled();
|
|
392
|
+
expect(appSession.removeListener(listener)).toBeUndefined();
|
|
393
|
+
await expect(appSession.dispose()).resolves.toBeUndefined();
|
|
394
|
+
expect(stopApp).toHaveBeenCalled();
|
|
395
|
+
});
|
|
396
|
+
it('reports an early Android crash from logcat before a PID poll succeeds', async () => {
|
|
397
|
+
vi.useFakeTimers();
|
|
398
|
+
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
399
|
+
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
400
|
+
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
401
|
+
vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
|
|
402
|
+
vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
|
|
403
|
+
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
404
|
+
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
405
|
+
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
406
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
407
|
+
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
408
|
+
vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
|
|
409
|
+
vi.spyOn(adb, 'startApp').mockResolvedValue(undefined);
|
|
410
|
+
vi.spyOn(adb, 'getAppPid').mockResolvedValue(null);
|
|
411
|
+
vi.spyOn(adb, 'startLogcat').mockReturnValue(createLogcatProcess([
|
|
412
|
+
'--------- beginning of crash',
|
|
413
|
+
'Process: com.harnessplayground, PID: 7777',
|
|
414
|
+
'FATAL EXCEPTION: main',
|
|
415
|
+
]));
|
|
416
|
+
const instance = await getAndroidEmulatorPlatformInstance({
|
|
417
|
+
name: 'android',
|
|
418
|
+
device: {
|
|
419
|
+
type: 'emulator',
|
|
420
|
+
name: 'Pixel_8_API_35',
|
|
421
|
+
avd: {
|
|
422
|
+
apiLevel: 35,
|
|
423
|
+
profile: 'pixel_8',
|
|
424
|
+
diskSize: '1G',
|
|
425
|
+
heapSize: '1G',
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
bundleId: 'com.harnessplayground',
|
|
429
|
+
activityName: '.MainActivity',
|
|
430
|
+
}, harnessConfig, init);
|
|
431
|
+
const listener = vi.fn();
|
|
432
|
+
const appSession = await instance.createAppSession();
|
|
433
|
+
appSession.addListener(listener);
|
|
434
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
435
|
+
expect(appSession.getLogs().map((entry) => entry.line)).toEqual([
|
|
436
|
+
'--------- beginning of crash',
|
|
437
|
+
'Process: com.harnessplayground, PID: 7777',
|
|
438
|
+
'FATAL EXCEPTION: main',
|
|
439
|
+
]);
|
|
440
|
+
await expect(appSession.getState()).resolves.toMatchObject({
|
|
441
|
+
status: 'exited',
|
|
442
|
+
pid: 7777,
|
|
443
|
+
reason: 'observed-exit',
|
|
444
|
+
});
|
|
445
|
+
expect(listener).toHaveBeenCalledWith({ type: 'app_exited' });
|
|
446
|
+
await appSession.dispose();
|
|
350
447
|
});
|
|
351
|
-
it('
|
|
448
|
+
it('creates a real physical-device app session when native crash detection is disabled', async () => {
|
|
449
|
+
vi.useFakeTimers();
|
|
352
450
|
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['012345']);
|
|
353
451
|
vi.spyOn(adb, 'getDeviceInfo').mockResolvedValue({
|
|
354
452
|
manufacturer: 'motorola',
|
|
@@ -358,17 +456,14 @@ describe('Android platform instance', () => {
|
|
|
358
456
|
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
359
457
|
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
360
458
|
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
459
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('01-01 00:00:00.000');
|
|
361
460
|
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
},
|
|
369
|
-
bundleId: 'com.harnessplayground',
|
|
370
|
-
activityName: '.MainActivity',
|
|
371
|
-
}, harnessConfigWithoutNativeCrashDetection)).resolves.toBeDefined();
|
|
461
|
+
vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
|
|
462
|
+
vi.spyOn(adb, 'startApp').mockResolvedValue(undefined);
|
|
463
|
+
vi.spyOn(adb, 'getAppPid').mockResolvedValue(8765);
|
|
464
|
+
const startLogcat = vi
|
|
465
|
+
.spyOn(adb, 'startLogcat')
|
|
466
|
+
.mockReturnValue(createLogcatProcess());
|
|
372
467
|
const instance = await getAndroidPhysicalDevicePlatformInstance({
|
|
373
468
|
name: 'android-device',
|
|
374
469
|
device: {
|
|
@@ -380,12 +475,26 @@ describe('Android platform instance', () => {
|
|
|
380
475
|
activityName: '.MainActivity',
|
|
381
476
|
}, harnessConfigWithoutNativeCrashDetection);
|
|
382
477
|
const listener = vi.fn();
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
await
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
478
|
+
const appSession = await instance.createAppSession();
|
|
479
|
+
appSession.addListener(listener);
|
|
480
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
481
|
+
expect(startLogcat).toHaveBeenCalledWith('012345', [
|
|
482
|
+
'logcat',
|
|
483
|
+
'-v',
|
|
484
|
+
'threadtime',
|
|
485
|
+
'-b',
|
|
486
|
+
'crash',
|
|
487
|
+
'--uid=10234',
|
|
488
|
+
'-T',
|
|
489
|
+
'01-01 00:00:00.000',
|
|
490
|
+
]);
|
|
491
|
+
await expect(appSession.getState()).resolves.toEqual({
|
|
492
|
+
status: 'running',
|
|
493
|
+
pid: 8765,
|
|
494
|
+
});
|
|
495
|
+
expect(listener).not.toHaveBeenCalled();
|
|
496
|
+
expect(appSession.removeListener(listener)).toBeUndefined();
|
|
497
|
+
await expect(appSession.dispose()).resolves.toBeUndefined();
|
|
389
498
|
});
|
|
390
499
|
it('grants permissions when permissions are enabled for emulator', async () => {
|
|
391
500
|
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
package/dist/adb.d.ts
CHANGED
|
@@ -40,10 +40,14 @@ export declare const waitForEmulator: (name: string, signal: AbortSignal) => Pro
|
|
|
40
40
|
export declare const waitForEmulatorDisconnect: (adbId: string, signal: AbortSignal) => Promise<void>;
|
|
41
41
|
export declare const waitForBoot: (name: string, signal: AbortSignal) => Promise<string>;
|
|
42
42
|
export declare const isAppRunning: (adbId: string, bundleId: string) => Promise<boolean>;
|
|
43
|
+
export declare const getAppPid: (adbId: string, bundleId: string) => Promise<number | null>;
|
|
43
44
|
export declare const getAppUid: (adbId: string, bundleId: string) => Promise<number>;
|
|
44
45
|
export declare const setHideErrorDialogs: (adbId: string, hide: boolean) => Promise<void>;
|
|
45
46
|
export declare const getLogcatTimestamp: (adbId: string) => Promise<string>;
|
|
46
47
|
export declare const startLogcat: (adbId: string, args: readonly string[]) => Subprocess;
|
|
48
|
+
export declare const DROPBOX_CRASH_TAGS: readonly ["data_app_crash", "data_app_native_crash"];
|
|
49
|
+
export declare const getDropboxPrint: (adbId: string, tags?: readonly string[]) => Promise<string>;
|
|
50
|
+
export declare const getActivityExitInfo: (adbId: string, bundleId: string) => Promise<string>;
|
|
47
51
|
export declare const getAvds: () => Promise<string[]>;
|
|
48
52
|
export type AdbDevice = {
|
|
49
53
|
id: string;
|
package/dist/adb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
|
package/dist/adb.js
CHANGED
|
@@ -432,6 +432,9 @@ export const waitForBoot = async (name, signal) => {
|
|
|
432
432
|
throw signal.reason;
|
|
433
433
|
};
|
|
434
434
|
export const isAppRunning = async (adbId, bundleId) => {
|
|
435
|
+
return (await getAppPid(adbId, bundleId)) != null;
|
|
436
|
+
};
|
|
437
|
+
export const getAppPid = async (adbId, bundleId) => {
|
|
435
438
|
try {
|
|
436
439
|
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
437
440
|
'-s',
|
|
@@ -440,11 +443,15 @@ export const isAppRunning = async (adbId, bundleId) => {
|
|
|
440
443
|
'pidof',
|
|
441
444
|
bundleId,
|
|
442
445
|
]);
|
|
443
|
-
|
|
446
|
+
const pid = stdout
|
|
447
|
+
.trim()
|
|
448
|
+
.split(/\s+/)
|
|
449
|
+
.find((value) => value !== '');
|
|
450
|
+
return pid ? Number(pid) : null;
|
|
444
451
|
}
|
|
445
452
|
catch (error) {
|
|
446
453
|
if (error instanceof SubprocessError && error.exitCode === 1) {
|
|
447
|
-
return
|
|
454
|
+
return null;
|
|
448
455
|
}
|
|
449
456
|
throw error;
|
|
450
457
|
}
|
|
@@ -494,6 +501,39 @@ export const startLogcat = (adbId, args) => spawn(getAdbBinaryPath(), ['-s', adb
|
|
|
494
501
|
stdout: 'pipe',
|
|
495
502
|
stderr: 'pipe',
|
|
496
503
|
});
|
|
504
|
+
export const DROPBOX_CRASH_TAGS = [
|
|
505
|
+
'data_app_crash',
|
|
506
|
+
'data_app_native_crash',
|
|
507
|
+
];
|
|
508
|
+
export const getDropboxPrint = async (adbId, tags = DROPBOX_CRASH_TAGS) => {
|
|
509
|
+
// Android treats multiple args after --print as one search string, so each
|
|
510
|
+
// tag must be queried separately and merged on the host.
|
|
511
|
+
const outputs = await Promise.all(tags.map(async (tag) => {
|
|
512
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
513
|
+
'-s',
|
|
514
|
+
adbId,
|
|
515
|
+
'shell',
|
|
516
|
+
'dumpsys',
|
|
517
|
+
'dropbox',
|
|
518
|
+
'--print',
|
|
519
|
+
tag,
|
|
520
|
+
]);
|
|
521
|
+
return stdout;
|
|
522
|
+
}));
|
|
523
|
+
return outputs.join('\n');
|
|
524
|
+
};
|
|
525
|
+
export const getActivityExitInfo = async (adbId, bundleId) => {
|
|
526
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
527
|
+
'-s',
|
|
528
|
+
adbId,
|
|
529
|
+
'shell',
|
|
530
|
+
'dumpsys',
|
|
531
|
+
'activity',
|
|
532
|
+
'exit-info',
|
|
533
|
+
bundleId,
|
|
534
|
+
]);
|
|
535
|
+
return stdout;
|
|
536
|
+
};
|
|
497
537
|
export const getAvds = async () => {
|
|
498
538
|
try {
|
|
499
539
|
const emulatorBinaryPath = await ensureEmulatorInstalled();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type AppSession, type CrashArtifactWriter } from '@react-native-harness/platforms';
|
|
2
|
+
import { type Subprocess } from '@react-native-harness/tools';
|
|
3
|
+
type CreateAndroidAppSessionOptions = {
|
|
4
|
+
appUid: number;
|
|
5
|
+
bundleId: string;
|
|
6
|
+
startApp: () => Promise<void>;
|
|
7
|
+
stopApp: () => Promise<void>;
|
|
8
|
+
getAppPid: () => Promise<number | null>;
|
|
9
|
+
getLogcatTimestamp: () => Promise<string>;
|
|
10
|
+
startLogcat: (args: readonly string[]) => Subprocess;
|
|
11
|
+
getDropboxOutput?: () => Promise<string>;
|
|
12
|
+
getExitInfo?: () => Promise<string>;
|
|
13
|
+
crashArtifactWriter?: CrashArtifactWriter;
|
|
14
|
+
};
|
|
15
|
+
export declare const createAndroidAppSession: ({ appUid, bundleId, startApp, stopApp, getAppPid, getLogcatTimestamp, startLogcat, getDropboxOutput, getExitInfo, crashArtifactWriter, }: CreateAndroidAppSessionOptions) => Promise<AppSession>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=app-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-session.d.ts","sourceRoot":"","sources":["../src/app-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EAEf,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,6BAA6B,CAAC;AAkErC,KAAK,8BAA8B,GAAG;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxC,kBAAkB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,UAAU,CAAC;IACrD,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAU,0IAW3C,8BAA8B,KAAG,OAAO,CAAC,UAAU,CAsMrD,CAAC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { createAppSessionEmitter, createBoundedLogBuffer, } from '@react-native-harness/platforms';
|
|
2
|
+
import { escapeRegExp, logger, } from '@react-native-harness/tools';
|
|
3
|
+
import { createAndroidCrashReporter } from './crash-reporter.js';
|
|
4
|
+
const androidAppSessionLogger = logger.child('android-app-session');
|
|
5
|
+
const APP_EXIT_POLL_INTERVAL_MS = 1000;
|
|
6
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
|
+
const getLogcatArgs = (appUid, fromTime) => [
|
|
8
|
+
'logcat',
|
|
9
|
+
'-v',
|
|
10
|
+
'threadtime',
|
|
11
|
+
'-b',
|
|
12
|
+
'crash',
|
|
13
|
+
`--uid=${appUid}`,
|
|
14
|
+
'-T',
|
|
15
|
+
fromTime,
|
|
16
|
+
];
|
|
17
|
+
const getProcessPattern = (bundleId) => new RegExp(`Process:\\s*${escapeRegExp(bundleId)},\\s*PID:\\s*(\\d+)`);
|
|
18
|
+
const getStartProcPattern = (bundleId) => new RegExp(`Start proc (\\d+):${escapeRegExp(bundleId)}(?:/|\\s)`);
|
|
19
|
+
const getProcessDiedPattern = (bundleId) => new RegExp(`Process\\s+${escapeRegExp(bundleId)}\\s+\\(pid\\s+(\\d+)\\)\\s+has\\s+died`, 'i');
|
|
20
|
+
const getObservedPid = (line, bundleId) => {
|
|
21
|
+
const match = line.match(getProcessPattern(bundleId)) ??
|
|
22
|
+
line.match(getStartProcPattern(bundleId)) ??
|
|
23
|
+
line.match(getProcessDiedPattern(bundleId));
|
|
24
|
+
return match?.[1] ? Number(match[1]) : undefined;
|
|
25
|
+
};
|
|
26
|
+
const isCrashSignal = (line, bundleId) => {
|
|
27
|
+
return (getProcessPattern(bundleId).test(line) ||
|
|
28
|
+
new RegExp(`>>>\\s*${escapeRegExp(bundleId)}\\s*<<<`).test(line) ||
|
|
29
|
+
getProcessDiedPattern(bundleId).test(line) ||
|
|
30
|
+
(line.includes(bundleId) &&
|
|
31
|
+
/fatal|crash|signal 11|signal 6|backtrace/i.test(line)));
|
|
32
|
+
};
|
|
33
|
+
const stopSubprocess = async (child) => {
|
|
34
|
+
try {
|
|
35
|
+
(await child.nodeChildProcess).kill();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Ignore termination failures for already-ended background processes.
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const isExitedState = (state) => state.status === 'exited';
|
|
42
|
+
export const createAndroidAppSession = async ({ appUid, bundleId, startApp, stopApp, getAppPid, getLogcatTimestamp, startLogcat, getDropboxOutput, getExitInfo, crashArtifactWriter, }) => {
|
|
43
|
+
const emitter = createAppSessionEmitter();
|
|
44
|
+
const logBuffer = createBoundedLogBuffer();
|
|
45
|
+
let state = { status: 'running' };
|
|
46
|
+
let disposed = false;
|
|
47
|
+
let stopPolling = false;
|
|
48
|
+
let hasObservedProcess = false;
|
|
49
|
+
let exitNotification = null;
|
|
50
|
+
let pollDelayTimeout = null;
|
|
51
|
+
let resolvePollDelay = null;
|
|
52
|
+
const getCurrentPid = () => ('pid' in state ? state.pid : undefined);
|
|
53
|
+
const setRunning = (pid) => {
|
|
54
|
+
if (disposed || state.status === 'disposed' || state.status === 'exited') {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (pid !== undefined) {
|
|
58
|
+
hasObservedProcess = true;
|
|
59
|
+
}
|
|
60
|
+
state =
|
|
61
|
+
pid === undefined ? { status: 'running' } : { status: 'running', pid };
|
|
62
|
+
};
|
|
63
|
+
const scheduleExitNotification = () => {
|
|
64
|
+
if (exitNotification) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
exitNotification = setTimeout(() => {
|
|
68
|
+
exitNotification = null;
|
|
69
|
+
if (!disposed && state.status === 'exited') {
|
|
70
|
+
emitter.emit({ type: 'app_exited' });
|
|
71
|
+
}
|
|
72
|
+
}, 0);
|
|
73
|
+
};
|
|
74
|
+
const waitForNextPoll = () => new Promise((resolve) => {
|
|
75
|
+
resolvePollDelay = () => {
|
|
76
|
+
resolvePollDelay = null;
|
|
77
|
+
pollDelayTimeout = null;
|
|
78
|
+
resolve();
|
|
79
|
+
};
|
|
80
|
+
pollDelayTimeout = setTimeout(() => {
|
|
81
|
+
resolvePollDelay?.();
|
|
82
|
+
}, APP_EXIT_POLL_INTERVAL_MS);
|
|
83
|
+
});
|
|
84
|
+
const cancelPendingPollDelay = () => {
|
|
85
|
+
if (pollDelayTimeout) {
|
|
86
|
+
clearTimeout(pollDelayTimeout);
|
|
87
|
+
pollDelayTimeout = null;
|
|
88
|
+
}
|
|
89
|
+
resolvePollDelay?.();
|
|
90
|
+
};
|
|
91
|
+
const setExited = (reason, pid) => {
|
|
92
|
+
if (disposed || state.status === 'disposed' || state.status === 'exited') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
state = {
|
|
96
|
+
status: 'exited',
|
|
97
|
+
occurredAt: Date.now(),
|
|
98
|
+
pid: pid ?? getCurrentPid(),
|
|
99
|
+
reason,
|
|
100
|
+
};
|
|
101
|
+
scheduleExitNotification();
|
|
102
|
+
};
|
|
103
|
+
const setExitedPid = (pid) => {
|
|
104
|
+
if (state.status !== 'exited' || state.pid !== undefined) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
state = {
|
|
108
|
+
status: 'exited',
|
|
109
|
+
occurredAt: state.occurredAt,
|
|
110
|
+
reason: state.reason,
|
|
111
|
+
pid,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
const logcatTimestamp = await getLogcatTimestamp();
|
|
115
|
+
const sessionStartedAt = Date.now();
|
|
116
|
+
const logcatProcess = startLogcat(getLogcatArgs(appUid, logcatTimestamp));
|
|
117
|
+
const crashReporter = createAndroidCrashReporter({
|
|
118
|
+
bundleId,
|
|
119
|
+
crashArtifactWriter,
|
|
120
|
+
getLogs: () => logBuffer.getLogs(),
|
|
121
|
+
getDropboxOutput,
|
|
122
|
+
getExitInfo,
|
|
123
|
+
minOccurredAt: sessionStartedAt,
|
|
124
|
+
});
|
|
125
|
+
const logTask = (async () => {
|
|
126
|
+
try {
|
|
127
|
+
for await (const rawLine of logcatProcess) {
|
|
128
|
+
const line = String(rawLine);
|
|
129
|
+
if (!disposed) {
|
|
130
|
+
logBuffer.push(line);
|
|
131
|
+
}
|
|
132
|
+
const observedPid = getObservedPid(line, bundleId);
|
|
133
|
+
if (observedPid !== undefined) {
|
|
134
|
+
if (state.status === 'running') {
|
|
135
|
+
setRunning(observedPid);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
setExitedPid(observedPid);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (state.status === 'running' && isCrashSignal(line, bundleId)) {
|
|
142
|
+
setExited('observed-exit', observedPid);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (!disposed) {
|
|
148
|
+
androidAppSessionLogger.debug('Android logcat stream stopped', error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
})();
|
|
152
|
+
try {
|
|
153
|
+
await startApp();
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
disposed = true;
|
|
157
|
+
stopPolling = true;
|
|
158
|
+
emitter.clear();
|
|
159
|
+
await stopSubprocess(logcatProcess);
|
|
160
|
+
await Promise.allSettled([logTask]);
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
const pollTask = (async () => {
|
|
164
|
+
await sleep(0);
|
|
165
|
+
while (!stopPolling) {
|
|
166
|
+
if (isExitedState(state)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const pid = await getAppPid();
|
|
171
|
+
if (pid != null) {
|
|
172
|
+
setRunning(pid);
|
|
173
|
+
}
|
|
174
|
+
else if (hasObservedProcess) {
|
|
175
|
+
setExited('process-gone');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
androidAppSessionLogger.debug('Android app session poll failed', error);
|
|
181
|
+
}
|
|
182
|
+
await waitForNextPoll();
|
|
183
|
+
}
|
|
184
|
+
})();
|
|
185
|
+
return {
|
|
186
|
+
dispose: async () => {
|
|
187
|
+
if (disposed) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
disposed = true;
|
|
191
|
+
stopPolling = true;
|
|
192
|
+
state = { status: 'disposed', occurredAt: Date.now() };
|
|
193
|
+
if (exitNotification) {
|
|
194
|
+
clearTimeout(exitNotification);
|
|
195
|
+
exitNotification = null;
|
|
196
|
+
}
|
|
197
|
+
cancelPendingPollDelay();
|
|
198
|
+
emitter.clear();
|
|
199
|
+
await stopSubprocess(logcatProcess);
|
|
200
|
+
await stopApp();
|
|
201
|
+
await Promise.allSettled([logTask, pollTask]);
|
|
202
|
+
},
|
|
203
|
+
getState: async () => state,
|
|
204
|
+
getLogs: () => logBuffer.getLogs(),
|
|
205
|
+
getCrashDetails: crashReporter.getCrashDetails,
|
|
206
|
+
addListener: emitter.addListener,
|
|
207
|
+
removeListener: emitter.removeListener,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AppCrashDetails, CrashArtifactWriter, CrashDetailsLookupOptions, CrashEnrichmentArtifact } from '@react-native-harness/platforms';
|
|
2
|
+
export declare const DROPBOX_CRASH_TAGS: readonly ["data_app_crash", "data_app_native_crash"];
|
|
3
|
+
export type DropboxEntry = {
|
|
4
|
+
tag: string;
|
|
5
|
+
timestamp: string;
|
|
6
|
+
content: string;
|
|
7
|
+
pid?: number;
|
|
8
|
+
processName?: string;
|
|
9
|
+
};
|
|
10
|
+
export type DropboxCrashArtifact = AppCrashDetails & {
|
|
11
|
+
artifactType: 'dropbox-crash' | 'dropbox-native-crash';
|
|
12
|
+
dropboxTag: string;
|
|
13
|
+
occurredAt: number;
|
|
14
|
+
score?: number;
|
|
15
|
+
};
|
|
16
|
+
type CollectDropboxArtifactsOptions = {
|
|
17
|
+
bundleId: string;
|
|
18
|
+
crashArtifactWriter?: CrashArtifactWriter;
|
|
19
|
+
getDropboxOutput: () => Promise<string>;
|
|
20
|
+
minOccurredAt?: number;
|
|
21
|
+
};
|
|
22
|
+
type WaitForDropboxArtifactsOptions = CollectDropboxArtifactsOptions & CrashDetailsLookupOptions;
|
|
23
|
+
export declare const parseDropboxOutput: (output: string) => DropboxEntry[];
|
|
24
|
+
export declare const collectDropboxArtifacts: ({ bundleId, crashArtifactWriter, getDropboxOutput, minOccurredAt, ...lookup }: WaitForDropboxArtifactsOptions) => Promise<DropboxCrashArtifact[]>;
|
|
25
|
+
export declare const waitForDropboxArtifacts: (options: WaitForDropboxArtifactsOptions) => Promise<DropboxCrashArtifact[]>;
|
|
26
|
+
export declare const filterExitInfo: ({ output, bundleId, pid, }: {
|
|
27
|
+
output: string;
|
|
28
|
+
bundleId: string;
|
|
29
|
+
pid?: number;
|
|
30
|
+
}) => string | null;
|
|
31
|
+
export declare const collectExitInfoArtifact: ({ bundleId, crashArtifactWriter, getExitInfo, pid, testFilePath, }: {
|
|
32
|
+
bundleId: string;
|
|
33
|
+
crashArtifactWriter?: CrashArtifactWriter;
|
|
34
|
+
getExitInfo: () => Promise<string>;
|
|
35
|
+
pid?: number;
|
|
36
|
+
testFilePath?: string;
|
|
37
|
+
}) => Promise<CrashEnrichmentArtifact | null>;
|
|
38
|
+
export declare const getBestDropboxArtifact: (artifacts: DropboxCrashArtifact[]) => DropboxCrashArtifact | null;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=crash-diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-diagnostics.d.ts","sourceRoot":"","sources":["../src/crash-diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,iCAAiC,CAAC;AAWzC,eAAO,MAAM,kBAAkB,sDAGrB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,eAAe,GAAG;IACnD,YAAY,EAAE,eAAe,GAAG,sBAAsB,CAAC;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,8BAA8B,GAAG;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gBAAgB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,8BAA8B,GAAG,8BAA8B,GAClE,yBAAyB,CAAC;AA2D5B,eAAO,MAAM,kBAAkB,GAAI,QAAQ,MAAM,KAAG,YAAY,EAgD/D,CAAC;AAkKF,eAAO,MAAM,uBAAuB,GAAU,+EAM3C,8BAA8B,KAAG,OAAO,CAAC,oBAAoB,EAAE,CA6BjE,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,SAAS,8BAA8B,KACtC,OAAO,CAAC,oBAAoB,EAAE,CAqBhC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,4BAI5B;IACD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,KAAG,MAAM,GAAG,IAgCZ,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAU,oEAM3C;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,KAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAmCzC,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,oBAAoB,EAAE,KAChC,oBAAoB,GAAG,IAEpB,CAAC"}
|