@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.
Files changed (62) hide show
  1. package/dist/__tests__/adb-wait.test.d.ts +2 -0
  2. package/dist/__tests__/adb-wait.test.d.ts.map +1 -0
  3. package/dist/__tests__/adb-wait.test.js +69 -0
  4. package/dist/__tests__/app-session.test.d.ts +2 -0
  5. package/dist/__tests__/app-session.test.d.ts.map +1 -0
  6. package/dist/__tests__/app-session.test.js +57 -0
  7. package/dist/__tests__/avd-config.test.js +58 -0
  8. package/dist/__tests__/crash-diagnostics.test.d.ts +2 -0
  9. package/dist/__tests__/crash-diagnostics.test.d.ts.map +1 -0
  10. package/dist/__tests__/crash-diagnostics.test.js +119 -0
  11. package/dist/__tests__/crash-reporter.test.d.ts +2 -0
  12. package/dist/__tests__/crash-reporter.test.d.ts.map +1 -0
  13. package/dist/__tests__/crash-reporter.test.js +150 -0
  14. package/dist/__tests__/emulator-startup.test.js +30 -3
  15. package/dist/__tests__/instance.test.js +139 -24
  16. package/dist/adb.d.ts +7 -1
  17. package/dist/adb.d.ts.map +1 -1
  18. package/dist/adb.js +72 -23
  19. package/dist/app-session.d.ts +26 -0
  20. package/dist/app-session.d.ts.map +1 -0
  21. package/dist/app-session.js +214 -0
  22. package/dist/avd-config.d.ts +1 -0
  23. package/dist/avd-config.d.ts.map +1 -1
  24. package/dist/avd-config.js +11 -0
  25. package/dist/crash-diagnostics.d.ts +40 -0
  26. package/dist/crash-diagnostics.d.ts.map +1 -0
  27. package/dist/crash-diagnostics.js +256 -0
  28. package/dist/crash-reporter.d.ts +12 -0
  29. package/dist/crash-reporter.d.ts.map +1 -0
  30. package/dist/crash-reporter.js +150 -0
  31. package/dist/emulator-startup.d.ts +23 -0
  32. package/dist/emulator-startup.d.ts.map +1 -1
  33. package/dist/emulator-startup.js +58 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1 -0
  37. package/dist/instance.d.ts +1 -1
  38. package/dist/instance.d.ts.map +1 -1
  39. package/dist/instance.js +51 -72
  40. package/dist/runner.d.ts +2 -2
  41. package/dist/runner.d.ts.map +1 -1
  42. package/dist/runner.js +1 -1
  43. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  44. package/package.json +4 -4
  45. package/src/__tests__/adb-wait.test.ts +95 -0
  46. package/src/__tests__/app-session.test.ts +76 -0
  47. package/src/__tests__/avd-config.test.ts +70 -0
  48. package/src/__tests__/crash-diagnostics.test.ts +144 -0
  49. package/src/__tests__/crash-reporter.test.ts +167 -0
  50. package/src/__tests__/emulator-startup.test.ts +53 -6
  51. package/src/__tests__/instance.test.ts +226 -84
  52. package/src/adb.ts +129 -63
  53. package/src/app-session.ts +316 -0
  54. package/src/avd-config.ts +17 -0
  55. package/src/crash-diagnostics.ts +471 -0
  56. package/src/crash-reporter.ts +263 -0
  57. package/src/emulator-startup.ts +74 -0
  58. package/src/index.ts +1 -0
  59. package/src/instance.ts +84 -105
  60. package/src/runner.ts +11 -11
  61. package/test/setup.ts +1 -0
  62. package/src/app-monitor.ts +0 -577
@@ -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 adb from './adb.js';
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 type { AppMonitor } from '@react-native-harness/platforms';
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
- adb.reversePort(adbId, metroPort),
85
- adb.reversePort(adbId, 8080),
86
- adb.setHideErrorDialogs(adbId, true),
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 adb.getAppUid(adbId, config.bundleId);
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 adb.startEmulator>[1];
91
+ mode?: Parameters<typeof adbModule.startEmulator>[1];
101
92
  }): Promise<string> => {
102
93
  await ensureAndroidEmulatorAvailable();
103
- await adb.startEmulator(emulatorName, mode);
104
- return adb.waitForBoot(emulatorName, signal);
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 adb.createAvd({
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 adb.hasAvd(emulatorName);
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 adb.deleteAvd(emulatorName);
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 adb.stopEmulator(generationAdbId);
178
- await adb.waitForEmulatorDisconnect(generationAdbId, signal);
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 detectNativeCrashes = harnessConfig.detectNativeCrashes ?? true;
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
- startApp: async (options) => {
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
- await adb.startApp(
297
- adbId,
298
- config.bundleId,
299
- config.activityName,
283
+ const launchOptions =
300
284
  (options as typeof config.appLaunchOptions | undefined) ??
301
- config.appLaunchOptions,
302
- );
303
- },
304
- stopApp: async () => {
305
- await adb.stopApp(adbId, config.bundleId);
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 detectNativeCrashes = harnessConfig.detectNativeCrashes ?? true;
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
- startApp: async (options) => {
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
- await adb.startApp(
377
- adbId,
378
- config.bundleId,
379
- config.activityName,
357
+ const launchOptions =
380
358
  (options as typeof config.appLaunchOptions | undefined) ??
381
- config.appLaunchOptions,
382
- );
383
- },
384
- stopApp: async () => {
385
- await adb.stopApp(adbId, config.bundleId);
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 = async (
21
- config: AndroidPlatformConfig,
22
- harnessConfig: HarnessConfig,
23
- init: HarnessPlatformInitOptions,
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(parsedConfig, harnessConfig);
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'),