@react-native-harness/platform-android 1.2.0-rc.1 → 1.2.0
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__/adb-id.test.d.ts +2 -0
- package/dist/__tests__/adb-id.test.d.ts.map +1 -0
- package/dist/__tests__/adb-id.test.js +41 -0
- package/dist/__tests__/adb.test.js +56 -13
- package/dist/__tests__/app-monitor.test.js +52 -75
- package/dist/__tests__/instance.test.js +3 -72
- package/dist/adb.d.ts +2 -0
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +5 -1
- package/dist/app-monitor.d.ts.map +1 -1
- package/dist/app-monitor.js +2 -5
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/eslint.config.mjs +1 -0
- package/package.json +5 -4
- package/src/__tests__/adb-id.test.ts +51 -0
- package/src/__tests__/instance.test.ts +9 -105
- package/src/adb.ts +15 -1
- package/src/app-monitor.ts +3 -8
- package/test/setup.ts +68 -0
- package/vite.config.ts +19 -0
- package/src/__tests__/adb.test.ts +0 -700
- package/src/__tests__/app-monitor.test.ts +0 -273
- package/src/__tests__/environment.test.ts +0 -212
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb-id.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/adb-id.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { getAdbId, isAdbIdEmulator } from '../adb-id.js';
|
|
3
|
+
import * as adb from '../adb.js';
|
|
4
|
+
describe('adb id resolution', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.restoreAllMocks();
|
|
7
|
+
});
|
|
8
|
+
it('identifies emulator adb ids', () => {
|
|
9
|
+
expect(isAdbIdEmulator('emulator-5554')).toBe(true);
|
|
10
|
+
expect(isAdbIdEmulator('device-serial-001')).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
it('skips non-emulator ids when resolving an emulator device', async () => {
|
|
13
|
+
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([
|
|
14
|
+
'device-serial-001',
|
|
15
|
+
'emulator-5554',
|
|
16
|
+
]);
|
|
17
|
+
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Test_AVD_API_35');
|
|
18
|
+
await expect(getAdbId({
|
|
19
|
+
type: 'emulator',
|
|
20
|
+
name: 'Test_AVD_API_35',
|
|
21
|
+
})).resolves.toBe('emulator-5554');
|
|
22
|
+
});
|
|
23
|
+
it('resolves matching physical device by manufacturer and model', async () => {
|
|
24
|
+
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([
|
|
25
|
+
'device-serial-001',
|
|
26
|
+
'emulator-5554',
|
|
27
|
+
]);
|
|
28
|
+
vi.spyOn(adb, 'getDeviceInfo').mockImplementation(async (adbId) => {
|
|
29
|
+
const results = {
|
|
30
|
+
'device-serial-001': { manufacturer: 'Acme', model: 'Model A1' },
|
|
31
|
+
'emulator-5554': { manufacturer: 'Emulator', model: 'Emulator Model' },
|
|
32
|
+
};
|
|
33
|
+
return results[adbId] || null;
|
|
34
|
+
});
|
|
35
|
+
await expect(getAdbId({
|
|
36
|
+
type: 'physical',
|
|
37
|
+
manufacturer: 'acme',
|
|
38
|
+
model: 'model a1',
|
|
39
|
+
})).resolves.toBe('device-serial-001');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
2
5
|
import { PassThrough } from 'node:stream';
|
|
3
6
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
7
|
import { SubprocessError } from '@react-native-harness/tools';
|
|
5
|
-
|
|
8
|
+
vi.unmock('../adb.js');
|
|
9
|
+
import { createAvd, deleteAvd, emulatorProcess, getAppUid, getLogcatTimestamp, getStartAppArgs, hasAvd, installApp, startLogcat, startEmulator, uninstallApp, waitForBoot, waitForEmulatorDisconnect, } from '../adb.js';
|
|
6
10
|
import * as tools from '@react-native-harness/tools';
|
|
7
11
|
import * as environment from '../environment.js';
|
|
8
12
|
import * as avdConfig from '../avd-config.js';
|
|
9
13
|
const createAbortError = () => new DOMException('The operation was aborted', 'AbortError');
|
|
14
|
+
let sdkRoot;
|
|
10
15
|
const createMockChildProcess = () => {
|
|
11
16
|
const process = new EventEmitter();
|
|
12
17
|
process.stdout = new PassThrough();
|
|
@@ -16,6 +21,14 @@ const createMockChildProcess = () => {
|
|
|
16
21
|
};
|
|
17
22
|
beforeEach(() => {
|
|
18
23
|
vi.restoreAllMocks();
|
|
24
|
+
vi.unstubAllEnvs();
|
|
25
|
+
sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
|
|
26
|
+
fs.mkdirSync(path.join(sdkRoot, 'emulator'), { recursive: true });
|
|
27
|
+
fs.writeFileSync(path.join(sdkRoot, 'emulator', 'emulator'), '');
|
|
28
|
+
fs.mkdirSync(path.join(sdkRoot, 'platform-tools'), { recursive: true });
|
|
29
|
+
fs.writeFileSync(path.join(sdkRoot, 'platform-tools', 'adb'), '');
|
|
30
|
+
vi.stubEnv('ANDROID_HOME', sdkRoot);
|
|
31
|
+
vi.spyOn(environment, 'ensureAndroidEmulatorAvailable').mockResolvedValue(sdkRoot);
|
|
19
32
|
});
|
|
20
33
|
describe('getStartAppArgs', () => {
|
|
21
34
|
it('maps supported extras to adb am start flags', () => {
|
|
@@ -81,8 +94,31 @@ describe('getStartAppArgs', () => {
|
|
|
81
94
|
"+'%m-%d %H:%M:%S.000'",
|
|
82
95
|
]);
|
|
83
96
|
});
|
|
97
|
+
it('starts logcat using the resolved adb binary path', async () => {
|
|
98
|
+
const subprocess = {};
|
|
99
|
+
const spawnSpy = vi.spyOn(tools, 'spawn').mockReturnValue(subprocess);
|
|
100
|
+
expect(startLogcat('emulator-5554', [
|
|
101
|
+
'logcat',
|
|
102
|
+
'-v',
|
|
103
|
+
'threadtime',
|
|
104
|
+
'-b',
|
|
105
|
+
'crash',
|
|
106
|
+
])).toBe(subprocess);
|
|
107
|
+
expect(spawnSpy).toHaveBeenCalledWith(expect.stringMatching(/adb$/), [
|
|
108
|
+
'-s',
|
|
109
|
+
'emulator-5554',
|
|
110
|
+
'logcat',
|
|
111
|
+
'-v',
|
|
112
|
+
'threadtime',
|
|
113
|
+
'-b',
|
|
114
|
+
'crash',
|
|
115
|
+
], {
|
|
116
|
+
stdout: 'pipe',
|
|
117
|
+
stderr: 'pipe',
|
|
118
|
+
});
|
|
119
|
+
});
|
|
84
120
|
it('checks whether an AVD exists', async () => {
|
|
85
|
-
vi.spyOn(tools, 'spawn').
|
|
121
|
+
vi.spyOn(tools, 'spawn').mockResolvedValue({
|
|
86
122
|
stdout: 'Pixel_6_API_33\nPixel_8_API_35\n',
|
|
87
123
|
});
|
|
88
124
|
await expect(hasAvd('Pixel_8_API_35')).resolves.toBe(true);
|
|
@@ -365,17 +401,24 @@ describe('getStartAppArgs', () => {
|
|
|
365
401
|
});
|
|
366
402
|
it('aborts while waiting for boot completion', async () => {
|
|
367
403
|
vi.useFakeTimers();
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
404
|
+
vi.spyOn(tools, 'spawn').mockImplementation((async (_file, args) => {
|
|
405
|
+
if (args?.[0] === 'devices') {
|
|
406
|
+
return {
|
|
407
|
+
stdout: 'List of devices attached\nemulator-5554\tdevice\n',
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
if (args?.includes('emu') && args?.includes('avd') && args?.includes('name')) {
|
|
411
|
+
return {
|
|
412
|
+
stdout: 'Pixel_8_API_35\n',
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (args?.includes('getprop')) {
|
|
416
|
+
return {
|
|
417
|
+
stdout: '0\n',
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
throw new Error(`Unexpected adb call: ${args?.join(' ')}`);
|
|
421
|
+
}));
|
|
379
422
|
const controller = new AbortController();
|
|
380
423
|
const waitPromise = waitForBoot('Pixel_8_API_35', controller.signal);
|
|
381
424
|
await vi.advanceTimersByTimeAsync(1000);
|
|
@@ -3,8 +3,8 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { createAndroidAppMonitor, createAndroidLogEvent } from '../app-monitor.js';
|
|
6
|
-
import * as tools from '@react-native-harness/tools';
|
|
7
6
|
import { createCrashArtifactWriter } from '@react-native-harness/tools';
|
|
7
|
+
import * as adb from '../adb.js';
|
|
8
8
|
const createMockSubprocess = () => ({
|
|
9
9
|
nodeChildProcess: Promise.resolve({
|
|
10
10
|
kill: vi.fn(),
|
|
@@ -55,15 +55,12 @@ describe('createAndroidLogEvent', () => {
|
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
it('starts logcat from the current device timestamp', async () => {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
return createMockSubprocess();
|
|
66
|
-
}));
|
|
58
|
+
const getLogcatTimestampSpy = vi
|
|
59
|
+
.spyOn(adb, 'getLogcatTimestamp')
|
|
60
|
+
.mockResolvedValue('03-12 11:35:08.000');
|
|
61
|
+
const startLogcatSpy = vi
|
|
62
|
+
.spyOn(adb, 'startLogcat')
|
|
63
|
+
.mockReturnValue(createMockSubprocess());
|
|
67
64
|
const monitor = createAndroidAppMonitor({
|
|
68
65
|
adbId: 'emulator-5554',
|
|
69
66
|
bundleId: 'com.harnessplayground',
|
|
@@ -71,9 +68,8 @@ describe('createAndroidLogEvent', () => {
|
|
|
71
68
|
});
|
|
72
69
|
await monitor.start();
|
|
73
70
|
await monitor.stop();
|
|
74
|
-
expect(
|
|
75
|
-
|
|
76
|
-
'emulator-5554',
|
|
71
|
+
expect(getLogcatTimestampSpy).toHaveBeenCalledWith('emulator-5554');
|
|
72
|
+
expect(startLogcatSpy).toHaveBeenCalledWith('emulator-5554', [
|
|
77
73
|
'logcat',
|
|
78
74
|
'-v',
|
|
79
75
|
'threadtime',
|
|
@@ -82,34 +78,24 @@ describe('createAndroidLogEvent', () => {
|
|
|
82
78
|
'--uid=10234',
|
|
83
79
|
'-T',
|
|
84
80
|
'03-12 11:35:08.000',
|
|
85
|
-
]
|
|
86
|
-
stdout: 'pipe',
|
|
87
|
-
stderr: 'pipe',
|
|
88
|
-
});
|
|
81
|
+
]);
|
|
89
82
|
});
|
|
90
83
|
it('hydrates crash details with stack lines that arrive after the first crash event', async () => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
line: '03-12 10:44:40.422 13861 13861 E AndroidRuntime: at com.harnessplayground.MainActivity.onCreate(MainActivity.kt:42)',
|
|
109
|
-
delayMs: 25,
|
|
110
|
-
},
|
|
111
|
-
]);
|
|
112
|
-
}));
|
|
84
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('03-12 10:44:40.000');
|
|
85
|
+
vi.spyOn(adb, 'startLogcat').mockReturnValue(createStreamingSubprocess([
|
|
86
|
+
{ line: '--------- beginning of crash' },
|
|
87
|
+
{
|
|
88
|
+
line: '03-12 10:44:40.420 13861 13861 E AndroidRuntime: Process: com.harnessplayground, PID: 13861',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
line: '03-12 10:44:40.421 13861 13861 E AndroidRuntime: java.lang.RuntimeException: boom',
|
|
92
|
+
delayMs: 25,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
line: '03-12 10:44:40.422 13861 13861 E AndroidRuntime: at com.harnessplayground.MainActivity.onCreate(MainActivity.kt:42)',
|
|
96
|
+
delayMs: 25,
|
|
97
|
+
},
|
|
98
|
+
]));
|
|
113
99
|
const monitor = createAndroidAppMonitor({
|
|
114
100
|
adbId: 'emulator-5554',
|
|
115
101
|
bundleId: 'com.harnessplayground',
|
|
@@ -130,24 +116,17 @@ describe('createAndroidLogEvent', () => {
|
|
|
130
116
|
]);
|
|
131
117
|
});
|
|
132
118
|
it('persists resolved Android crash blocks into .harness', async () => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
line: '03-12 10:44:40.421 13861 13861 E AndroidRuntime: java.lang.RuntimeException: boom',
|
|
147
|
-
delayMs: 20,
|
|
148
|
-
},
|
|
149
|
-
]);
|
|
150
|
-
}));
|
|
119
|
+
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('03-12 10:44:40.000');
|
|
120
|
+
vi.spyOn(adb, 'startLogcat').mockReturnValue(createStreamingSubprocess([
|
|
121
|
+
{ line: '--------- beginning of crash' },
|
|
122
|
+
{
|
|
123
|
+
line: '03-12 10:44:40.420 13861 13861 E AndroidRuntime: Process: com.harnessplayground, PID: 13861',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
line: '03-12 10:44:40.421 13861 13861 E AndroidRuntime: java.lang.RuntimeException: boom',
|
|
127
|
+
delayMs: 20,
|
|
128
|
+
},
|
|
129
|
+
]));
|
|
151
130
|
const monitor = createAndroidAppMonitor({
|
|
152
131
|
adbId: 'emulator-5554',
|
|
153
132
|
bundleId: 'com.harnessplayground',
|
|
@@ -171,21 +150,13 @@ describe('createAndroidLogEvent', () => {
|
|
|
171
150
|
expect(fs.readFileSync(details.artifactPath, 'utf8')).toContain('RuntimeException: boom');
|
|
172
151
|
});
|
|
173
152
|
it('can be started again after timestamp lookup fails', async () => {
|
|
174
|
-
const spawnSpy = vi.spyOn(tools, 'spawn');
|
|
175
153
|
const timestampError = new Error('date failed');
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
stdout: '03-12 11:35:08.000\n',
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
return createMockSubprocess();
|
|
188
|
-
}));
|
|
154
|
+
vi.spyOn(adb, 'getLogcatTimestamp')
|
|
155
|
+
.mockRejectedValueOnce(timestampError)
|
|
156
|
+
.mockResolvedValueOnce('03-12 11:35:08.000');
|
|
157
|
+
const startLogcatSpy = vi
|
|
158
|
+
.spyOn(adb, 'startLogcat')
|
|
159
|
+
.mockReturnValue(createMockSubprocess());
|
|
189
160
|
const monitor = createAndroidAppMonitor({
|
|
190
161
|
adbId: 'emulator-5554',
|
|
191
162
|
bundleId: 'com.harnessplayground',
|
|
@@ -194,9 +165,15 @@ describe('createAndroidLogEvent', () => {
|
|
|
194
165
|
await expect(monitor.start()).rejects.toThrow(timestampError);
|
|
195
166
|
await expect(monitor.start()).resolves.toBeUndefined();
|
|
196
167
|
await monitor.stop();
|
|
197
|
-
expect(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
168
|
+
expect(startLogcatSpy).toHaveBeenCalledWith('emulator-5554', [
|
|
169
|
+
'logcat',
|
|
170
|
+
'-v',
|
|
171
|
+
'threadtime',
|
|
172
|
+
'-b',
|
|
173
|
+
'crash',
|
|
174
|
+
'--uid=10234',
|
|
175
|
+
'-T',
|
|
176
|
+
'03-12 11:35:08.000',
|
|
177
|
+
]);
|
|
201
178
|
});
|
|
202
179
|
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { DEFAULT_METRO_PORT, } from '@react-native-harness/config';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
3
|
import { getAndroidEmulatorPlatformInstance, getAndroidPhysicalDevicePlatformInstance, } from '../instance.js';
|
|
7
4
|
import * as adb from '../adb.js';
|
|
8
5
|
import * as avdConfig from '../avd-config.js';
|
|
@@ -19,9 +16,10 @@ const init = {
|
|
|
19
16
|
signal: new AbortController().signal,
|
|
20
17
|
};
|
|
21
18
|
describe('Android platform instance', () => {
|
|
22
|
-
beforeEach(() => {
|
|
19
|
+
beforeEach(async () => {
|
|
23
20
|
vi.restoreAllMocks();
|
|
24
21
|
vi.unstubAllEnvs();
|
|
22
|
+
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorAvailable').mockResolvedValue('/tmp/android-sdk');
|
|
25
23
|
});
|
|
26
24
|
it('reuses a running emulator and does not shut it down on dispose', async () => {
|
|
27
25
|
const ensureAndroidEmulatorEnvironment = vi
|
|
@@ -137,6 +135,7 @@ describe('Android platform instance', () => {
|
|
|
137
135
|
});
|
|
138
136
|
it('reuses a compatible cached AVD snapshot when caching is enabled', async () => {
|
|
139
137
|
vi.stubEnv('HARNESS_AVD_CACHING', 'true');
|
|
138
|
+
vi.spyOn(await import('../environment.js'), 'getHostAndroidSystemImageArch').mockReturnValue('arm64-v8a');
|
|
140
139
|
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
141
140
|
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
|
|
142
141
|
vi.spyOn(adb, 'hasAvd').mockResolvedValue(true);
|
|
@@ -261,74 +260,6 @@ describe('Android platform instance', () => {
|
|
|
261
260
|
expect(waitForEmulatorDisconnect).toHaveBeenCalledWith('emulator-5554', init.signal);
|
|
262
261
|
expect(startEmulator).toHaveBeenNthCalledWith(2, 'Pixel_8_API_35', 'snapshot-reuse');
|
|
263
262
|
});
|
|
264
|
-
it('installs the app from HARNESS_APP_PATH when missing', async () => {
|
|
265
|
-
const appPath = path.join(os.tmpdir(), 'HarnessPlayground.apk');
|
|
266
|
-
fs.writeFileSync(appPath, 'apk');
|
|
267
|
-
vi.stubEnv('HARNESS_APP_PATH', appPath);
|
|
268
|
-
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
269
|
-
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
270
|
-
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
271
|
-
vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
|
|
272
|
-
vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(false);
|
|
273
|
-
const installApp = vi.spyOn(adb, 'installApp').mockResolvedValue(undefined);
|
|
274
|
-
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
275
|
-
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
276
|
-
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
277
|
-
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
278
|
-
await expect(getAndroidEmulatorPlatformInstance({
|
|
279
|
-
name: 'android',
|
|
280
|
-
device: {
|
|
281
|
-
type: 'emulator',
|
|
282
|
-
name: 'Pixel_8_API_35',
|
|
283
|
-
avd: {
|
|
284
|
-
apiLevel: 35,
|
|
285
|
-
profile: 'pixel_8',
|
|
286
|
-
diskSize: '1G',
|
|
287
|
-
heapSize: '1G',
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
bundleId: 'com.harnessplayground',
|
|
291
|
-
activityName: '.MainActivity',
|
|
292
|
-
}, harnessConfig, init)).resolves.toBeDefined();
|
|
293
|
-
expect(installApp).toHaveBeenCalledWith('emulator-5554', appPath);
|
|
294
|
-
fs.rmSync(appPath, { force: true });
|
|
295
|
-
});
|
|
296
|
-
it('reinstalls the app from HARNESS_APP_PATH when already installed', async () => {
|
|
297
|
-
const appPath = path.join(os.tmpdir(), 'HarnessPlayground-installed.apk');
|
|
298
|
-
fs.writeFileSync(appPath, 'apk');
|
|
299
|
-
vi.stubEnv('HARNESS_APP_PATH', appPath);
|
|
300
|
-
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
301
|
-
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
302
|
-
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
303
|
-
vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
|
|
304
|
-
vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
|
|
305
|
-
const uninstallApp = vi
|
|
306
|
-
.spyOn(adb, 'uninstallApp')
|
|
307
|
-
.mockResolvedValue(undefined);
|
|
308
|
-
const installApp = vi.spyOn(adb, 'installApp').mockResolvedValue(undefined);
|
|
309
|
-
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
310
|
-
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
311
|
-
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
312
|
-
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
313
|
-
await expect(getAndroidEmulatorPlatformInstance({
|
|
314
|
-
name: 'android',
|
|
315
|
-
device: {
|
|
316
|
-
type: 'emulator',
|
|
317
|
-
name: 'Pixel_8_API_35',
|
|
318
|
-
avd: {
|
|
319
|
-
apiLevel: 35,
|
|
320
|
-
profile: 'pixel_8',
|
|
321
|
-
diskSize: '1G',
|
|
322
|
-
heapSize: '1G',
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
bundleId: 'com.harnessplayground',
|
|
326
|
-
activityName: '.MainActivity',
|
|
327
|
-
}, harnessConfig, init)).resolves.toBeDefined();
|
|
328
|
-
expect(uninstallApp).toHaveBeenCalledWith('emulator-5554', 'com.harnessplayground');
|
|
329
|
-
expect(installApp).toHaveBeenCalledWith('emulator-5554', appPath);
|
|
330
|
-
fs.rmSync(appPath, { force: true });
|
|
331
|
-
});
|
|
332
263
|
it('throws a HarnessAppPathError when HARNESS_APP_PATH is missing', async () => {
|
|
333
264
|
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
334
265
|
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
package/dist/adb.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
|
|
2
|
+
import { type Subprocess } from '@react-native-harness/tools';
|
|
2
3
|
import type { ChildProcessByStdio } from 'node:child_process';
|
|
3
4
|
import type { Readable } from 'node:stream';
|
|
4
5
|
import { type EmulatorBootMode } from './emulator-startup.js';
|
|
@@ -42,6 +43,7 @@ export declare const isAppRunning: (adbId: string, bundleId: string) => Promise<
|
|
|
42
43
|
export declare const getAppUid: (adbId: string, bundleId: string) => Promise<number>;
|
|
43
44
|
export declare const setHideErrorDialogs: (adbId: string, hide: boolean) => Promise<void>;
|
|
44
45
|
export declare const getLogcatTimestamp: (adbId: string) => Promise<string>;
|
|
46
|
+
export declare const startLogcat: (adbId: string, args: readonly string[]) => Subprocess;
|
|
45
47
|
export declare const getAvds: () => Promise<string[]>;
|
|
46
48
|
export type AdbDevice = {
|
|
47
49
|
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":"AAAA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
|
package/dist/adb.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { logger, spawn, SubprocessError } from '@react-native-harness/tools';
|
|
1
|
+
import { logger, spawn, SubprocessError, } from '@react-native-harness/tools';
|
|
2
2
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
3
3
|
import { access, rm } from 'node:fs/promises';
|
|
4
4
|
import { getAvdConfigPath, getAvdDirectory, readAvdConfig, } from './avd-config.js';
|
|
@@ -490,6 +490,10 @@ export const getLogcatTimestamp = async (adbId) => {
|
|
|
490
490
|
]);
|
|
491
491
|
return stdout.trim().replace(/^'+|'+$/g, '');
|
|
492
492
|
};
|
|
493
|
+
export const startLogcat = (adbId, args) => spawn(getAdbBinaryPath(), ['-s', adbId, ...args], {
|
|
494
|
+
stdout: 'pipe',
|
|
495
|
+
stderr: 'pipe',
|
|
496
|
+
});
|
|
493
497
|
export const getAvds = async () => {
|
|
494
498
|
try {
|
|
495
499
|
const emulatorBinaryPath = await ensureEmulatorInstalled();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-monitor.d.ts","sourceRoot":"","sources":["../src/app-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,eAAe,EAErB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"app-monitor.d.ts","sourceRoot":"","sources":["../src/app-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,eAAe,EAErB,MAAM,iCAAiC,CAAC;AA+SzC,QAAA,MAAM,qBAAqB,GACzB,MAAM,MAAM,EACZ,UAAU,MAAM,KACf,eAAe,GAAG,IAwEpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,mDAKrC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C,KAAG,iBA4KH,CAAC;AAEF,OAAO,EAAE,qBAAqB,EAAE,CAAC;AACjC,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG;IAC3C,eAAe,EAAE,CACf,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CACtC,CAAC"}
|
package/dist/app-monitor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeRegExp, getEmitter, logger,
|
|
1
|
+
import { escapeRegExp, getEmitter, logger, SubprocessError, } from '@react-native-harness/tools';
|
|
2
2
|
import * as adb from './adb.js';
|
|
3
3
|
import { androidCrashParser } from './crash-parser.js';
|
|
4
4
|
const androidAppMonitorLogger = logger.child('android-app-monitor');
|
|
@@ -281,10 +281,7 @@ export const createAndroidAppMonitor = ({ adbId, bundleId, appUid, crashArtifact
|
|
|
281
281
|
};
|
|
282
282
|
const startLogcat = async () => {
|
|
283
283
|
const logcatTimestamp = await adb.getLogcatTimestamp(adbId);
|
|
284
|
-
logcatProcess =
|
|
285
|
-
stdout: 'pipe',
|
|
286
|
-
stderr: 'pipe',
|
|
287
|
-
});
|
|
284
|
+
logcatProcess = adb.startLogcat(adbId, getLogcatArgs(appUid, logcatTimestamp));
|
|
288
285
|
const currentProcess = logcatProcess;
|
|
289
286
|
if (!currentProcess) {
|
|
290
287
|
return;
|