@react-native-harness/platform-android 1.3.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__/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/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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-diagnostics.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/crash-diagnostics.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { collectDropboxArtifacts, filterExitInfo, getBestDropboxArtifact, parseDropboxOutput, } from '../crash-diagnostics.js';
|
|
3
|
+
const javaCrashDropbox = `
|
|
4
|
+
Drop box contents: 1 entries
|
|
5
|
+
Max entries: 1000
|
|
6
|
+
========================================
|
|
7
|
+
2026-03-12 10:30:45.123 data_app_crash (text, 500 bytes)
|
|
8
|
+
Process: com.harnessplayground
|
|
9
|
+
PID: 7777
|
|
10
|
+
Package: com.harnessplayground v1 (1.0)
|
|
11
|
+
java.lang.RuntimeException: boom
|
|
12
|
+
\tat com.harnessplayground.MainActivity.onCreate(MainActivity.kt:38)
|
|
13
|
+
`;
|
|
14
|
+
const nativeCrashDropbox = `
|
|
15
|
+
========================================
|
|
16
|
+
2026-03-12 10:31:10.456 data_app_native_crash (text, 800 bytes)
|
|
17
|
+
Process: com.harnessplayground
|
|
18
|
+
PID: 8888
|
|
19
|
+
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xdeadbeef
|
|
20
|
+
Abort message: 'JNI DETECTED ERROR IN APPLICATION'
|
|
21
|
+
backtrace:
|
|
22
|
+
#00 pc 00001234 /data/app/lib/libapp.so
|
|
23
|
+
`;
|
|
24
|
+
const deviceJavaCrashDropbox = `
|
|
25
|
+
Drop box contents: 8 entries
|
|
26
|
+
Searching for: data_app_crash
|
|
27
|
+
|
|
28
|
+
========================================
|
|
29
|
+
2026-05-29 10:22:34 data_app_crash (text, 1328 bytes)
|
|
30
|
+
SystemUptimeMs: 79467172
|
|
31
|
+
Process: com.harnessplayground
|
|
32
|
+
PID: 586
|
|
33
|
+
Package: com.harnessplayground v1 (1.0)
|
|
34
|
+
java.lang.RuntimeException: Intentional asynchronous Kotlin crash
|
|
35
|
+
\tat com.harnessplayground.PlaygroundCrashModule.crashFromKotlinAsync(PlaygroundCrashModule.kt:44)
|
|
36
|
+
`;
|
|
37
|
+
describe('collectDropboxArtifacts', () => {
|
|
38
|
+
it('matches harness playground crashes from merged per-tag dropbox output', async () => {
|
|
39
|
+
const artifacts = await collectDropboxArtifacts({
|
|
40
|
+
bundleId: 'com.harnessplayground',
|
|
41
|
+
getDropboxOutput: async () => deviceJavaCrashDropbox,
|
|
42
|
+
occurredAt: Date.now(),
|
|
43
|
+
pid: 586,
|
|
44
|
+
});
|
|
45
|
+
expect(artifacts).toHaveLength(1);
|
|
46
|
+
expect(artifacts[0]).toMatchObject({
|
|
47
|
+
artifactType: 'dropbox-crash',
|
|
48
|
+
pid: 586,
|
|
49
|
+
processName: 'com.harnessplayground',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it('ignores Dropbox entries older than the session start time', async () => {
|
|
53
|
+
const artifacts = await collectDropboxArtifacts({
|
|
54
|
+
bundleId: 'com.harnessplayground',
|
|
55
|
+
getDropboxOutput: async () => deviceJavaCrashDropbox,
|
|
56
|
+
minOccurredAt: Date.parse('2026-05-29T10:22:35.000Z'),
|
|
57
|
+
occurredAt: Date.parse('2026-05-29T10:22:36.000Z'),
|
|
58
|
+
});
|
|
59
|
+
expect(artifacts).toHaveLength(0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('parseDropboxOutput', () => {
|
|
63
|
+
it('parses java and native dropbox entries', () => {
|
|
64
|
+
const entries = parseDropboxOutput(`${javaCrashDropbox}\n${nativeCrashDropbox}`);
|
|
65
|
+
expect(entries).toHaveLength(2);
|
|
66
|
+
expect(entries[0]).toMatchObject({
|
|
67
|
+
tag: 'data_app_crash',
|
|
68
|
+
pid: 7777,
|
|
69
|
+
});
|
|
70
|
+
expect(entries[1]).toMatchObject({
|
|
71
|
+
tag: 'data_app_native_crash',
|
|
72
|
+
pid: 8888,
|
|
73
|
+
});
|
|
74
|
+
expect(entries[1]?.content).toContain('SIGSEGV');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('getBestDropboxArtifact', () => {
|
|
78
|
+
it('prefers the highest-scored artifact', () => {
|
|
79
|
+
const entries = parseDropboxOutput(`${javaCrashDropbox}\n${nativeCrashDropbox}`);
|
|
80
|
+
const artifacts = entries.map((entry, index) => ({
|
|
81
|
+
artifactType: entry.tag === 'data_app_native_crash'
|
|
82
|
+
? 'dropbox-native-crash'
|
|
83
|
+
: 'dropbox-crash',
|
|
84
|
+
dropboxTag: entry.tag,
|
|
85
|
+
occurredAt: Date.now(),
|
|
86
|
+
score: index === 0 ? 100 : 300,
|
|
87
|
+
summary: entry.content,
|
|
88
|
+
pid: entry.pid,
|
|
89
|
+
processName: 'com.harnessplayground',
|
|
90
|
+
}));
|
|
91
|
+
expect(getBestDropboxArtifact(artifacts)?.dropboxTag).toBe('data_app_native_crash');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('filterExitInfo', () => {
|
|
95
|
+
it('returns matching exit-info records for the target package and pid', () => {
|
|
96
|
+
const output = `
|
|
97
|
+
ApplicationExitInfo #0:
|
|
98
|
+
package=com.harnessplayground pid=7777 realUid=10123
|
|
99
|
+
reason=4 (APP CRASH(NATIVE))
|
|
100
|
+
timestamp=2026-03-12 10:31:10.456
|
|
101
|
+
ApplicationExitInfo #1:
|
|
102
|
+
package=com.other.app pid=9999 realUid=10199
|
|
103
|
+
reason=10 (USER REQUESTED)
|
|
104
|
+
`;
|
|
105
|
+
const filtered = filterExitInfo({
|
|
106
|
+
output,
|
|
107
|
+
bundleId: 'com.harnessplayground',
|
|
108
|
+
pid: 7777,
|
|
109
|
+
});
|
|
110
|
+
expect(filtered).toContain('com.harnessplayground pid=7777');
|
|
111
|
+
expect(filtered).not.toContain('com.other.app');
|
|
112
|
+
});
|
|
113
|
+
it('returns null when no exit-info records exist', () => {
|
|
114
|
+
expect(filterExitInfo({
|
|
115
|
+
output: 'No exit info records for com.harnessplayground',
|
|
116
|
+
bundleId: 'com.harnessplayground',
|
|
117
|
+
})).toBeNull();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-reporter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/crash-reporter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createAndroidCrashReporter } from '../crash-reporter.js';
|
|
3
|
+
const javaCrashDropbox = `
|
|
4
|
+
========================================
|
|
5
|
+
2026-03-12 10:30:45.123 data_app_crash (text, 500 bytes)
|
|
6
|
+
Process: com.harnessplayground
|
|
7
|
+
PID: 7777
|
|
8
|
+
Package: com.harnessplayground v1 (1.0)
|
|
9
|
+
java.lang.RuntimeException: boom
|
|
10
|
+
\tat com.harnessplayground.MainActivity.onCreate(MainActivity.kt:38)
|
|
11
|
+
`;
|
|
12
|
+
const nativeCrashDropbox = `
|
|
13
|
+
========================================
|
|
14
|
+
2026-03-12 10:31:10.456 data_app_native_crash (text, 800 bytes)
|
|
15
|
+
Process: com.harnessplayground
|
|
16
|
+
PID: 8888
|
|
17
|
+
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xdeadbeef
|
|
18
|
+
Abort message: 'JNI DETECTED ERROR IN APPLICATION'
|
|
19
|
+
backtrace:
|
|
20
|
+
#00 pc 00001234 /data/app/lib/libapp.so
|
|
21
|
+
`;
|
|
22
|
+
describe('createAndroidCrashReporter', () => {
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
vi.useRealTimers();
|
|
25
|
+
});
|
|
26
|
+
it('persists the current logcat crash block with the test file path', async () => {
|
|
27
|
+
vi.useFakeTimers();
|
|
28
|
+
const persistArtifact = vi.fn(() => '/tmp/.harness/crash-reports/logcat.txt');
|
|
29
|
+
const reporter = createAndroidCrashReporter({
|
|
30
|
+
bundleId: 'com.harnessplayground',
|
|
31
|
+
crashArtifactWriter: {
|
|
32
|
+
runTimestamp: '2026-03-12T11-35-08-000Z',
|
|
33
|
+
persistArtifact,
|
|
34
|
+
},
|
|
35
|
+
getLogs: () => [
|
|
36
|
+
{ line: 'ordinary crash-buffer line', occurredAt: 1_000 },
|
|
37
|
+
{ line: '--------- beginning of crash', occurredAt: 1_100 },
|
|
38
|
+
{
|
|
39
|
+
line: 'Process: com.harnessplayground, PID: 7777',
|
|
40
|
+
occurredAt: 1_100,
|
|
41
|
+
},
|
|
42
|
+
{ line: 'FATAL EXCEPTION: main', occurredAt: 1_100 },
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
const detailsPromise = reporter.getCrashDetails({
|
|
46
|
+
occurredAt: 1_100,
|
|
47
|
+
pid: 7777,
|
|
48
|
+
testFilePath: '/test/native-crash.test.ts',
|
|
49
|
+
});
|
|
50
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
51
|
+
const details = await detailsPromise;
|
|
52
|
+
expect(details).toMatchObject({
|
|
53
|
+
artifactType: 'logcat',
|
|
54
|
+
artifactPath: '/tmp/.harness/crash-reports/logcat.txt',
|
|
55
|
+
pid: 7777,
|
|
56
|
+
processName: 'com.harnessplayground',
|
|
57
|
+
});
|
|
58
|
+
expect(persistArtifact).toHaveBeenCalledWith({
|
|
59
|
+
artifactKind: 'logcat',
|
|
60
|
+
testFilePath: '/test/native-crash.test.ts',
|
|
61
|
+
source: {
|
|
62
|
+
kind: 'text',
|
|
63
|
+
fileName: 'logcat.txt',
|
|
64
|
+
text: [
|
|
65
|
+
'--------- beginning of crash',
|
|
66
|
+
'Process: com.harnessplayground, PID: 7777',
|
|
67
|
+
'FATAL EXCEPTION: main',
|
|
68
|
+
'',
|
|
69
|
+
].join('\n'),
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
it('adds dropbox artifacts as enrichment when logcat evidence is available', async () => {
|
|
74
|
+
vi.useFakeTimers();
|
|
75
|
+
const persistArtifact = vi
|
|
76
|
+
.fn()
|
|
77
|
+
.mockImplementation(({ artifactKind }) => {
|
|
78
|
+
return `/tmp/.harness/crash-reports/${artifactKind}.txt`;
|
|
79
|
+
});
|
|
80
|
+
const reporter = createAndroidCrashReporter({
|
|
81
|
+
bundleId: 'com.harnessplayground',
|
|
82
|
+
crashArtifactWriter: {
|
|
83
|
+
runTimestamp: '2026-03-12T11-35-08-000Z',
|
|
84
|
+
persistArtifact,
|
|
85
|
+
},
|
|
86
|
+
getLogs: () => [
|
|
87
|
+
{ line: '--------- beginning of crash', occurredAt: 1_100 },
|
|
88
|
+
{
|
|
89
|
+
line: 'Process: com.harnessplayground, PID: 7777',
|
|
90
|
+
occurredAt: 1_100,
|
|
91
|
+
},
|
|
92
|
+
{ line: 'FATAL EXCEPTION: main', occurredAt: 1_100 },
|
|
93
|
+
],
|
|
94
|
+
getDropboxOutput: async () => javaCrashDropbox,
|
|
95
|
+
getExitInfo: async () => 'ApplicationExitInfo #0:\n package=com.harnessplayground pid=7777 reason=4 (APP CRASH(NATIVE))',
|
|
96
|
+
});
|
|
97
|
+
const detailsPromise = reporter.getCrashDetails({
|
|
98
|
+
occurredAt: 1_100,
|
|
99
|
+
pid: 7777,
|
|
100
|
+
testFilePath: '/test/native-crash.test.ts',
|
|
101
|
+
});
|
|
102
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
103
|
+
const details = await detailsPromise;
|
|
104
|
+
expect(details).toMatchObject({
|
|
105
|
+
artifactType: 'logcat',
|
|
106
|
+
artifactPath: '/tmp/.harness/crash-reports/logcat.txt',
|
|
107
|
+
});
|
|
108
|
+
expect(details?.enrichmentArtifacts).toEqual(expect.arrayContaining([
|
|
109
|
+
{
|
|
110
|
+
artifactType: 'dropbox-crash',
|
|
111
|
+
artifactPath: '/tmp/.harness/crash-reports/dropbox-crash.txt',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
artifactType: 'exit-info',
|
|
115
|
+
artifactPath: '/tmp/.harness/crash-reports/exit-info.txt',
|
|
116
|
+
},
|
|
117
|
+
]));
|
|
118
|
+
});
|
|
119
|
+
it('falls back to native dropbox evidence when logcat is empty', async () => {
|
|
120
|
+
vi.useFakeTimers();
|
|
121
|
+
const persistArtifact = vi
|
|
122
|
+
.fn()
|
|
123
|
+
.mockImplementation(({ artifactKind }) => {
|
|
124
|
+
return `/tmp/.harness/crash-reports/${artifactKind}.txt`;
|
|
125
|
+
});
|
|
126
|
+
const reporter = createAndroidCrashReporter({
|
|
127
|
+
bundleId: 'com.harnessplayground',
|
|
128
|
+
crashArtifactWriter: {
|
|
129
|
+
runTimestamp: '2026-03-12T11-35-08-000Z',
|
|
130
|
+
persistArtifact,
|
|
131
|
+
},
|
|
132
|
+
getLogs: () => [],
|
|
133
|
+
getDropboxOutput: async () => nativeCrashDropbox,
|
|
134
|
+
});
|
|
135
|
+
const detailsPromise = reporter.getCrashDetails({
|
|
136
|
+
occurredAt: 1_100,
|
|
137
|
+
pid: 8888,
|
|
138
|
+
testFilePath: '/test/native-crash.test.ts',
|
|
139
|
+
});
|
|
140
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
141
|
+
const details = await detailsPromise;
|
|
142
|
+
expect(details).toMatchObject({
|
|
143
|
+
artifactType: 'dropbox-native-crash',
|
|
144
|
+
artifactPath: '/tmp/.harness/crash-reports/dropbox-native-crash.txt',
|
|
145
|
+
pid: 8888,
|
|
146
|
+
signal: 'SIGSEGV',
|
|
147
|
+
processName: 'com.harnessplayground',
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -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"}
|