@react-native-harness/platform-android 1.3.0 → 1.4.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-wait.test.d.ts +2 -0
- package/dist/__tests__/adb-wait.test.d.ts.map +1 -0
- package/dist/__tests__/adb-wait.test.js +69 -0
- package/dist/__tests__/app-session.test.d.ts +2 -0
- package/dist/__tests__/app-session.test.d.ts.map +1 -0
- package/dist/__tests__/app-session.test.js +57 -0
- package/dist/__tests__/avd-config.test.js +58 -0
- 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__/emulator-startup.test.js +30 -3
- package/dist/__tests__/instance.test.js +139 -24
- package/dist/adb.d.ts +7 -1
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +72 -23
- package/dist/app-session.d.ts +26 -0
- package/dist/app-session.d.ts.map +1 -0
- package/dist/app-session.js +214 -0
- package/dist/avd-config.d.ts +1 -0
- package/dist/avd-config.d.ts.map +1 -1
- package/dist/avd-config.js +11 -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/emulator-startup.d.ts +23 -0
- package/dist/emulator-startup.d.ts.map +1 -1
- package/dist/emulator-startup.js +58 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/instance.d.ts +1 -1
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +51 -72
- package/dist/runner.d.ts +2 -2
- 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__/adb-wait.test.ts +95 -0
- package/src/__tests__/app-session.test.ts +76 -0
- package/src/__tests__/avd-config.test.ts +70 -0
- package/src/__tests__/crash-diagnostics.test.ts +144 -0
- package/src/__tests__/crash-reporter.test.ts +167 -0
- package/src/__tests__/emulator-startup.test.ts +53 -6
- package/src/__tests__/instance.test.ts +226 -84
- package/src/adb.ts +129 -63
- package/src/app-session.ts +316 -0
- package/src/avd-config.ts +17 -0
- package/src/crash-diagnostics.ts +471 -0
- package/src/crash-reporter.ts +263 -0
- package/src/emulator-startup.ts +74 -0
- package/src/index.ts +1 -0
- package/src/instance.ts +84 -105
- package/src/runner.ts +11 -11
- package/test/setup.ts +1 -0
- package/src/app-monitor.ts +0 -577
package/src/emulator-startup.ts
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDeviceCoreBudget,
|
|
3
|
+
logger,
|
|
4
|
+
spawn,
|
|
5
|
+
SubprocessError,
|
|
6
|
+
} from '@react-native-harness/tools';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import { getEmulatorBinaryPath } from './environment.js';
|
|
9
|
+
|
|
10
|
+
const emulatorStartupLogger = logger.child('android-emulator-startup');
|
|
11
|
+
|
|
1
12
|
export type EmulatorBootMode =
|
|
2
13
|
| 'default-boot'
|
|
3
14
|
| 'clean-snapshot-generation'
|
|
4
15
|
| 'snapshot-reuse';
|
|
5
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Returns the vCPU count baked into an AVD for this runner.
|
|
19
|
+
*
|
|
20
|
+
* AVD snapshots require an identical hardware configuration to load, so all
|
|
21
|
+
* AVD creation, compatibility, and cache-key callers must use this value on
|
|
22
|
+
* the same host.
|
|
23
|
+
*/
|
|
24
|
+
export const getEmulatorCpuCores = (): number => {
|
|
25
|
+
return getDeviceCoreBudget(os.availableParallelism());
|
|
26
|
+
};
|
|
27
|
+
|
|
6
28
|
const COMMON_EMULATOR_ARGS = [
|
|
7
29
|
'-no-window',
|
|
8
30
|
'-gpu',
|
|
9
31
|
'swiftshader_indirect',
|
|
10
32
|
'-noaudio',
|
|
11
33
|
'-no-boot-anim',
|
|
34
|
+
'-no-metrics',
|
|
12
35
|
] as const;
|
|
13
36
|
|
|
14
37
|
export const getEmulatorStartupArgs = (
|
|
@@ -24,3 +47,54 @@ export const getEmulatorStartupArgs = (
|
|
|
24
47
|
|
|
25
48
|
return [`@${name}`, ...modeArgs, ...COMMON_EMULATOR_ARGS];
|
|
26
49
|
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Parses the output of `emulator -accel-check` to determine whether
|
|
53
|
+
* hardware acceleration (KVM on Linux, HVF on macOS) is usable. The
|
|
54
|
+
* emulator prints a message containing "is installed and usable" when
|
|
55
|
+
* acceleration is available.
|
|
56
|
+
*/
|
|
57
|
+
export const isAccelerationUsable = (accelCheckOutput: string): boolean => {
|
|
58
|
+
return accelCheckOutput.includes('is installed and usable');
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Runs `emulator -accel-check` and logs a non-fatal warning when hardware
|
|
63
|
+
* acceleration (KVM/HVF) appears unavailable, in which case the emulator
|
|
64
|
+
* falls back to very slow software emulation. This check must never fail or
|
|
65
|
+
* block emulator startup - any error running it is swallowed and only
|
|
66
|
+
* debug-logged.
|
|
67
|
+
*/
|
|
68
|
+
export const warnIfEmulatorAccelerationUnavailable =
|
|
69
|
+
async (): Promise<void> => {
|
|
70
|
+
let combinedOutput: string;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const emulatorBinaryPath = getEmulatorBinaryPath();
|
|
74
|
+
const { stdout, stderr } = await spawn(emulatorBinaryPath, [
|
|
75
|
+
'-accel-check',
|
|
76
|
+
]);
|
|
77
|
+
combinedOutput = `${stdout}\n${stderr}`;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof SubprocessError) {
|
|
80
|
+
// The check itself ran but exited non-zero - this is how
|
|
81
|
+
// `-accel-check` reports that acceleration is unavailable, so treat
|
|
82
|
+
// its output as a normal (non-fatal) result rather than swallowing it.
|
|
83
|
+
combinedOutput = `${error.stdout}\n${error.stderr}`;
|
|
84
|
+
} else {
|
|
85
|
+
// Failed to even run the check (e.g. binary missing). This must never
|
|
86
|
+
// fail or block emulator startup.
|
|
87
|
+
emulatorStartupLogger.debug(
|
|
88
|
+
'Failed to run "emulator -accel-check": %o',
|
|
89
|
+
error
|
|
90
|
+
);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!isAccelerationUsable(combinedOutput)) {
|
|
96
|
+
logger.warn(
|
|
97
|
+
'Hardware acceleration (KVM/HVF) appears unavailable for the Android emulator. It will fall back to very slow software emulation.'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -9,5 +9,6 @@ export {
|
|
|
9
9
|
resolveAvdCachingEnabled,
|
|
10
10
|
} from './avd-config.js';
|
|
11
11
|
export { getHostAndroidSystemImageArch } from './environment.js';
|
|
12
|
+
export { getEmulatorCpuCores } from './emulator-startup.js';
|
|
12
13
|
export { HarnessAppPathError, HarnessEmulatorConfigError } from './errors.js';
|
|
13
14
|
export { getRunTargets } from './targets.js';
|
package/src/instance.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AppNotInstalledError,
|
|
3
|
-
CreateAppMonitorOptions,
|
|
4
3
|
DeviceNotFoundError,
|
|
5
4
|
type HarnessPlatformInitOptions,
|
|
6
5
|
HarnessPlatformRunner,
|
|
7
6
|
} from '@react-native-harness/platforms';
|
|
8
7
|
import type { Config as HarnessConfig } from '@react-native-harness/config';
|
|
9
|
-
import { logger } from '@react-native-harness/tools';
|
|
8
|
+
import { instrumented, logger, noopDiagnostics } from '@react-native-harness/tools';
|
|
10
9
|
import {
|
|
11
10
|
AndroidPlatformConfig,
|
|
12
11
|
assertAndroidDeviceEmulator,
|
|
@@ -18,33 +17,25 @@ import {
|
|
|
18
17
|
resolveAvdCachingEnabled,
|
|
19
18
|
} from './avd-config.js';
|
|
20
19
|
import { getAdbId } from './adb-id.js';
|
|
21
|
-
import * as
|
|
20
|
+
import * as adbModule from './adb.js';
|
|
22
21
|
import {
|
|
23
22
|
applyHarnessDebugHttpHost,
|
|
24
23
|
clearHarnessDebugHttpHost,
|
|
25
24
|
} from './shared-prefs.js';
|
|
26
25
|
import { getDeviceName } from './utils.js';
|
|
27
|
-
import { createAndroidAppMonitor } from './app-monitor.js';
|
|
28
26
|
import { HarnessAppPathError, HarnessEmulatorConfigError } from './errors.js';
|
|
29
27
|
import {
|
|
30
28
|
ensureAndroidEmulatorAvailable,
|
|
31
29
|
ensureAndroidEmulatorEnvironment,
|
|
32
30
|
getHostAndroidSystemImageArch,
|
|
33
31
|
} from './environment.js';
|
|
32
|
+
import { warnIfEmulatorAccelerationUnavailable } from './emulator-startup.js';
|
|
34
33
|
import { isInteractive } from '@react-native-harness/tools';
|
|
35
34
|
import fs from 'node:fs';
|
|
36
|
-
import
|
|
35
|
+
import { createAndroidAppSession } from './app-session.js';
|
|
37
36
|
|
|
38
37
|
const androidInstanceLogger = logger.child('android-instance');
|
|
39
38
|
|
|
40
|
-
const createNoopAppMonitor = (): AppMonitor => ({
|
|
41
|
-
start: async () => undefined,
|
|
42
|
-
stop: async () => undefined,
|
|
43
|
-
dispose: async () => undefined,
|
|
44
|
-
addListener: () => undefined,
|
|
45
|
-
removeListener: () => undefined,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
39
|
const getHarnessAppPath = (): string => {
|
|
49
40
|
const appPath = process.env.HARNESS_APP_PATH;
|
|
50
41
|
|
|
@@ -76,18 +67,18 @@ const getOptionalHarnessAppPath = (): string | undefined => {
|
|
|
76
67
|
const configureAndroidRuntime = async (
|
|
77
68
|
adbId: string,
|
|
78
69
|
config: AndroidPlatformConfig,
|
|
79
|
-
harnessConfig: HarnessConfig
|
|
70
|
+
harnessConfig: HarnessConfig
|
|
80
71
|
): Promise<number> => {
|
|
81
72
|
const metroPort = harnessConfig.metroPort;
|
|
82
73
|
|
|
83
74
|
await Promise.all([
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
adbModule.reversePort(adbId, metroPort),
|
|
76
|
+
adbModule.reversePort(adbId, 8080),
|
|
77
|
+
adbModule.setHideErrorDialogs(adbId, true),
|
|
87
78
|
applyHarnessDebugHttpHost(adbId, config.bundleId, `localhost:${metroPort}`),
|
|
88
79
|
]);
|
|
89
80
|
|
|
90
|
-
return
|
|
81
|
+
return adbModule.getAppUid(adbId, config.bundleId);
|
|
91
82
|
};
|
|
92
83
|
|
|
93
84
|
const startAndWaitForBoot = async ({
|
|
@@ -97,11 +88,12 @@ const startAndWaitForBoot = async ({
|
|
|
97
88
|
}: {
|
|
98
89
|
emulatorName: string;
|
|
99
90
|
signal: AbortSignal;
|
|
100
|
-
mode?: Parameters<typeof
|
|
91
|
+
mode?: Parameters<typeof adbModule.startEmulator>[1];
|
|
101
92
|
}): Promise<string> => {
|
|
102
93
|
await ensureAndroidEmulatorAvailable();
|
|
103
|
-
await
|
|
104
|
-
|
|
94
|
+
await warnIfEmulatorAccelerationUnavailable();
|
|
95
|
+
await adbModule.startEmulator(emulatorName, mode);
|
|
96
|
+
return adbModule.waitForBoot(emulatorName, signal);
|
|
105
97
|
};
|
|
106
98
|
|
|
107
99
|
const recreateAvd = async ({
|
|
@@ -116,7 +108,7 @@ const recreateAvd = async ({
|
|
|
116
108
|
throw new HarnessEmulatorConfigError(emulatorConfig.name);
|
|
117
109
|
}
|
|
118
110
|
|
|
119
|
-
await
|
|
111
|
+
await adbModule.createAvd({
|
|
120
112
|
name: emulatorConfig.name,
|
|
121
113
|
apiLevel: emulatorConfig.avd.apiLevel,
|
|
122
114
|
profile: emulatorConfig.avd.profile,
|
|
@@ -137,7 +129,7 @@ const prepareCachedAvd = async ({
|
|
|
137
129
|
}): Promise<string> => {
|
|
138
130
|
const emulatorName = emulatorConfig.name;
|
|
139
131
|
const hostArch = getHostAndroidSystemImageArch();
|
|
140
|
-
const hasExistingAvd = await
|
|
132
|
+
const hasExistingAvd = await adbModule.hasAvd(emulatorName);
|
|
141
133
|
const avdConfig = hasExistingAvd ? await readAvdConfig(emulatorName) : null;
|
|
142
134
|
const compatibility =
|
|
143
135
|
avdConfig == null
|
|
@@ -153,16 +145,16 @@ const prepareCachedAvd = async ({
|
|
|
153
145
|
hasExistingAvd
|
|
154
146
|
? 'Recreating incompatible Android emulator %s...'
|
|
155
147
|
: 'Creating Android emulator %s...',
|
|
156
|
-
emulatorName
|
|
148
|
+
emulatorName
|
|
157
149
|
);
|
|
158
150
|
|
|
159
151
|
if (hasExistingAvd && !compatibility.compatible) {
|
|
160
152
|
androidInstanceLogger.debug(
|
|
161
153
|
'Android AVD %s is not reusable: %s',
|
|
162
154
|
emulatorName,
|
|
163
|
-
compatibility.reason
|
|
155
|
+
compatibility.reason
|
|
164
156
|
);
|
|
165
|
-
await
|
|
157
|
+
await adbModule.deleteAvd(emulatorName);
|
|
166
158
|
}
|
|
167
159
|
|
|
168
160
|
await recreateAvd({ emulatorConfig });
|
|
@@ -174,8 +166,8 @@ const prepareCachedAvd = async ({
|
|
|
174
166
|
});
|
|
175
167
|
|
|
176
168
|
logger.info('Saving Android emulator snapshot for %s...', emulatorName);
|
|
177
|
-
await
|
|
178
|
-
await
|
|
169
|
+
await adbModule.stopEmulator(generationAdbId);
|
|
170
|
+
await adbModule.waitForEmulatorDisconnect(generationAdbId, signal);
|
|
179
171
|
} else {
|
|
180
172
|
logger.info('Using cached Android emulator %s...', emulatorName);
|
|
181
173
|
}
|
|
@@ -190,10 +182,14 @@ const prepareCachedAvd = async ({
|
|
|
190
182
|
export const getAndroidEmulatorPlatformInstance = async (
|
|
191
183
|
config: AndroidPlatformConfig,
|
|
192
184
|
harnessConfig: HarnessConfig,
|
|
193
|
-
init: HarnessPlatformInitOptions
|
|
185
|
+
init: HarnessPlatformInitOptions
|
|
194
186
|
): Promise<HarnessPlatformRunner> => {
|
|
195
187
|
assertAndroidDeviceEmulator(config.device);
|
|
196
|
-
const
|
|
188
|
+
const adb = instrumented(
|
|
189
|
+
adbModule,
|
|
190
|
+
'platform.android.adb',
|
|
191
|
+
init.diagnostics ?? noopDiagnostics
|
|
192
|
+
);
|
|
197
193
|
const permissionsEnabled = harnessConfig.permissions ?? false;
|
|
198
194
|
const emulatorConfig = config.device;
|
|
199
195
|
const emulatorName = emulatorConfig.name;
|
|
@@ -209,7 +205,7 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
209
205
|
androidInstanceLogger.debug(
|
|
210
206
|
'resolved Android emulator %s with adb id %s',
|
|
211
207
|
emulatorConfig.name,
|
|
212
|
-
adbId ?? 'not-found'
|
|
208
|
+
adbId ?? 'not-found'
|
|
213
209
|
);
|
|
214
210
|
|
|
215
211
|
if (!adbId) {
|
|
@@ -229,7 +225,7 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
229
225
|
logger.info('Creating Android emulator %s...', emulatorName);
|
|
230
226
|
androidInstanceLogger.debug(
|
|
231
227
|
'creating Android AVD %s before startup',
|
|
232
|
-
emulatorConfig.name
|
|
228
|
+
emulatorConfig.name
|
|
233
229
|
);
|
|
234
230
|
await recreateAvd({ emulatorConfig });
|
|
235
231
|
} else {
|
|
@@ -238,7 +234,7 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
238
234
|
|
|
239
235
|
androidInstanceLogger.debug(
|
|
240
236
|
'starting Android emulator %s',
|
|
241
|
-
emulatorConfig.name
|
|
237
|
+
emulatorConfig.name
|
|
242
238
|
);
|
|
243
239
|
return startAndWaitForBoot({
|
|
244
240
|
emulatorName: emulatorConfig.name,
|
|
@@ -251,7 +247,7 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
251
247
|
androidInstanceLogger.debug(
|
|
252
248
|
'Android emulator %s connected as %s',
|
|
253
249
|
emulatorConfig.name,
|
|
254
|
-
adbId
|
|
250
|
+
adbId
|
|
255
251
|
);
|
|
256
252
|
}
|
|
257
253
|
|
|
@@ -261,7 +257,7 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
261
257
|
|
|
262
258
|
androidInstanceLogger.debug(
|
|
263
259
|
'waiting for Android emulator %s to finish booting',
|
|
264
|
-
adbId
|
|
260
|
+
adbId
|
|
265
261
|
);
|
|
266
262
|
|
|
267
263
|
const isInstalled = await adb.isAppInstalled(adbId, config.bundleId);
|
|
@@ -282,27 +278,31 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
282
278
|
}
|
|
283
279
|
|
|
284
280
|
return {
|
|
285
|
-
|
|
286
|
-
await adb.startApp(
|
|
287
|
-
adbId,
|
|
288
|
-
config.bundleId,
|
|
289
|
-
config.activityName,
|
|
290
|
-
(options as typeof config.appLaunchOptions | undefined) ??
|
|
291
|
-
config.appLaunchOptions,
|
|
292
|
-
);
|
|
293
|
-
},
|
|
294
|
-
restartApp: async (options) => {
|
|
281
|
+
createAppSession: async (options) => {
|
|
295
282
|
await adb.stopApp(adbId, config.bundleId);
|
|
296
|
-
|
|
297
|
-
adbId,
|
|
298
|
-
config.bundleId,
|
|
299
|
-
config.activityName,
|
|
283
|
+
const launchOptions =
|
|
300
284
|
(options as typeof config.appLaunchOptions | undefined) ??
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
285
|
+
config.appLaunchOptions;
|
|
286
|
+
|
|
287
|
+
return await createAndroidAppSession({
|
|
288
|
+
appUid,
|
|
289
|
+
bundleId: config.bundleId,
|
|
290
|
+
startApp: () =>
|
|
291
|
+
adb.startApp(
|
|
292
|
+
adbId,
|
|
293
|
+
config.bundleId,
|
|
294
|
+
config.activityName,
|
|
295
|
+
launchOptions
|
|
296
|
+
),
|
|
297
|
+
stopApp: () => adb.stopApp(adbId, config.bundleId),
|
|
298
|
+
getAppPid: () => adb.getAppPid(adbId, config.bundleId),
|
|
299
|
+
getLogcatTimestamp: () => adb.getLogcatTimestamp(adbId),
|
|
300
|
+
startLogcat: (args, options) => adb.startLogcat(adbId, args, options),
|
|
301
|
+
getDropboxOutput: () => adb.getDropboxPrint(adbId),
|
|
302
|
+
getExitInfo: () => adb.getActivityExitInfo(adbId, config.bundleId),
|
|
303
|
+
crashArtifactWriter: init.crashArtifactWriter,
|
|
304
|
+
signal: init.signal,
|
|
305
|
+
});
|
|
306
306
|
},
|
|
307
307
|
dispose: async () => {
|
|
308
308
|
await adb.stopApp(adbId, config.bundleId);
|
|
@@ -314,30 +314,20 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
314
314
|
await adb.stopEmulator(adbId);
|
|
315
315
|
}
|
|
316
316
|
},
|
|
317
|
-
isAppRunning: async () => {
|
|
318
|
-
return await adb.isAppRunning(adbId, config.bundleId);
|
|
319
|
-
},
|
|
320
|
-
createAppMonitor: (options?: CreateAppMonitorOptions) => {
|
|
321
|
-
if (!detectNativeCrashes) {
|
|
322
|
-
return createNoopAppMonitor();
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return createAndroidAppMonitor({
|
|
326
|
-
adbId,
|
|
327
|
-
bundleId: config.bundleId,
|
|
328
|
-
appUid,
|
|
329
|
-
crashArtifactWriter: options?.crashArtifactWriter,
|
|
330
|
-
});
|
|
331
|
-
},
|
|
332
317
|
};
|
|
333
318
|
};
|
|
334
319
|
|
|
335
320
|
export const getAndroidPhysicalDevicePlatformInstance = async (
|
|
336
321
|
config: AndroidPlatformConfig,
|
|
337
322
|
harnessConfig: HarnessConfig,
|
|
323
|
+
init?: HarnessPlatformInitOptions
|
|
338
324
|
): Promise<HarnessPlatformRunner> => {
|
|
339
325
|
assertAndroidDevicePhysical(config.device);
|
|
340
|
-
const
|
|
326
|
+
const adb = instrumented(
|
|
327
|
+
adbModule,
|
|
328
|
+
'platform.android.adb',
|
|
329
|
+
init?.diagnostics ?? noopDiagnostics
|
|
330
|
+
);
|
|
341
331
|
const permissionsEnabled = harnessConfig.permissions ?? false;
|
|
342
332
|
|
|
343
333
|
const adbId = await getAdbId(config.device);
|
|
@@ -351,7 +341,7 @@ export const getAndroidPhysicalDevicePlatformInstance = async (
|
|
|
351
341
|
if (!isInstalled) {
|
|
352
342
|
throw new AppNotInstalledError(
|
|
353
343
|
config.bundleId,
|
|
354
|
-
getDeviceName(config.device)
|
|
344
|
+
getDeviceName(config.device)
|
|
355
345
|
);
|
|
356
346
|
}
|
|
357
347
|
|
|
@@ -362,47 +352,36 @@ export const getAndroidPhysicalDevicePlatformInstance = async (
|
|
|
362
352
|
}
|
|
363
353
|
|
|
364
354
|
return {
|
|
365
|
-
|
|
366
|
-
await adb.startApp(
|
|
367
|
-
adbId,
|
|
368
|
-
config.bundleId,
|
|
369
|
-
config.activityName,
|
|
370
|
-
(options as typeof config.appLaunchOptions | undefined) ??
|
|
371
|
-
config.appLaunchOptions,
|
|
372
|
-
);
|
|
373
|
-
},
|
|
374
|
-
restartApp: async (options) => {
|
|
355
|
+
createAppSession: async (options) => {
|
|
375
356
|
await adb.stopApp(adbId, config.bundleId);
|
|
376
|
-
|
|
377
|
-
adbId,
|
|
378
|
-
config.bundleId,
|
|
379
|
-
config.activityName,
|
|
357
|
+
const launchOptions =
|
|
380
358
|
(options as typeof config.appLaunchOptions | undefined) ??
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
359
|
+
config.appLaunchOptions;
|
|
360
|
+
|
|
361
|
+
return await createAndroidAppSession({
|
|
362
|
+
appUid,
|
|
363
|
+
bundleId: config.bundleId,
|
|
364
|
+
startApp: () =>
|
|
365
|
+
adb.startApp(
|
|
366
|
+
adbId,
|
|
367
|
+
config.bundleId,
|
|
368
|
+
config.activityName,
|
|
369
|
+
launchOptions
|
|
370
|
+
),
|
|
371
|
+
stopApp: () => adb.stopApp(adbId, config.bundleId),
|
|
372
|
+
getAppPid: () => adb.getAppPid(adbId, config.bundleId),
|
|
373
|
+
getLogcatTimestamp: () => adb.getLogcatTimestamp(adbId),
|
|
374
|
+
startLogcat: (args, options) => adb.startLogcat(adbId, args, options),
|
|
375
|
+
getDropboxOutput: () => adb.getDropboxPrint(adbId),
|
|
376
|
+
getExitInfo: () => adb.getActivityExitInfo(adbId, config.bundleId),
|
|
377
|
+
crashArtifactWriter: init?.crashArtifactWriter,
|
|
378
|
+
signal: init?.signal,
|
|
379
|
+
});
|
|
386
380
|
},
|
|
387
381
|
dispose: async () => {
|
|
388
382
|
await adb.stopApp(adbId, config.bundleId);
|
|
389
383
|
await clearHarnessDebugHttpHost(adbId, config.bundleId);
|
|
390
384
|
await adb.setHideErrorDialogs(adbId, false);
|
|
391
385
|
},
|
|
392
|
-
isAppRunning: async () => {
|
|
393
|
-
return await adb.isAppRunning(adbId, config.bundleId);
|
|
394
|
-
},
|
|
395
|
-
createAppMonitor: (options?: CreateAppMonitorOptions) => {
|
|
396
|
-
if (!detectNativeCrashes) {
|
|
397
|
-
return createNoopAppMonitor();
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return createAndroidAppMonitor({
|
|
401
|
-
adbId,
|
|
402
|
-
bundleId: config.bundleId,
|
|
403
|
-
appUid,
|
|
404
|
-
crashArtifactWriter: options?.crashArtifactWriter,
|
|
405
|
-
});
|
|
406
|
-
},
|
|
407
386
|
};
|
|
408
387
|
};
|
package/src/runner.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HarnessPlatformRunner,
|
|
3
|
-
type HarnessPlatformInitOptions,
|
|
4
|
-
} from '@react-native-harness/platforms';
|
|
1
|
+
import { type HarnessPlatformRunnerFactory } from '@react-native-harness/platforms';
|
|
5
2
|
import type { Config as HarnessConfig } from '@react-native-harness/config';
|
|
6
3
|
import {
|
|
7
4
|
AndroidPlatformConfigSchema,
|
|
@@ -17,11 +14,10 @@ import {
|
|
|
17
14
|
initializeAndroidProcessEnv,
|
|
18
15
|
} from './environment.js';
|
|
19
16
|
|
|
20
|
-
const getAndroidRunner
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
): Promise<HarnessPlatformRunner> => {
|
|
17
|
+
const getAndroidRunner: HarnessPlatformRunnerFactory<
|
|
18
|
+
AndroidPlatformConfig,
|
|
19
|
+
HarnessConfig
|
|
20
|
+
> = async (config, harnessConfig, init) => {
|
|
25
21
|
const parsedConfig = AndroidPlatformConfigSchema.parse(config);
|
|
26
22
|
|
|
27
23
|
initializeAndroidProcessEnv();
|
|
@@ -32,11 +28,15 @@ const getAndroidRunner = async (
|
|
|
32
28
|
return getAndroidEmulatorPlatformInstance(
|
|
33
29
|
parsedConfig,
|
|
34
30
|
harnessConfig,
|
|
35
|
-
init
|
|
31
|
+
init
|
|
36
32
|
);
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
return getAndroidPhysicalDevicePlatformInstance(
|
|
35
|
+
return getAndroidPhysicalDevicePlatformInstance(
|
|
36
|
+
parsedConfig,
|
|
37
|
+
harnessConfig,
|
|
38
|
+
init
|
|
39
|
+
);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
export default getAndroidRunner;
|
package/test/setup.ts
CHANGED
|
@@ -57,6 +57,7 @@ vi.mock('../src/adb.js', async (importOriginal) => {
|
|
|
57
57
|
waitForEmulatorDisconnect: vi.fn().mockResolvedValue(undefined),
|
|
58
58
|
waitForBoot: vi.fn().mockResolvedValue('emulator-5554'),
|
|
59
59
|
isAppRunning: vi.fn().mockResolvedValue(false),
|
|
60
|
+
getAppPid: vi.fn().mockResolvedValue(null),
|
|
60
61
|
getAppUid: vi.fn().mockResolvedValue(0),
|
|
61
62
|
setHideErrorDialogs: vi.fn().mockResolvedValue(undefined),
|
|
62
63
|
getLogcatTimestamp: vi.fn().mockResolvedValue('01-01 00:00:00.000'),
|