obsidian-integration-testing 12.0.0 → 12.1.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 (176) hide show
  1. package/dist/lib/cjs/adb-device-list.cjs +54 -0
  2. package/dist/lib/cjs/adb-device-list.d.cts +72 -0
  3. package/dist/lib/cjs/android-sdk.cjs +52 -0
  4. package/dist/lib/cjs/android-sdk.d.cts +29 -0
  5. package/dist/lib/cjs/app-startup-progress.cjs +87 -0
  6. package/dist/lib/cjs/app-startup-progress.d.cts +128 -0
  7. package/dist/lib/cjs/appium-server-marker.cjs +29 -32
  8. package/dist/lib/cjs/appium-server-marker.d.cts +22 -0
  9. package/dist/lib/cjs/avd-probe-verdict.cjs +90 -0
  10. package/dist/lib/cjs/avd-probe-verdict.d.cts +175 -0
  11. package/dist/lib/cjs/community-plugin-registry.cjs +1 -1
  12. package/dist/lib/cjs/community-plugin-registry.d.cts +1 -1
  13. package/dist/lib/cjs/config-directory-fallback-error.cjs +57 -0
  14. package/dist/lib/cjs/config-directory-fallback-error.d.cts +64 -0
  15. package/dist/lib/cjs/config-directory.cjs +46 -0
  16. package/dist/lib/cjs/config-directory.d.cts +45 -0
  17. package/dist/lib/cjs/connect-to-cdp.cjs +13 -7
  18. package/dist/lib/cjs/connect-to-cdp.d.cts +19 -0
  19. package/dist/lib/cjs/demo-vault-bootstrap.cjs +1 -1
  20. package/dist/lib/cjs/demo-vault-bootstrap.d.cts +3 -2
  21. package/dist/lib/cjs/demo-vault-populate.cjs +3 -2
  22. package/dist/lib/cjs/device-readiness.cjs +38 -3
  23. package/dist/lib/cjs/device-readiness.d.cts +83 -9
  24. package/dist/lib/cjs/emulator-arguments.cjs +6 -2
  25. package/dist/lib/cjs/emulator-arguments.d.cts +26 -0
  26. package/dist/lib/cjs/emulator-backend.cjs +71 -0
  27. package/dist/lib/cjs/emulator-backend.d.cts +76 -0
  28. package/dist/lib/cjs/emulator-liveness.cjs +72 -0
  29. package/dist/lib/cjs/emulator-liveness.d.cts +158 -0
  30. package/dist/lib/cjs/emulator-snapshot.cjs +57 -0
  31. package/dist/lib/cjs/emulator-snapshot.d.cts +68 -0
  32. package/dist/lib/cjs/global-setup-core.cjs +30 -30
  33. package/dist/lib/cjs/global-setup-core.d.cts +27 -2
  34. package/dist/lib/cjs/headless-vault-config.cjs +51 -0
  35. package/dist/lib/cjs/headless-vault-config.d.cts +67 -0
  36. package/dist/lib/cjs/index.cjs +12 -1
  37. package/dist/lib/cjs/index.d.cts +6 -1
  38. package/dist/lib/cjs/library.cjs +1 -1
  39. package/dist/lib/cjs/mobile-input.cjs +14 -2
  40. package/dist/lib/cjs/mobile-input.d.cts +5 -9
  41. package/dist/lib/cjs/namespace-bootstrap.cjs +1 -1
  42. package/dist/lib/cjs/obsidian-instance.cjs +52 -3
  43. package/dist/lib/cjs/obsidian-instance.d.cts +12 -0
  44. package/dist/lib/cjs/open-obsidian-settings-tab.cjs +1 -1
  45. package/dist/lib/cjs/open-obsidian-settings-tab.d.cts +3 -2
  46. package/dist/lib/cjs/owned-instance-exit-marker.cjs +99 -0
  47. package/dist/lib/cjs/owned-instance-exit-marker.d.cts +106 -0
  48. package/dist/lib/cjs/owned-instance-exited-error.cjs +106 -0
  49. package/dist/lib/cjs/owned-instance-exited-error.d.cts +98 -0
  50. package/dist/lib/cjs/owned-vault-seed.cjs +9 -5
  51. package/dist/lib/cjs/owned-vault-seed.d.cts +24 -3
  52. package/dist/lib/cjs/parent-liveness.cjs +42 -5
  53. package/dist/lib/cjs/parent-liveness.d.cts +37 -5
  54. package/dist/lib/cjs/port-owner.cjs +61 -0
  55. package/dist/lib/cjs/port-owner.d.cts +47 -0
  56. package/dist/lib/cjs/process-capture.cjs +62 -0
  57. package/dist/lib/cjs/process-capture.d.cts +83 -0
  58. package/dist/lib/cjs/process-exit-message.cjs +14 -8
  59. package/dist/lib/cjs/process-exit-message.d.cts +37 -3
  60. package/dist/lib/cjs/process-liveness.cjs +51 -0
  61. package/dist/lib/cjs/process-liveness.d.cts +18 -0
  62. package/dist/lib/cjs/run-version-matrix.cjs +1 -1
  63. package/dist/lib/cjs/run-version-matrix.d.cts +1 -1
  64. package/dist/lib/cjs/teardown-verdict.cjs +53 -0
  65. package/dist/lib/cjs/teardown-verdict.d.cts +69 -0
  66. package/dist/lib/cjs/temporary-vault.cjs +43 -8
  67. package/dist/lib/cjs/temporary-vault.d.cts +47 -5
  68. package/dist/lib/cjs/transport-appium.cjs +102 -17
  69. package/dist/lib/cjs/transport-appium.d.cts +53 -3
  70. package/dist/lib/cjs/transport-desktop-cdp.cjs +195 -6
  71. package/dist/lib/cjs/transport-desktop-cdp.d.cts +99 -0
  72. package/dist/lib/cjs/transport-factory.cjs +951 -133
  73. package/dist/lib/cjs/transport-options.cjs +11 -3
  74. package/dist/lib/cjs/transport-options.d.cts +131 -12
  75. package/dist/lib/cjs/transport.cjs +1 -1
  76. package/dist/lib/cjs/transport.d.cts +7 -1
  77. package/dist/lib/cjs/vault-plugin-install.cjs +40 -0
  78. package/dist/lib/cjs/vault-plugin-install.d.cts +64 -0
  79. package/dist/lib/cjs/vault-teardown-verdict.cjs +42 -0
  80. package/dist/lib/cjs/vault-teardown-verdict.d.cts +57 -0
  81. package/dist/lib/cjs/version-matrix.cjs +1 -1
  82. package/dist/lib/cjs/version-matrix.d.cts +2 -2
  83. package/dist/lib/cjs/webview-cdp.cjs +6 -7
  84. package/dist/lib/cjs/webview-cdp.d.cts +5 -1
  85. package/dist/lib/esm/adb-device-list.d.mts +72 -0
  86. package/dist/lib/esm/adb-device-list.mjs +28 -0
  87. package/dist/lib/esm/android-sdk.d.mts +29 -0
  88. package/dist/lib/esm/android-sdk.mjs +18 -0
  89. package/dist/lib/esm/app-startup-progress.d.mts +128 -0
  90. package/dist/lib/esm/app-startup-progress.mjs +59 -0
  91. package/dist/lib/esm/appium-server-marker.d.mts +22 -0
  92. package/dist/lib/esm/appium-server-marker.mjs +28 -22
  93. package/dist/lib/esm/avd-probe-verdict.d.mts +175 -0
  94. package/dist/lib/esm/avd-probe-verdict.mjs +62 -0
  95. package/dist/lib/esm/community-plugin-registry.d.mts +1 -1
  96. package/dist/lib/esm/community-plugin-registry.mjs +1 -1
  97. package/dist/lib/esm/config-directory-fallback-error.d.mts +64 -0
  98. package/dist/lib/esm/config-directory-fallback-error.mjs +33 -0
  99. package/dist/lib/esm/config-directory.d.mts +45 -0
  100. package/dist/lib/esm/config-directory.mjs +21 -0
  101. package/dist/lib/esm/connect-to-cdp.d.mts +19 -0
  102. package/dist/lib/esm/connect-to-cdp.mjs +13 -7
  103. package/dist/lib/esm/demo-vault-bootstrap.d.mts +3 -2
  104. package/dist/lib/esm/demo-vault-bootstrap.mjs +1 -1
  105. package/dist/lib/esm/demo-vault-populate.mjs +3 -2
  106. package/dist/lib/esm/device-readiness.d.mts +83 -9
  107. package/dist/lib/esm/device-readiness.mjs +34 -2
  108. package/dist/lib/esm/emulator-arguments.d.mts +26 -0
  109. package/dist/lib/esm/emulator-arguments.mjs +6 -2
  110. package/dist/lib/esm/emulator-backend.d.mts +76 -0
  111. package/dist/lib/esm/emulator-backend.mjs +45 -0
  112. package/dist/lib/esm/emulator-liveness.d.mts +158 -0
  113. package/dist/lib/esm/emulator-liveness.mjs +47 -0
  114. package/dist/lib/esm/emulator-snapshot.d.mts +68 -0
  115. package/dist/lib/esm/emulator-snapshot.mjs +32 -0
  116. package/dist/lib/esm/global-setup-core.d.mts +27 -2
  117. package/dist/lib/esm/global-setup-core.mjs +34 -35
  118. package/dist/lib/esm/headless-vault-config.d.mts +67 -0
  119. package/dist/lib/esm/headless-vault-config.mjs +31 -0
  120. package/dist/lib/esm/index.d.mts +6 -1
  121. package/dist/lib/esm/index.mjs +11 -1
  122. package/dist/lib/esm/library.mjs +1 -1
  123. package/dist/lib/esm/mobile-input.d.mts +5 -9
  124. package/dist/lib/esm/mobile-input.mjs +14 -2
  125. package/dist/lib/esm/namespace-bootstrap.mjs +1 -1
  126. package/dist/lib/esm/obsidian-instance.d.mts +12 -0
  127. package/dist/lib/esm/obsidian-instance.mjs +55 -3
  128. package/dist/lib/esm/open-obsidian-settings-tab.d.mts +3 -2
  129. package/dist/lib/esm/open-obsidian-settings-tab.mjs +1 -1
  130. package/dist/lib/esm/owned-instance-exit-marker.d.mts +106 -0
  131. package/dist/lib/esm/owned-instance-exit-marker.mjs +78 -0
  132. package/dist/lib/esm/owned-instance-exited-error.d.mts +98 -0
  133. package/dist/lib/esm/owned-instance-exited-error.mjs +81 -0
  134. package/dist/lib/esm/owned-vault-seed.d.mts +24 -3
  135. package/dist/lib/esm/owned-vault-seed.mjs +9 -5
  136. package/dist/lib/esm/parent-liveness.d.mts +37 -5
  137. package/dist/lib/esm/parent-liveness.mjs +42 -5
  138. package/dist/lib/esm/port-owner.d.mts +47 -0
  139. package/dist/lib/esm/port-owner.mjs +36 -0
  140. package/dist/lib/esm/process-capture.d.mts +83 -0
  141. package/dist/lib/esm/process-capture.mjs +38 -0
  142. package/dist/lib/esm/process-exit-message.d.mts +37 -3
  143. package/dist/lib/esm/process-exit-message.mjs +13 -8
  144. package/dist/lib/esm/process-liveness.d.mts +18 -0
  145. package/dist/lib/esm/process-liveness.mjs +17 -0
  146. package/dist/lib/esm/run-version-matrix.d.mts +1 -1
  147. package/dist/lib/esm/run-version-matrix.mjs +1 -1
  148. package/dist/lib/esm/teardown-verdict.d.mts +69 -0
  149. package/dist/lib/esm/teardown-verdict.mjs +28 -0
  150. package/dist/lib/esm/temporary-vault.d.mts +47 -5
  151. package/dist/lib/esm/temporary-vault.mjs +43 -8
  152. package/dist/lib/esm/transport-appium.d.mts +53 -3
  153. package/dist/lib/esm/transport-appium.mjs +108 -17
  154. package/dist/lib/esm/transport-desktop-cdp.d.mts +99 -0
  155. package/dist/lib/esm/transport-desktop-cdp.mjs +202 -7
  156. package/dist/lib/esm/transport-factory.mjs +995 -138
  157. package/dist/lib/esm/transport-options.d.mts +131 -12
  158. package/dist/lib/esm/transport-options.mjs +9 -2
  159. package/dist/lib/esm/transport.d.mts +7 -1
  160. package/dist/lib/esm/vault-plugin-install.d.mts +64 -0
  161. package/dist/lib/esm/vault-plugin-install.mjs +20 -0
  162. package/dist/lib/esm/vault-teardown-verdict.d.mts +57 -0
  163. package/dist/lib/esm/vault-teardown-verdict.mjs +18 -0
  164. package/dist/lib/esm/version-matrix.d.mts +2 -2
  165. package/dist/lib/esm/version-matrix.mjs +1 -1
  166. package/dist/lib/esm/webview-cdp.d.mts +5 -1
  167. package/dist/lib/esm/webview-cdp.mjs +6 -7
  168. package/package.json +24 -23
  169. package/dist/lib/cjs/native-dialog-monitor.cjs +0 -192
  170. package/dist/lib/cjs/native-dialog-monitor.d.cts +0 -66
  171. package/dist/lib/cjs/obsidian-namespace.cjs +0 -17
  172. package/dist/lib/cjs/obsidian-namespace.d.cts +0 -128
  173. package/dist/lib/esm/native-dialog-monitor.d.mts +0 -66
  174. package/dist/lib/esm/native-dialog-monitor.mjs +0 -158
  175. package/dist/lib/esm/obsidian-namespace.d.mts +0 -128
  176. package/dist/lib/esm/obsidian-namespace.mjs +0 -1
@@ -1,24 +1,41 @@
1
1
  /**
2
2
  * @file
3
3
  *
4
- * Pure helpers for deciding when a freshly-booted Android emulator is idle
5
- * enough to establish an Appium session, plus resolving the idle-wait timeout
6
- * from the transport options. Kept separate from the integration-only
4
+ * Pure helpers for deciding when a freshly-booted Android emulator is ready
5
+ * enough to establish an Appium session, plus resolving the two readiness-wait
6
+ * timeouts from the transport options. Kept separate from the integration-only
7
7
  * `transport-factory` so the parsing and default resolution stay unit-testable
8
8
  * (the factory itself needs a real emulator and is excluded from unit tests).
9
9
  *
10
- * `sys.boot_completed` fires *before* the guest is actually idle: package
11
- * optimization (`dex2oat`) and system services keep churning after it, so a
12
- * session established the instant it fires makes every one of UiAutomator2's
13
- * serialized `adb` round-trips contend with that work and inflate ~3x. Gating
14
- * the session on a later, quieter signal (the boot animation has stopped and
15
- * the package manager is serving) lets it run against an idle guest instead.
10
+ * Readiness is two independent questions, waited on in two phases:
11
+ *
12
+ * 1. **Is the guest idle?** `sys.boot_completed` fires *before* it is: package
13
+ * optimization (`dex2oat`) and system services keep churning after it, so a
14
+ * session established the instant it fires makes every one of UiAutomator2's
15
+ * serialized `adb` round-trips contend with that work and inflate ~3x. Gating
16
+ * the session on a later, quieter signal (the boot animation has stopped and
17
+ * the package manager is serving) lets it run against an idle guest instead.
18
+ * 2. **Does the guest have a network?** Both idle signals are true well *before*
19
+ * the default network is created and validated — that lands ~80s into guest
20
+ * uptime, tens of seconds after the idle gate clears — and neither says
21
+ * anything about connectivity, so an idle-only gate hands the suite a device
22
+ * with no route. See {@link checkNetworkValidated} for the measurement and
23
+ * for why the three obvious connectivity probes are all wrong here.
16
24
  */
17
25
  import type { ObsidianAndroidAppiumTransportOptions } from './transport-options.cjs';
18
26
  /**
19
27
  * Default for {@link ObsidianAndroidAppiumTransportOptions.deviceIdleTimeoutInMilliseconds}.
20
28
  */
21
29
  export declare const DEFAULT_DEVICE_IDLE_TIMEOUT_IN_MILLISECONDS = 60000;
30
+ /**
31
+ * Default for {@link ObsidianAndroidAppiumTransportOptions.networkReadyTimeoutInMilliseconds}.
32
+ *
33
+ * The budget starts *after* `sys.boot_completed`, where the measured wait on a
34
+ * cold WHPX emulator is ~30s (validation at ~80s from boot, against a boot flag
35
+ * at ~56s). The default is headroom for the starved/CI regime the other Android
36
+ * budgets are already sized for, not the expected cost.
37
+ */
38
+ export declare const DEFAULT_NETWORK_READY_TIMEOUT_IN_MILLISECONDS = 120000;
22
39
  /**
23
40
  * Parameters for {@link checkDeviceIdle}.
24
41
  */
@@ -32,6 +49,15 @@ export interface CheckDeviceIdleParams {
32
49
  */
33
50
  readonly packageListOutput: string;
34
51
  }
52
+ /**
53
+ * Parameters for {@link checkNetworkValidated}.
54
+ */
55
+ export interface CheckNetworkValidatedParams {
56
+ /**
57
+ Raw stdout of `adb shell dumpsys connectivity`.
58
+ */
59
+ readonly connectivityOutput: string;
60
+ }
35
61
  /**
36
62
  * Decides whether a booted emulator is idle enough to start an Appium session,
37
63
  * from the raw output of the two probe commands.
@@ -45,6 +71,42 @@ export interface CheckDeviceIdleParams {
45
71
  * @returns `true` when the guest is idle enough to proceed.
46
72
  */
47
73
  export declare function checkDeviceIdle(params: CheckDeviceIdleParams): boolean;
74
+ /**
75
+ * Decides whether a booted emulator has a working network, from the raw output
76
+ * of `adb shell dumpsys connectivity`.
77
+ *
78
+ * Ready means the dump names an **active default network** *and* that network's
79
+ * own agent info reports validation — either an `IS_VALIDATED` score policy or a
80
+ * non-zero `firstValidated` timestamp. Both are required: a default network
81
+ * exists for seconds before it validates, and during that window it carries no
82
+ * usable route.
83
+ *
84
+ * Why this matters at all: the two {@link checkDeviceIdle} signals are satisfied
85
+ * well before this one. Validation lands ~70-80s into guest uptime — measured on
86
+ * `obsidian_test`, `created 70635 firstValidated 72681`, and on a cold boot of
87
+ * `obsidian_screenshots`, `created 66870 firstValidated 79870`, which at 85s
88
+ * uptime still scored `EVER_EVALUATED&IS_UNMETERED` with no `IS_VALIDATED` and
89
+ * no `firstValidated` field at all. Against a boot flag at ~56s that leaves a
90
+ * gap of tens of seconds on a fast host, and more on a slow one. A suite started
91
+ * in that gap does not merely run slowly: a test that reaches the network runs
92
+ * to completion against a silently empty result, which no assertion inside the
93
+ * suite can tell apart from a genuinely empty feed.
94
+ *
95
+ * `dumpsys connectivity` is the honest source, and the three probes that look
96
+ * like better ones are all wrong — each of them misled a full day of
97
+ * investigation before this was understood:
98
+ *
99
+ * - `ip route` shows **no** default route on a healthy device: Android uses
100
+ * per-network policy routing, and the route lives in table 1015, not `main`.
101
+ * - `ping` fails on a healthy device: QEMU user-mode NAT does not forward ICMP
102
+ * on Windows hosts.
103
+ * - `getprop net.dns1` is empty on every modern Android: DNS went per-network
104
+ * in 8/9.
105
+ *
106
+ * @param params - The sampled probe output.
107
+ * @returns `true` when the guest has a validated default network.
108
+ */
109
+ export declare function checkNetworkValidated(params: CheckNetworkValidatedParams): boolean;
48
110
  /**
49
111
  * Resolves the post-boot device-idle wait timeout, applying the default when
50
112
  * the option is omitted.
@@ -57,3 +119,15 @@ export declare function checkDeviceIdle(params: CheckDeviceIdleParams): boolean;
57
119
  * @returns The timeout in milliseconds.
58
120
  */
59
121
  export declare function resolveDeviceIdleTimeoutInMilliseconds(options: ObsidianAndroidAppiumTransportOptions): number;
122
+ /**
123
+ * Resolves the network-ready wait timeout, applying the default when the option
124
+ * is omitted.
125
+ *
126
+ * This bounds how long the factory waits, after the idle gate, for the guest to
127
+ * report a validated default network (see {@link checkNetworkValidated}) before
128
+ * establishing the session. A value of `0` skips the wait entirely.
129
+ *
130
+ * @param options - The Android Appium transport options.
131
+ * @returns The timeout in milliseconds.
132
+ */
133
+ export declare function resolveNetworkReadyTimeoutInMilliseconds(options: ObsidianAndroidAppiumTransportOptions): number;
@@ -23,7 +23,11 @@ __export(emulator_arguments_exports, {
23
23
  module.exports = __toCommonJS(emulator_arguments_exports);
24
24
  const DNS_SERVER = "8.8.8.8";
25
25
  function buildEmulatorArguments(params) {
26
- const emulatorArguments = ["-avd", params.avdName, "-no-snapshot-save", "-dns-server", DNS_SERVER];
26
+ const emulatorArguments = ["-avd", params.avdName];
27
+ if (!params.shouldReuseSnapshot) {
28
+ emulatorArguments.push("-no-snapshot-load", "-no-snapshot-save");
29
+ }
30
+ emulatorArguments.push("-dns-server", DNS_SERVER);
27
31
  if (params.isHidden) {
28
32
  emulatorArguments.push("-no-window");
29
33
  }
@@ -33,4 +37,4 @@ function buildEmulatorArguments(params) {
33
37
  0 && (module.exports = {
34
38
  buildEmulatorArguments
35
39
  });
36
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2VtdWxhdG9yLWFyZ3VtZW50cy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBAZmlsZVxuICpcbiAqIEJ1aWxkcyB0aGUgYXJndW1lbnQgbGlzdCBmb3Igc3Bhd25pbmcgdGhlIEFuZHJvaWQgZW11bGF0b3IuXG4gKi9cblxuY29uc3QgRE5TX1NFUlZFUiA9ICc4LjguOC44JztcblxuLyoqXG4gKiBQYXJhbWV0ZXJzIGZvciB7QGxpbmsgYnVpbGRFbXVsYXRvckFyZ3VtZW50c30uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRFbXVsYXRvckFyZ3VtZW50c1BhcmFtcyB7XG4gIC8qKlxuICBUaGUgQW5kcm9pZCBWaXJ0dWFsIERldmljZSBuYW1lLlxuICAgKi9cbiAgcmVhZG9ubHkgYXZkTmFtZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIHJ1biB0aGUgZW11bGF0b3IgaGVhZGxlc3MgKGAtbm8td2luZG93YCksIHNvIGl0IG5ldmVyIHN0ZWFsc1xuICAgKiBmb2N1cy4gUmVzb2x2ZWQgZnJvbSB0aGUgYGlzRW11bGF0b3JWaXNpYmxlYCB0cmFuc3BvcnQgb3B0aW9uLlxuICAgKi9cbiAgcmVhZG9ubHkgaXNIaWRkZW46IGJvb2xlYW47XG59XG5cbi8qKlxuICogQnVpbGRzIHRoZSBhcmd1bWVudCBsaXN0IGZvciBzcGF3bmluZyB0aGUgQW5kcm9pZCBlbXVsYXRvci5cbiAqXG4gKiBAcGFyYW0gcGFyYW1zIC0gVGhlIGVtdWxhdG9yIGFyZ3VtZW50IHBhcmFtZXRlcnMuXG4gKiBAcmV0dXJucyBUaGUgYXJndW1lbnQgYXJyYXkgdG8gcGFzcyB0byB0aGUgZW11bGF0b3IgYmluYXJ5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRFbXVsYXRvckFyZ3VtZW50cyhwYXJhbXM6IEJ1aWxkRW11bGF0b3JBcmd1bWVudHNQYXJhbXMpOiBzdHJpbmdbXSB7XG4gIGNvbnN0IGVtdWxhdG9yQXJndW1lbnRzID0gWyctYXZkJywgcGFyYW1zLmF2ZE5hbWUsICctbm8tc25hcHNob3Qtc2F2ZScsICctZG5zLXNlcnZlcicsIEROU19TRVJWRVJdO1xuICBpZiAocGFyYW1zLmlzSGlkZGVuKSB7XG4gICAgZW11bGF0b3JBcmd1bWVudHMucHVzaCgnLW5vLXdpbmRvdycpO1xuICB9XG4gIHJldHVybiBlbXVsYXRvckFyZ3VtZW50cztcbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFNQSxNQUFNLGFBQWE7QUF3QlosU0FBUyx1QkFBdUIsUUFBZ0Q7QUFDckYsUUFBTSxvQkFBb0IsQ0FBQyxRQUFRLE9BQU8sU0FBUyxxQkFBcUIsZUFBZSxVQUFVO0FBQ2pHLE1BQUksT0FBTyxVQUFVO0FBQ25CLHNCQUFrQixLQUFLLFlBQVk7QUFBQSxFQUNyQztBQUNBLFNBQU87QUFDVDsiLAogICJuYW1lcyI6IFtdCn0K
40
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2VtdWxhdG9yLWFyZ3VtZW50cy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBAZmlsZVxuICpcbiAqIEJ1aWxkcyB0aGUgYXJndW1lbnQgbGlzdCBmb3Igc3Bhd25pbmcgdGhlIEFuZHJvaWQgZW11bGF0b3IuXG4gKlxuICogKipUaGUgaGFybmVzcyBvd25zIHRoZSBzbmFwc2hvdCBpdCBsb2Fkcywgb3IgaXQgbG9hZHMgbm9uZS4qKiBUaGlzIGZpbGUgdXNlZFxuICogdG8gcGFzcyBgLW5vLXNuYXBzaG90LXNhdmVgIGFsb25lIFx1MjAxNCB0aGUgb25lIGNvbWJpbmF0aW9uIHRoYXQgaXMgbmV2ZXJcbiAqIGNvcnJlY3QuIEl0IG5ldmVyICp3cml0ZXMqIGBkZWZhdWx0X2Jvb3RgLCB5ZXQgZXZlcnkgcnVuICpyZXN1bWVkKiBpdCwgc28gdGhlXG4gKiBndWVzdCBlYWNoIHJ1biBzdGFydGVkIGZyb20gd2FzIHN0YXRlIG5vdGhpbmcgaGFkIHZhbGlkYXRlZCBzaW5jZSB3aGVuZXZlciBhXG4gKiBodW1hbiBsYXN0IHNhdmVkIGl0LiBNZWFzdXJlZCAyMDI2LTA5LTAzLCBzYW1lIEFWRCBiYWNrIHRvIGJhY2s6XG4gKlxuICogfCBMYXVuY2ggfCBSZXN1bHQgfFxuICogfCAtLS0gfCAtLS0gfFxuICogfCBzbmFwc2hvdCByZXN1bWVkIChgLW5vLXNuYXBzaG90LXNhdmVgIGFsb25lKSB8IGd1ZXN0IGRpZXMgfjkwcyBpbiwgZXZlcnkgdGltZSB8XG4gKiB8IGAtbm8tc25hcHNob3QtbG9hZGAgKGNvbGQpIHwgYm9vdGVkIGluIDUwcywgYWxpdmUgYXQgNjAvMTIwLzE4MC8yNDBzIHxcbiAqXG4gKiBBIHJlc3VtZWQgc25hcHNob3Qgcm90cyBzaWxlbnRseSwgYW5kIHRoZSBmYWlsdXJlIGl0IGV2ZW50dWFsbHkgcHJvZHVjZXMgXHUyMDE0XG4gKiBhIGRldmljZSB0aGF0IHNlcnZlcyBhZGIsIGFjY2VwdHMgYSBzZXNzaW9uIGFuZCB0aGVuIGRyb3BzIGBvZmZsaW5lYCBhXG4gKiBoYWxmLW1pbnV0ZSBsYXRlciBcdTIwMTQgaXMgaW5kaXN0aW5ndWlzaGFibGUgZnJvbSBhIGNvZGUgcmVncmVzc2lvbi4gU28gdGhlXG4gKiBkZWZhdWx0IGlzIGEgZnVsbCBjb2xkIGJvb3QsIGFuZCBzbmFwc2hvdCByZXVzZSBpcyBhbiBleHBsaWNpdCBvcHQtaW4gdGhhdFxuICogdGFrZXMgKipib3RoKiogaGFsdmVzOiBsb2FkIGFuZCBzYXZlIHRvZ2V0aGVyLCBzbyB0aGUgc25hcHNob3QgdGhlIG5leHQgcnVuXG4gKiByZXN1bWVzIGlzIG9uZSB0aGlzIHJ1biB3cm90ZS5cbiAqL1xuXG5jb25zdCBETlNfU0VSVkVSID0gJzguOC44LjgnO1xuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBidWlsZEVtdWxhdG9yQXJndW1lbnRzfS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdWlsZEVtdWxhdG9yQXJndW1lbnRzUGFyYW1zIHtcbiAgLyoqXG4gIFRoZSBBbmRyb2lkIFZpcnR1YWwgRGV2aWNlIG5hbWUuXG4gICAqL1xuICByZWFkb25seSBhdmROYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gcnVuIHRoZSBlbXVsYXRvciBoZWFkbGVzcyAoYC1uby13aW5kb3dgKSwgc28gaXQgbmV2ZXIgc3RlYWxzXG4gICAqIGZvY3VzLiBSZXNvbHZlZCBmcm9tIHRoZSBgaXNFbXVsYXRvclZpc2libGVgIHRyYW5zcG9ydCBvcHRpb24uXG4gICAqL1xuICByZWFkb25seSBpc0hpZGRlbjogYm9vbGVhbjtcblxuICAvKipcbiAgICogV2hldGhlciB0aGUgZW11bGF0b3IgbWF5IHJlc3VtZSBcdTIwMTQgYW5kIHJlZnJlc2ggXHUyMDE0IHRoZSBBVkQncyBzYXZlZCBib290XG4gICAqIHNuYXBzaG90LiBSZXNvbHZlZCBmcm9tIHRoZSBgc2hvdWxkUmV1c2VFbXVsYXRvclNuYXBzaG90YCB0cmFuc3BvcnQgb3B0aW9uLlxuICAgKlxuICAgKiBgZmFsc2VgICh0aGUgZGVmYXVsdCkgY29sZC1ib290cyBoZXJtZXRpY2FsbHkuIGB0cnVlYCBidXlzIGJhY2sgdGhlIH4xMTJzXG4gICAqIGJvb3Qgb24gYSBwZXJzaXN0ZW50IHJ1bm5lciwgYXQgdGhlIGNvc3Qgb2YgdGVzdCBpc29sYXRpb24uXG4gICAqL1xuICByZWFkb25seSBzaG91bGRSZXVzZVNuYXBzaG90OiBib29sZWFuO1xufVxuXG4vKipcbiAqIEJ1aWxkcyB0aGUgYXJndW1lbnQgbGlzdCBmb3Igc3Bhd25pbmcgdGhlIEFuZHJvaWQgZW11bGF0b3IuXG4gKlxuICogQHBhcmFtIHBhcmFtcyAtIFRoZSBlbXVsYXRvciBhcmd1bWVudCBwYXJhbWV0ZXJzLlxuICogQHJldHVybnMgVGhlIGFyZ3VtZW50IGFycmF5IHRvIHBhc3MgdG8gdGhlIGVtdWxhdG9yIGJpbmFyeS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkRW11bGF0b3JBcmd1bWVudHMocGFyYW1zOiBCdWlsZEVtdWxhdG9yQXJndW1lbnRzUGFyYW1zKTogc3RyaW5nW10ge1xuICBjb25zdCBlbXVsYXRvckFyZ3VtZW50cyA9IFsnLWF2ZCcsIHBhcmFtcy5hdmROYW1lXTtcbiAgLypcbiAgICogQm90aCBmbGFncyBvciBuZWl0aGVyOiBsb2FkaW5nIHdpdGhvdXQgc2F2aW5nIGlzIHRoZSByZXN1bWUtd2hhdC13ZS1uZXZlci1cbiAgICogd3JvdGUgY29tYmluYXRpb24gdGhpcyBmaWxlJ3MgaGVhZGVyIGV4aXN0cyB0byBydWxlIG91dC5cbiAgICovXG4gIGlmICghcGFyYW1zLnNob3VsZFJldXNlU25hcHNob3QpIHtcbiAgICBlbXVsYXRvckFyZ3VtZW50cy5wdXNoKCctbm8tc25hcHNob3QtbG9hZCcsICctbm8tc25hcHNob3Qtc2F2ZScpO1xuICB9XG4gIGVtdWxhdG9yQXJndW1lbnRzLnB1c2goJy1kbnMtc2VydmVyJywgRE5TX1NFUlZFUik7XG4gIGlmIChwYXJhbXMuaXNIaWRkZW4pIHtcbiAgICBlbXVsYXRvckFyZ3VtZW50cy5wdXNoKCctbm8td2luZG93Jyk7XG4gIH1cbiAgcmV0dXJuIGVtdWxhdG9yQXJndW1lbnRzO1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQXdCQSxNQUFNLGFBQWE7QUFpQ1osU0FBUyx1QkFBdUIsUUFBZ0Q7QUFDckYsUUFBTSxvQkFBb0IsQ0FBQyxRQUFRLE9BQU8sT0FBTztBQUtqRCxNQUFJLENBQUMsT0FBTyxxQkFBcUI7QUFDL0Isc0JBQWtCLEtBQUsscUJBQXFCLG1CQUFtQjtBQUFBLEVBQ2pFO0FBQ0Esb0JBQWtCLEtBQUssZUFBZSxVQUFVO0FBQ2hELE1BQUksT0FBTyxVQUFVO0FBQ25CLHNCQUFrQixLQUFLLFlBQVk7QUFBQSxFQUNyQztBQUNBLFNBQU87QUFDVDsiLAogICJuYW1lcyI6IFtdCn0K
@@ -2,6 +2,24 @@
2
2
  * @file
3
3
  *
4
4
  * Builds the argument list for spawning the Android emulator.
5
+ *
6
+ * **The harness owns the snapshot it loads, or it loads none.** This file used
7
+ * to pass `-no-snapshot-save` alone — the one combination that is never
8
+ * correct. It never *writes* `default_boot`, yet every run *resumed* it, so the
9
+ * guest each run started from was state nothing had validated since whenever a
10
+ * human last saved it. Measured 2026-09-03, same AVD back to back:
11
+ *
12
+ * | Launch | Result |
13
+ * | --- | --- |
14
+ * | snapshot resumed (`-no-snapshot-save` alone) | guest dies ~90s in, every time |
15
+ * | `-no-snapshot-load` (cold) | booted in 50s, alive at 60/120/180/240s |
16
+ *
17
+ * A resumed snapshot rots silently, and the failure it eventually produces —
18
+ * a device that serves adb, accepts a session and then drops `offline` a
19
+ * half-minute later — is indistinguishable from a code regression. So the
20
+ * default is a full cold boot, and snapshot reuse is an explicit opt-in that
21
+ * takes **both** halves: load and save together, so the snapshot the next run
22
+ * resumes is one this run wrote.
5
23
  */
6
24
  /**
7
25
  * Parameters for {@link buildEmulatorArguments}.
@@ -16,6 +34,14 @@ export interface BuildEmulatorArgumentsParams {
16
34
  * focus. Resolved from the `isEmulatorVisible` transport option.
17
35
  */
18
36
  readonly isHidden: boolean;
37
+ /**
38
+ * Whether the emulator may resume — and refresh — the AVD's saved boot
39
+ * snapshot. Resolved from the `shouldReuseEmulatorSnapshot` transport option.
40
+ *
41
+ * `false` (the default) cold-boots hermetically. `true` buys back the ~112s
42
+ * boot on a persistent runner, at the cost of test isolation.
43
+ */
44
+ readonly shouldReuseSnapshot: boolean;
19
45
  }
20
46
  /**
21
47
  * Builds the argument list for spawning the Android emulator.
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var emulator_backend_exports = {};
20
+ __export(emulator_backend_exports, {
21
+ parsePosixProcessList: () => parsePosixProcessList,
22
+ parseWindowsTaskList: () => parseWindowsTaskList,
23
+ selectEmulatorBackendPids: () => selectEmulatorBackendPids
24
+ });
25
+ module.exports = __toCommonJS(emulator_backend_exports);
26
+ const EMULATOR_BACKEND_NAME_PATTERN = /^(?:emulator|emulator64-[\w.-]+|qemu-system-[\w.-]+)$/;
27
+ const EXECUTABLE_SUFFIX_PATTERN = /\.exe$/;
28
+ const RADIX_DECIMAL = 10;
29
+ const TASK_LIST_FIELDS_READ = 2;
30
+ function parsePosixProcessList(processListOutput) {
31
+ const entries = [];
32
+ for (const rawLine of processListOutput.split("\n")) {
33
+ const line = rawLine.trim();
34
+ const separatorIndex = line.indexOf(" ");
35
+ if (separatorIndex === -1) {
36
+ continue;
37
+ }
38
+ const pid = Number.parseInt(line.slice(0, separatorIndex), RADIX_DECIMAL);
39
+ if (Number.isNaN(pid)) {
40
+ continue;
41
+ }
42
+ entries.push({ name: line.slice(separatorIndex + 1).trim(), pid });
43
+ }
44
+ return entries;
45
+ }
46
+ function parseWindowsTaskList(taskListOutput) {
47
+ const entries = [];
48
+ for (const rawLine of taskListOutput.split("\n")) {
49
+ const [rawName = "", rawPid = ""] = rawLine.trim().replaceAll('"', "").split(",", TASK_LIST_FIELDS_READ);
50
+ const pid = Number.parseInt(rawPid, RADIX_DECIMAL);
51
+ if (rawName.length === 0 || Number.isNaN(pid)) {
52
+ continue;
53
+ }
54
+ entries.push({ name: rawName, pid });
55
+ }
56
+ return entries;
57
+ }
58
+ function selectEmulatorBackendPids(params) {
59
+ return params.processes.filter((entry) => checkIsEmulatorBackendName(entry.name) && !params.knownPids.includes(entry.pid)).map((entry) => entry.pid);
60
+ }
61
+ function checkIsEmulatorBackendName(name) {
62
+ const baseName = name.slice(Math.max(name.lastIndexOf("/"), name.lastIndexOf("\\")) + 1);
63
+ return EMULATOR_BACKEND_NAME_PATTERN.test(baseName.toLowerCase().replace(EXECUTABLE_SUFFIX_PATTERN, ""));
64
+ }
65
+ // Annotate the CommonJS export names for ESM import in node:
66
+ 0 && (module.exports = {
67
+ parsePosixProcessList,
68
+ parseWindowsTaskList,
69
+ selectEmulatorBackendPids
70
+ });
71
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2VtdWxhdG9yLWJhY2tlbmQudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQGZpbGVcbiAqXG4gKiBQdXJlIGhlbHBlcnMgZm9yIGZpbmRpbmcgdGhlIEFuZHJvaWQgZW11bGF0b3IgcHJvY2VzcyB0aGF0ICoqb3V0bGl2ZXMgdGhlIHBpZFxuICogdGhlIGhhcm5lc3Mga2lsbGVkKiouXG4gKlxuICogVW5kZXIgYC1uby13aW5kb3dgIHRoZSBlbXVsYXRvcidzIGJhY2tlbmQgcHJvY2VzcyBpc1xuICogYHFlbXUtc3lzdGVtLXg4Nl82NC1oZWFkbGVzc2AgKGBxZW11LXN5c3RlbS14ODZfNjRgIHdoZW4gd2luZG93ZWQpLCBub3QgdGhlXG4gKiBgZW11bGF0b3JgIGxhdW5jaGVyIHRoZSBoYXJuZXNzIHNwYXduZWQgYW5kIGhvbGRzIGEgYENoaWxkUHJvY2Vzc2AgZm9yLiBUaGVcbiAqIGxhdW5jaGVyIGV4aXRzIG9yIGlzIGtpbGxlZCwgYW5kIHRoZSBiYWNrZW5kIGlzIHdoYXQgYWN0dWFsbHkgaG9sZHMgdGhlIEFWRCdzXG4gKiBgbXVsdGlpbnN0YW5jZS5sb2NrYCBhbmQgdGhlIGNvbnNvbGUvYWRiIHBvcnRzIFx1MjAxNCBzbyBgdGFza2tpbGwgL0YgL1RgIGFnYWluc3RcbiAqIHRoZSBsYXVuY2hlcidzIHBpZCByZXBvcnRzIHN1Y2Nlc3Mgd2hpbGUgdGhlIGVtdWxhdG9yIGtlZXBzIHJ1bm5pbmcuIEV2ZXJ5XG4gKiBsYXRlciBydW4gYWdhaW5zdCB0aGF0IEFWRCB0aGVuIGRpZXMgd2l0aCBgUnVubmluZyBtdWx0aXBsZSBlbXVsYXRvcnMgd2l0aCB0aGVcbiAqIHNhbWUgQVZEIGlzIGFuIGV4cGVyaW1lbnRhbCBmZWF0dXJlYCwgd2hpY2ggbmV2ZXIgcmVhY2hlcyB0aGUgdXNlciBiZWNhdXNlIHRoZVxuICogZW11bGF0b3Igd3JpdGVzIGl0IHRvIGl0cyBvd24gc3Rkb3V0LlxuICpcbiAqIE5vdGUgdGhhdCB0aGUgb2J2aW91cyBmaWx0ZXIgXHUyMDE0IGAtTmFtZSBxZW11LXN5c3RlbS14ODZfNjRgIC8gbWF0Y2hpbmcgdGhhdFxuICogZXhhY3QgaW1hZ2UgbmFtZSBcdTIwMTQgZG9lcyAqKm5vdCoqIG1hdGNoIHRoZSBgLWhlYWRsZXNzYCBidWlsZCwgd2hpY2ggaXMgaG93IHRoZVxuICogbGVmdG92ZXIgc3RheXMgaW52aXNpYmxlIHRocm91Z2ggc2V2ZXJhbCByb3VuZHMgb2YgXCJubyBlbXVsYXRvciBpcyBydW5uaW5nXCIuXG4gKlxuICogT3duZXJzaGlwIGlzIGRlY2lkZWQgYnkgYSAqKnByZS1sYXVuY2ggc25hcHNob3QqKjogYSBiYWNrZW5kIGFscmVhZHkgcnVubmluZ1xuICogYmVmb3JlIHRoaXMgcnVuIHN0YXJ0ZWQgaXRzIG93biBiZWxvbmdzIHRvIHNvbWVib2R5IGVsc2UgKGFub3RoZXIgQVZELCBvciBvbmVcbiAqIHRoZSB1c2VyIGJvb3RlZCBieSBoYW5kKSBhbmQgaXMgbmV2ZXIgdG91Y2hlZC4gS2VwdCBzZXBhcmF0ZSBmcm9tIHRoZVxuICogaW50ZWdyYXRpb24tb25seSBgdHJhbnNwb3J0LWZhY3RvcnlgIHNvIHRoZSBwYXJzaW5nIGFuZCB0aGUgc2VsZWN0aW9uIHN0YXlcbiAqIHVuaXQtdGVzdGFibGUuXG4gKi9cblxuLyoqXG4gKiBPbmUgcHJvY2VzcyBmcm9tIGEgaG9zdCBwcm9jZXNzIGxpc3RpbmcuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHJvY2Vzc0xpc3RFbnRyeSB7XG4gIC8qKlxuICBUaGUgcHJvY2Vzcy9pbWFnZSBuYW1lLCBhcyB0aGUgcGxhdGZvcm0ncyBsaXN0aW5nIHJlcG9ydHMgaXQuXG4gICAqL1xuICByZWFkb25seSBuYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gIFRoZSBwcm9jZXNzIElELlxuICAgKi9cbiAgcmVhZG9ubHkgcGlkOiBudW1iZXI7XG59XG5cbi8qKlxuICogUGFyYW1ldGVycyBmb3Ige0BsaW5rIHNlbGVjdEVtdWxhdG9yQmFja2VuZFBpZHN9LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNlbGVjdEVtdWxhdG9yQmFja2VuZFBpZHNQYXJhbXMge1xuICAvKipcbiAgRW11bGF0b3IgYmFja2VuZCBQSURzIHNlZW4gKipiZWZvcmUqKiB0aGlzIHJ1biBsYXVuY2hlZCBpdHMgZW11bGF0b3IsIHdoaWNoIGl0IHRoZXJlZm9yZSBkb2VzIG5vdCBvd24uXG4gICAqL1xuICByZWFkb25seSBrbm93blBpZHM6IHJlYWRvbmx5IG51bWJlcltdO1xuXG4gIC8qKlxuICBUaGUgaG9zdCdzIGN1cnJlbnQgcHJvY2VzcyBsaXN0aW5nLlxuICAgKi9cbiAgcmVhZG9ubHkgcHJvY2Vzc2VzOiByZWFkb25seSBQcm9jZXNzTGlzdEVudHJ5W107XG59XG5cbi8qKlxuICogTWF0Y2hlcyB0aGUgZW11bGF0b3IgbGF1bmNoZXIgYW5kIGJvdGggUUVNVSBiYWNrZW5kIGJ1aWxkcyBcdTIwMTQgY3J1Y2lhbGx5XG4gKiBpbmNsdWRpbmcgdGhlIGAtaGVhZGxlc3NgIHN1ZmZpeCBgLW5vLXdpbmRvd2AgcnVucyBwcm9kdWNlLlxuICovXG5jb25zdCBFTVVMQVRPUl9CQUNLRU5EX05BTUVfUEFUVEVSTiA9IC9eKD86ZW11bGF0b3J8ZW11bGF0b3I2NC1bXFx3Li1dK3xxZW11LXN5c3RlbS1bXFx3Li1dKykkLztcbmNvbnN0IEVYRUNVVEFCTEVfU1VGRklYX1BBVFRFUk4gPSAvXFwuZXhlJC87XG5jb25zdCBSQURJWF9ERUNJTUFMID0gMTA7XG4vKipcbiAqIE9ubHkgdGhlIGltYWdlIG5hbWUgYW5kIHRoZSBQSUQgYXJlIHJlYWQsIHNvIHRoZSBzcGxpdCBzdG9wcyBhZnRlciB0aGVtLlxuICovXG5jb25zdCBUQVNLX0xJU1RfRklFTERTX1JFQUQgPSAyO1xuXG4vKipcbiAqIFBhcnNlcyBgcHMgLWVvIHBpZD0sY29tbT1gIG91dHB1dC5cbiAqXG4gKiBAcGFyYW0gcHJvY2Vzc0xpc3RPdXRwdXQgLSBSYXcgc3Rkb3V0IG9mIHRoZSBgcHNgIGxpc3RpbmcuXG4gKiBAcmV0dXJucyBPbmUgZW50cnkgcGVyIHBhcnNhYmxlIHJvdywgaW4gbGlzdGVkIG9yZGVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VQb3NpeFByb2Nlc3NMaXN0KHByb2Nlc3NMaXN0T3V0cHV0OiBzdHJpbmcpOiBQcm9jZXNzTGlzdEVudHJ5W10ge1xuICBjb25zdCBlbnRyaWVzOiBQcm9jZXNzTGlzdEVudHJ5W10gPSBbXTtcblxuICBmb3IgKGNvbnN0IHJhd0xpbmUgb2YgcHJvY2Vzc0xpc3RPdXRwdXQuc3BsaXQoJ1xcbicpKSB7XG4gICAgY29uc3QgbGluZSA9IHJhd0xpbmUudHJpbSgpO1xuICAgIGNvbnN0IHNlcGFyYXRvckluZGV4ID0gbGluZS5pbmRleE9mKCcgJyk7XG4gICAgaWYgKHNlcGFyYXRvckluZGV4ID09PSAtMSkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgY29uc3QgcGlkID0gTnVtYmVyLnBhcnNlSW50KGxpbmUuc2xpY2UoMCwgc2VwYXJhdG9ySW5kZXgpLCBSQURJWF9ERUNJTUFMKTtcbiAgICBpZiAoTnVtYmVyLmlzTmFOKHBpZCkpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGVudHJpZXMucHVzaCh7IG5hbWU6IGxpbmUuc2xpY2Uoc2VwYXJhdG9ySW5kZXggKyAxKS50cmltKCksIHBpZCB9KTtcbiAgfVxuXG4gIHJldHVybiBlbnRyaWVzO1xufVxuXG4vKipcbiAqIFBhcnNlcyBgdGFza2xpc3QgL0ZPIENTViAvTkhgIG91dHB1dC5cbiAqXG4gKiBPbmx5IHRoZSBmaXJzdCB0d28gZmllbGRzIGFyZSByZWFkIFx1MjAxNCB0aGUgaW1hZ2UgbmFtZSBhbmQgdGhlIFBJRCBcdTIwMTQgc28gdGhlXG4gKiBtZW1vcnkgY29sdW1uJ3Mgb3duIGNvbW1hcyBjYW5ub3QgY29uZnVzZSB0aGUgc3BsaXQuXG4gKlxuICogQHBhcmFtIHRhc2tMaXN0T3V0cHV0IC0gUmF3IHN0ZG91dCBvZiB0aGUgYHRhc2tsaXN0YCBsaXN0aW5nLlxuICogQHJldHVybnMgT25lIGVudHJ5IHBlciBwYXJzYWJsZSByb3csIGluIGxpc3RlZCBvcmRlci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlV2luZG93c1Rhc2tMaXN0KHRhc2tMaXN0T3V0cHV0OiBzdHJpbmcpOiBQcm9jZXNzTGlzdEVudHJ5W10ge1xuICBjb25zdCBlbnRyaWVzOiBQcm9jZXNzTGlzdEVudHJ5W10gPSBbXTtcblxuICBmb3IgKGNvbnN0IHJhd0xpbmUgb2YgdGFza0xpc3RPdXRwdXQuc3BsaXQoJ1xcbicpKSB7XG4gICAgY29uc3QgW3Jhd05hbWUgPSAnJywgcmF3UGlkID0gJyddID0gcmF3TGluZS50cmltKCkucmVwbGFjZUFsbCgnXCInLCAnJykuc3BsaXQoJywnLCBUQVNLX0xJU1RfRklFTERTX1JFQUQpO1xuICAgIGNvbnN0IHBpZCA9IE51bWJlci5wYXJzZUludChyYXdQaWQsIFJBRElYX0RFQ0lNQUwpO1xuICAgIGlmIChyYXdOYW1lLmxlbmd0aCA9PT0gMCB8fCBOdW1iZXIuaXNOYU4ocGlkKSkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgZW50cmllcy5wdXNoKHsgbmFtZTogcmF3TmFtZSwgcGlkIH0pO1xuICB9XG5cbiAgcmV0dXJuIGVudHJpZXM7XG59XG5cbi8qKlxuICogU2VsZWN0cyB0aGUgZW11bGF0b3IgcHJvY2Vzc2VzIHRoaXMgcnVuIGlzIHJlc3BvbnNpYmxlIGZvciBraWxsaW5nLlxuICpcbiAqIEBwYXJhbSBwYXJhbXMgLSBUaGUgY3VycmVudCBsaXN0aW5nIHBsdXMgdGhlIFBJRHMgdGhhdCBwcmVkYXRlIHRoaXMgcnVuJ3MgZW11bGF0b3IuXG4gKiBAcmV0dXJucyBUaGUgUElEcyBvZiBlbXVsYXRvciBwcm9jZXNzZXMgdGhpcyBydW4gb3ducywgaW4gbGlzdGVkIG9yZGVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2VsZWN0RW11bGF0b3JCYWNrZW5kUGlkcyhwYXJhbXM6IFNlbGVjdEVtdWxhdG9yQmFja2VuZFBpZHNQYXJhbXMpOiBudW1iZXJbXSB7XG4gIHJldHVybiBwYXJhbXMucHJvY2Vzc2VzXG4gICAgLmZpbHRlcigoZW50cnkpID0+IGNoZWNrSXNFbXVsYXRvckJhY2tlbmROYW1lKGVudHJ5Lm5hbWUpICYmICFwYXJhbXMua25vd25QaWRzLmluY2x1ZGVzKGVudHJ5LnBpZCkpXG4gICAgLm1hcCgoZW50cnkpID0+IGVudHJ5LnBpZCk7XG59XG5cbmZ1bmN0aW9uIGNoZWNrSXNFbXVsYXRvckJhY2tlbmROYW1lKG5hbWU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBiYXNlTmFtZSA9IG5hbWUuc2xpY2UoTWF0aC5tYXgobmFtZS5sYXN0SW5kZXhPZignLycpLCBuYW1lLmxhc3RJbmRleE9mKCdcXFxcJykpICsgMSk7XG4gIHJldHVybiBFTVVMQVRPUl9CQUNLRU5EX05BTUVfUEFUVEVSTi50ZXN0KGJhc2VOYW1lLnRvTG93ZXJDYXNlKCkucmVwbGFjZShFWEVDVVRBQkxFX1NVRkZJWF9QQVRURVJOLCAnJykpO1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUE2REEsTUFBTSxnQ0FBZ0M7QUFDdEMsTUFBTSw0QkFBNEI7QUFDbEMsTUFBTSxnQkFBZ0I7QUFJdEIsTUFBTSx3QkFBd0I7QUFRdkIsU0FBUyxzQkFBc0IsbUJBQStDO0FBQ25GLFFBQU0sVUFBOEIsQ0FBQztBQUVyQyxhQUFXLFdBQVcsa0JBQWtCLE1BQU0sSUFBSSxHQUFHO0FBQ25ELFVBQU0sT0FBTyxRQUFRLEtBQUs7QUFDMUIsVUFBTSxpQkFBaUIsS0FBSyxRQUFRLEdBQUc7QUFDdkMsUUFBSSxtQkFBbUIsSUFBSTtBQUN6QjtBQUFBLElBQ0Y7QUFFQSxVQUFNLE1BQU0sT0FBTyxTQUFTLEtBQUssTUFBTSxHQUFHLGNBQWMsR0FBRyxhQUFhO0FBQ3hFLFFBQUksT0FBTyxNQUFNLEdBQUcsR0FBRztBQUNyQjtBQUFBLElBQ0Y7QUFFQSxZQUFRLEtBQUssRUFBRSxNQUFNLEtBQUssTUFBTSxpQkFBaUIsQ0FBQyxFQUFFLEtBQUssR0FBRyxJQUFJLENBQUM7QUFBQSxFQUNuRTtBQUVBLFNBQU87QUFDVDtBQVdPLFNBQVMscUJBQXFCLGdCQUE0QztBQUMvRSxRQUFNLFVBQThCLENBQUM7QUFFckMsYUFBVyxXQUFXLGVBQWUsTUFBTSxJQUFJLEdBQUc7QUFDaEQsVUFBTSxDQUFDLFVBQVUsSUFBSSxTQUFTLEVBQUUsSUFBSSxRQUFRLEtBQUssRUFBRSxXQUFXLEtBQUssRUFBRSxFQUFFLE1BQU0sS0FBSyxxQkFBcUI7QUFDdkcsVUFBTSxNQUFNLE9BQU8sU0FBUyxRQUFRLGFBQWE7QUFDakQsUUFBSSxRQUFRLFdBQVcsS0FBSyxPQUFPLE1BQU0sR0FBRyxHQUFHO0FBQzdDO0FBQUEsSUFDRjtBQUVBLFlBQVEsS0FBSyxFQUFFLE1BQU0sU0FBUyxJQUFJLENBQUM7QUFBQSxFQUNyQztBQUVBLFNBQU87QUFDVDtBQVFPLFNBQVMsMEJBQTBCLFFBQW1EO0FBQzNGLFNBQU8sT0FBTyxVQUNYLE9BQU8sQ0FBQyxVQUFVLDJCQUEyQixNQUFNLElBQUksS0FBSyxDQUFDLE9BQU8sVUFBVSxTQUFTLE1BQU0sR0FBRyxDQUFDLEVBQ2pHLElBQUksQ0FBQyxVQUFVLE1BQU0sR0FBRztBQUM3QjtBQUVBLFNBQVMsMkJBQTJCLE1BQXVCO0FBQ3pELFFBQU0sV0FBVyxLQUFLLE1BQU0sS0FBSyxJQUFJLEtBQUssWUFBWSxHQUFHLEdBQUcsS0FBSyxZQUFZLElBQUksQ0FBQyxJQUFJLENBQUM7QUFDdkYsU0FBTyw4QkFBOEIsS0FBSyxTQUFTLFlBQVksRUFBRSxRQUFRLDJCQUEyQixFQUFFLENBQUM7QUFDekc7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Pure helpers for finding the Android emulator process that **outlives the pid
5
+ * the harness killed**.
6
+ *
7
+ * Under `-no-window` the emulator's backend process is
8
+ * `qemu-system-x86_64-headless` (`qemu-system-x86_64` when windowed), not the
9
+ * `emulator` launcher the harness spawned and holds a `ChildProcess` for. The
10
+ * launcher exits or is killed, and the backend is what actually holds the AVD's
11
+ * `multiinstance.lock` and the console/adb ports — so `taskkill /F /T` against
12
+ * the launcher's pid reports success while the emulator keeps running. Every
13
+ * later run against that AVD then dies with `Running multiple emulators with the
14
+ * same AVD is an experimental feature`, which never reaches the user because the
15
+ * emulator writes it to its own stdout.
16
+ *
17
+ * Note that the obvious filter — `-Name qemu-system-x86_64` / matching that
18
+ * exact image name — does **not** match the `-headless` build, which is how the
19
+ * leftover stays invisible through several rounds of "no emulator is running".
20
+ *
21
+ * Ownership is decided by a **pre-launch snapshot**: a backend already running
22
+ * before this run started its own belongs to somebody else (another AVD, or one
23
+ * the user booted by hand) and is never touched. Kept separate from the
24
+ * integration-only `transport-factory` so the parsing and the selection stay
25
+ * unit-testable.
26
+ */
27
+ /**
28
+ * One process from a host process listing.
29
+ */
30
+ export interface ProcessListEntry {
31
+ /**
32
+ The process/image name, as the platform's listing reports it.
33
+ */
34
+ readonly name: string;
35
+ /**
36
+ The process ID.
37
+ */
38
+ readonly pid: number;
39
+ }
40
+ /**
41
+ * Parameters for {@link selectEmulatorBackendPids}.
42
+ */
43
+ export interface SelectEmulatorBackendPidsParams {
44
+ /**
45
+ Emulator backend PIDs seen **before** this run launched its emulator, which it therefore does not own.
46
+ */
47
+ readonly knownPids: readonly number[];
48
+ /**
49
+ The host's current process listing.
50
+ */
51
+ readonly processes: readonly ProcessListEntry[];
52
+ }
53
+ /**
54
+ * Parses `ps -eo pid=,comm=` output.
55
+ *
56
+ * @param processListOutput - Raw stdout of the `ps` listing.
57
+ * @returns One entry per parsable row, in listed order.
58
+ */
59
+ export declare function parsePosixProcessList(processListOutput: string): ProcessListEntry[];
60
+ /**
61
+ * Parses `tasklist /FO CSV /NH` output.
62
+ *
63
+ * Only the first two fields are read — the image name and the PID — so the
64
+ * memory column's own commas cannot confuse the split.
65
+ *
66
+ * @param taskListOutput - Raw stdout of the `tasklist` listing.
67
+ * @returns One entry per parsable row, in listed order.
68
+ */
69
+ export declare function parseWindowsTaskList(taskListOutput: string): ProcessListEntry[];
70
+ /**
71
+ * Selects the emulator processes this run is responsible for killing.
72
+ *
73
+ * @param params - The current listing plus the PIDs that predate this run's emulator.
74
+ * @returns The PIDs of emulator processes this run owns, in listed order.
75
+ */
76
+ export declare function selectEmulatorBackendPids(params: SelectEmulatorBackendPidsParams): number[];
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var emulator_liveness_exports = {};
20
+ __export(emulator_liveness_exports, {
21
+ buildEmulatorLivenessMessage: () => buildEmulatorLivenessMessage,
22
+ resolveEmulatorLivenessVerdict: () => resolveEmulatorLivenessVerdict
23
+ });
24
+ module.exports = __toCommonJS(emulator_liveness_exports);
25
+ var import_avd_probe_verdict = require("./avd-probe-verdict.cjs");
26
+ var import_process_exit_message = require("./process-exit-message.cjs");
27
+ function buildEmulatorLivenessMessage(params) {
28
+ const budget = `${String(params.probeTimeoutInMilliseconds)}ms`;
29
+ const diagnosis = {
30
+ "device-gone": [
31
+ "The host's `adb devices` no longer lists it, so it is no longer serving adb at all.",
32
+ "Either the emulator exited, or it wedged badly enough to drop off adb \u2014 the emulator output below is the best evidence for which.",
33
+ "An emulator this run started is replaced automatically once, so seeing this means the replacement did not survive either. Check that nothing else on this host stops emulators, and that the emulator build and system image are sound."
34
+ ],
35
+ "emulator-wedged": [
36
+ `Neither the guest (\`adb -s ${params.deviceId} shell\`) nor the emulator's own console (\`adb -s ${params.deviceId} emu avd status\`) answered within ${budget}.`,
37
+ "The console is served by the emulator process, not by the guest, so its silence means the EMULATOR is wedged \u2014 not the guest, not the Appium server, and not the device being absent.",
38
+ "`adb devices` will still list this device and will mislead you: nothing is left running to update that state.",
39
+ "A wedged emulator does not come back, so retrying the session against it cannot succeed. One this run started is replaced with a fresh boot automatically, which means seeing this message is the replacement failing too.",
40
+ "If it recurs at the same point in every run, the fault is below the harness \u2014 and the first thing to check is what filters this host's sockets. Endpoint-security, content-blocking and VPN products install socket filter drivers in the path of every socket call the emulator makes, the console's localhost connection included; stopping that service has fixed exactly this signature. After that, the emulator build, the system image and the host hypervisor."
41
+ ],
42
+ "guest-unresponsive": [
43
+ `The guest (\`adb -s ${params.deviceId} shell\`) did not answer within ${budget}, but the emulator's own console did.`,
44
+ "So the emulator process is healthy and the guest is frozen or too starved to schedule `adbd`.",
45
+ "A contended host is the usual cause; `deviceIdleTimeoutInMilliseconds` is the budget that governs waiting one out.",
46
+ "But if it recurs at the same point in every run, no timeout will fix it, and the host is not merely busy. Check what filters this host's sockets first \u2014 a content-blocking, endpoint-security or VPN socket filter driver produces this verdict as readily as it produces a fully wedged emulator \u2014 then the emulator build, the system image and the host hypervisor."
47
+ ]
48
+ };
49
+ const lines = [
50
+ `Device ${params.deviceId} stopped answering before the Appium session could be established.`,
51
+ ...diagnosis[params.verdict]
52
+ ];
53
+ return (0, import_process_exit_message.appendProcessOutputTail)(lines.join("\n"), { output: params.emulatorOutput, outputLabel: "Emulator output" });
54
+ }
55
+ function resolveEmulatorLivenessVerdict(params) {
56
+ if (params.shellProbe === "answered") {
57
+ return "alive";
58
+ }
59
+ if (!params.isListedByAdb) {
60
+ return "device-gone";
61
+ }
62
+ if (!(0, import_avd_probe_verdict.checkIsEmulatorDeviceId)(params.deviceId)) {
63
+ return "guest-unresponsive";
64
+ }
65
+ return params.consoleProbe === "answered" ? "guest-unresponsive" : "emulator-wedged";
66
+ }
67
+ // Annotate the CommonJS export names for ESM import in node:
68
+ 0 && (module.exports = {
69
+ buildEmulatorLivenessMessage,
70
+ resolveEmulatorLivenessVerdict
71
+ });
72
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2VtdWxhdG9yLWxpdmVuZXNzLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogUHVyZSBjbGFzc2lmaWNhdGlvbiBvZiB3aGV0aGVyIHRoZSBkZXZpY2UgYSBydW4gaXMgYWJvdXQgdG8gZHJpdmUgaXMgc3RpbGxcbiAqICoqdGhlcmUqKiBcdTIwMTQgYW5kLCB3aGVuIGl0IGlzIG5vdCwgd2hpY2ggb2YgdGhlIHR3byB2ZXJ5IGRpZmZlcmVudCB0aGluZ3Mgd2VudFxuICogd3Jvbmc6IHRoZSBndWVzdCBmcm96ZSwgb3IgdGhlIGVtdWxhdG9yIHByb2Nlc3MgaXRzZWxmIGRpZC5cbiAqXG4gKiBUaGUgcmVhZGluZXNzIGdhdGVzIGluIGBkZXZpY2UtcmVhZGluZXNzLnRzYCBhbnN3ZXIgKlwiaXMgdGhlIGd1ZXN0IHJlYWR5XG4gKiB5ZXQ/XCIqLCBhbmQgYm90aCBhcmUgZGVsaWJlcmF0ZWx5IGJlc3QtZWZmb3J0OiB0aGV5IHdhcm4gYW5kIHByb2NlZWQuIE5vdGhpbmdcbiAqIGFza2VkICpcImlzIGl0IHN0aWxsIGFuc3dlcmluZz9cIiogYXQgdGhlIG1vbWVudCB0aGUgc2Vzc2lvbiBpcyBjcmVhdGVkLCBzbyBhXG4gKiBndWVzdCB0aGF0IHN0b3BwZWQgYW5zd2VyaW5nIGJldHdlZW4gdGhlIGxhc3QgZ2F0ZSBhbmQgYHJlbW90ZSgpYCB3YXMgaGFuZGVkXG4gKiB0byBBcHBpdW0gYW55d2F5LiBXaGF0IHRoZSBvcGVyYXRvciB0aGVuIHNhdyB3YXMgQXBwaXVtJ3Mgb3duIHJlcG9ydDpcbiAqXG4gKiBgYGB0ZXh0XG4gKiBXZWJEcml2ZXJFcnJvcjogRGV2aWNlIGVtdWxhdG9yLTU1NTQgd2FzIG5vdCBpbiB0aGUgbGlzdCBvZiBjb25uZWN0ZWQgZGV2aWNlc1xuICogICB3aGVuIHJ1bm5pbmcgXCJodHRwOi8vbG9jYWxob3N0OjQ3MjMvc2Vzc2lvblwiIHdpdGggbWV0aG9kIFwiUE9TVFwiXG4gKiBgYGBcbiAqXG4gKiBcdTIwMTQgbWVhc3VyZWQgb24gdGhpcyBob3N0IDIwMjYtMDktMDUsIGFmdGVyIDUxNXMuIFRoYXQgbmFtZXMgdGhlIGRldmljZSwgYW5kXG4gKiBgYWRiIGRldmljZXNgIHRoZW4gbGlzdHMgdGhlIGRldmljZSBoYXBwaWx5LCBzbyBpdCBzZW5kcyB0aGUgcmVhZGVyIHRvIHRoZSBvbmVcbiAqIGRpYWdub3N0aWMgdGhhdCBjYW5ub3QgaGVscC4gVGhlIHNhbWUgdHJhcCBgd2VkZ2VkLWFwcGl1bS1zZXJ2ZXIudHNgIGV4aXN0cyB0b1xuICogZXNjYXBlLCBvbmUgbGF5ZXIgZnVydGhlciBkb3duLlxuICpcbiAqICMjIFRoZSBjb25zb2xlIGlzIHdoYXQgdGVsbHMgdGhlIHR3byBhcGFydFxuICpcbiAqIGBhZGIgLXMgPGRldmljZT4gZW11IDxjb21tYW5kPmAgaXMgc2VydmVkIGJ5IHRoZSAqKmVtdWxhdG9yJ3Mgb3duIGNvbnNvbGUqKixcbiAqIG5vdCBieSB0aGUgZ3Vlc3QncyBgYWRiZGAuIFNvIHRoZSB0d28gcHJvYmVzIGFuc3dlciBkaWZmZXJlbnQgcXVlc3Rpb25zLCBhbmRcbiAqIHRoZSBwYWlyIGlzIGEgZGlhZ25vc2lzIHdoZXJlIGVpdGhlciBhbG9uZSBpcyBhIGd1ZXNzOlxuICpcbiAqIHwgYGFkYiBzaGVsbGAgfCBgYWRiIGVtdWAgfCBXaGF0IGl0IG1lYW5zIHxcbiAqIHwgLS0tIHwgLS0tIHwgLS0tIHxcbiAqIHwgc3VjY2VlZHMgfCBcdTIwMTQgfCBBbGl2ZS4gUHJvY2VlZC4gfFxuICogfCBmYWlscyB8IHN1Y2NlZWRzIHwgVGhlICoqZ3Vlc3QqKiBpcyBmcm96ZW4gb3IgdG9vIGJ1c3kgdG8gc2NoZWR1bGUgYGFkYmRgLiB8XG4gKiB8IGZhaWxzIHwgZmFpbHMgfCBUaGUgKiplbXVsYXRvciBwcm9jZXNzKiogaXMgdW5yZWFjaGFibGUuIE5vdGhpbmcgd2lsbCByZWNvdmVyIGl0LiB8XG4gKlxuICogT25seSBhICpzdWNjZXNzZnVsKiBjb25zb2xlIHByb2JlIGNsZWFycyB0aGUgZW11bGF0b3IgXHUyMDE0IHNlZVxuICoge0BsaW5rIEVtdWxhdG9yTGl2ZW5lc3NQcm9iZU91dGNvbWV9IGZvciB3aHkgYSBmYWlsZWQgb25lIGNhbm5vdCBiZSByZWFkIGFzXG4gKiB0aGUgZW11bGF0b3IgYW5zd2VyaW5nLlxuICpcbiAqIE1lYXN1cmVkIDIwMjYtMDktMDUgYWNyb3NzIHNpeCBoYW5kLWJvb3RzIG9uIHRoaXMgaG9zdCBcdTIwMTQgdHdvIEFWRHMgYW5kIGZpdmVcbiAqIGFyZ3VtZW50IHNldHMgXHUyMDE0IGV2ZXJ5IG9uZSBvZiB3aGljaCByZWFjaGVkIHRoZSBib3R0b20gcm93IDY0XHUyMDEzOTJzIGFmdGVyIGJvb3Q6XG4gKlxuICogYGBgdGV4dFxuICogODBzICBlbXVsYXRvciBjb25zb2xlIChRRU1VLCBub3QgdGhlIGd1ZXN0KTogSFVOR1xuICogODBzICBxZW11LXN5c3RlbS14ODZfNjQtaGVhZGxlc3MgKHBpZCAxMDAwNCk6IGNwdT0wJSByc3M9NS44MkdCXG4gKiBgYGBcbiAqXG4gKiAqKjAlIENQVSoqIFx1MjAxNCBibG9ja2VkLCBub3Qgc3Bpbm5pbmcgXHUyMDE0IGFuZCBhIGNvbnNvbGUgdGhhdCBjYW5ub3QgYW5zd2VyLiBUaGF0IGlzXG4gKiB0aGUgUUVNVSBtYWluIGxvb3AsIGJlbG93IGFueXRoaW5nIHRoZSBndWVzdCwgdGhlIEdQVSBzdGFjaywgdGhlIG5ldHdvcmtcbiAqIHNpbXVsYXRvciBvciB0aGUgQVZEJ3Mgc2l6ZSBjYW4gZXhwbGFpbiAoZWFjaCB3YXMgZWxpbWluYXRlZCBieSBtZWFzdXJlbWVudCxcbiAqIG5vdCBieSBhcmd1bWVudCkuIEl0IGFsc28gZXhwbGFpbnMgdGhlIHR3byBkb3duc3RyZWFtIHN5bXB0b21zIHRoYXQgaGFkIGFsd2F5c1xuICogbG9va2VkIGxpa2Ugc2VwYXJhdGUgZmF1bHRzOiBgYWRiIGRldmljZXNgIGtlZXBzIHJlcG9ydGluZyBgZGV2aWNlYCBiZWNhdXNlXG4gKiBub3RoaW5nIGlzIGxlZnQgcnVubmluZyB0byB1cGRhdGUgdGhhdCBzdGF0ZSwgYW5kIHRoZSB0ZWFyZG93bidzXG4gKiBgYWRiIGVtdSBraWxsYCBoYW5ncyBmb3IgaXRzIGZ1bGwgYnVkZ2V0IGJlY2F1c2UgaXQgaXMgYXNraW5nIHRoZSB3ZWRnZWRcbiAqIGNvbnNvbGUgdG8gc2h1dCBpdHNlbGYgZG93bi5cbiAqXG4gKiBUaGUgaGFybmVzcyBhbHJlYWR5ICpvYnNlcnZlZCogdGhlIHNlY29uZCBoYWxmIG9mIHRoaXMgXHUyMDE0IHRlYXJkb3duIGxvZ3NcbiAqIGBFbXVsYXRvciBjb25zb2xlIHNodXRkb3duIGZvciBlbXVsYXRvci01NTU0IGRpZCBub3QgYW5zd2VyYCBcdTIwMTQgYnV0IG9ubHkgYWZ0ZXJcbiAqIHRoZSBydW4gaGFkIGFscmVhZHkgZmFpbGVkIHdpdGggYW4gZXJyb3IgYmxhbWluZyB0aGUgZGV2aWNlLiBUaGlzIG1vZHVsZSBtb3Zlc1xuICogdGhhdCBvYnNlcnZhdGlvbiB0byB3aGVyZSBpdCBjaGFuZ2VzIHRoZSB2ZXJkaWN0IGluc3RlYWQgb2YgYW5ub3RhdGluZyBpdC5cbiAqXG4gKiBLZXB0IHNlcGFyYXRlIGZyb20gdGhlIGludGVncmF0aW9uLW9ubHkgYHRyYW5zcG9ydC1mYWN0b3J5YCAoZXhjbHVkZWQgZnJvbVxuICogdW5pdCB0ZXN0cykgc28gdGhlIGNsYXNzaWZpY2F0aW9uIGFuZCB0aGUgbWVzc2FnZSBzdGF5IHVuaXQtdGVzdGFibGUgXHUyMDE0IHRoZVxuICogZmFjdG9yeSBpdHNlbGYgc2hlbGxzIG91dCB0byBgYWRiYC5cbiAqL1xuXG5pbXBvcnQgeyBjaGVja0lzRW11bGF0b3JEZXZpY2VJZCB9IGZyb20gJy4vYXZkLXByb2JlLXZlcmRpY3QuY2pzJztcbmltcG9ydCB7IGFwcGVuZFByb2Nlc3NPdXRwdXRUYWlsIH0gZnJvbSAnLi9wcm9jZXNzLWV4aXQtbWVzc2FnZS5janMnO1xuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBidWlsZEVtdWxhdG9yTGl2ZW5lc3NNZXNzYWdlfS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdWlsZEVtdWxhdG9yTGl2ZW5lc3NNZXNzYWdlUGFyYW1zIHtcbiAgLyoqXG4gIFRoZSBkZXZpY2UgdGhlIHJ1biB3YXMgYWJvdXQgdG8gZHJpdmUuXG4gICAqL1xuICByZWFkb25seSBkZXZpY2VJZDogc3RyaW5nO1xuXG4gIC8qKlxuICBUaGUgZW11bGF0b3IncyBjYXB0dXJlZCBzdGRvdXQrc3RkZXJyIHRhaWwsIHdoZW4gdGhpcyBydW4gc3RhcnRlZCBpdCAoZW1wdHkgd2hlbiBub25lKS5cbiAgICovXG4gIHJlYWRvbmx5IGVtdWxhdG9yT3V0cHV0OiBzdHJpbmc7XG5cbiAgLyoqXG4gIFRoZSBwZXItcHJvYmUgYnVkZ2V0LCBzbyB0aGUgbWVzc2FnZSBzdGF0ZXMgd2hhdCBcImRpZCBub3QgYW5zd2VyXCIgd2FzIG1lYXN1cmVkIGFnYWluc3QuXG4gICAqL1xuICByZWFkb25seSBwcm9iZVRpbWVvdXRJbk1pbGxpc2Vjb25kczogbnVtYmVyO1xuXG4gIC8qKlxuICBXaGljaCBmYWlsdXJlIHRoaXMgaXMuIGAnYWxpdmUnYCBpcyBub3QgYSBmYWlsdXJlIGFuZCBoYXMgbm8gbWVzc2FnZS5cbiAgICovXG4gIHJlYWRvbmx5IHZlcmRpY3Q6IEV4Y2x1ZGU8RW11bGF0b3JMaXZlbmVzc1ZlcmRpY3QsICdhbGl2ZSc+O1xufVxuXG4vKipcbiAqIFdoZXRoZXIgYSBwcm9iZSBnb3QgYW4gYW5zd2VyIGZyb20gdGhlIHRoaW5nIGl0IHdhcyBhc2tpbmcuXG4gKlxuICogVHdvLXZhbHVlZCBvbiBwdXJwb3NlLiBBbiBlYXJsaWVyIGRyYWZ0IHNwbGl0IGZhaWx1cmVzIGludG8gXCJlcnJvcmVkXCIgYW5kXG4gKiBcInRpbWVkIG91dFwiIGFuZCByZWFkIGFuIGVycm9yZWQgY29uc29sZSBhcyAqdGhlIGVtdWxhdG9yIHNwZWFraW5nLCB3aXRoIGFcbiAqIHJlZnVzYWwqIFx1MjAxNCB3aGljaCBpcyB3cm9uZywgYW5kIHdyb25nIGluIHRoZSBleGFjdCB3YXkgdGhpcyBtb2R1bGUgZXhpc3RzIHRvXG4gKiBhdm9pZC4gYGFkYiAtcyA8ZGV2aWNlPiBlbXUgPGNvbW1hbmQ+YCBpcyByb3V0ZWQgYnkgdGhlICoqYWRiIHNlcnZlcioqOiB3aGVuXG4gKiB0aGUgZGV2aWNlIGlzIGBvZmZsaW5lYCBhZGIgcmVmdXNlcyBvbiB0aGUgc3BvdCBhbmQgbmV2ZXIgcmVhY2hlcyB0aGUgY29uc29sZSxcbiAqIHNvIHRoZSBlcnJvciBpcyBhZGIncywgbm90IHRoZSBlbXVsYXRvcidzLiBNZWFzdXJlZCAyMDI2LTA5LTA1LCB3aGVyZSB0aGF0XG4gKiBkcmFmdCBsb2dnZWQgYHNoZWxsPWVycm9yZWQsIGNvbnNvbGU9ZXJyb3JlZCAtPiBndWVzdC11bnJlc3BvbnNpdmVgIGZvciBhXG4gKiBkZXZpY2Ugd2hvc2UgZW11bGF0b3IgdGhlbiBoYWQgdG8gYmUga2lsbGVkIGJ5IFBJRCBiZWNhdXNlIGBhZGIgZW11IGtpbGxgXG4gKiBjb3VsZCBub3QgcmVhY2ggaXQgZWl0aGVyLiBPbmx5IGEgKipzdWNjZXNzZnVsKiogcHJvYmUgaXMgYW4gYW5zd2VyLlxuICovXG5leHBvcnQgdHlwZSBFbXVsYXRvckxpdmVuZXNzUHJvYmVPdXRjb21lID0gJ2Fuc3dlcmVkJyB8ICduby1hbnN3ZXInO1xuXG4vKipcbiAqIFdoZXRoZXIgdGhlIGRldmljZSBpcyBzdGlsbCB1c2FibGUsIGFuZCBpZiBub3QsIHdoaWNoIGxheWVyIGZhaWxlZC5cbiAqXG4gKiAtIGAnYWxpdmUnYCBcdTIwMTQgdGhlIGd1ZXN0IGFuc3dlcmVkLiBQcm9jZWVkLlxuICogLSBgJ2RldmljZS1nb25lJ2AgXHUyMDE0IHRoZSBob3N0J3MgYWRiIG5vIGxvbmdlciBsaXN0cyBpdCBhdCBhbGwuXG4gKiAtIGAnZW11bGF0b3Itd2VkZ2VkJ2AgXHUyMDE0IG5laXRoZXIgdGhlIGd1ZXN0IG5vciB0aGUgZW11bGF0b3IncyBvd24gY29uc29sZVxuICogICBhbnN3ZXJzLiBUaGUgZW11bGF0b3IgcHJvY2VzcyBpcyBzdHVjazsgcmV0cnlpbmcgYW55dGhpbmcgYWdhaW5zdCB0aGlzXG4gKiAgIGRldmljZSBjYW5ub3Qgc3VjY2VlZC5cbiAqIC0gYCdndWVzdC11bnJlc3BvbnNpdmUnYCBcdTIwMTQgdGhlIGNvbnNvbGUgYW5zd2VycyBidXQgdGhlIGd1ZXN0IGRvZXMgbm90LCBzbyB0aGVcbiAqICAgZW11bGF0b3IgaXMgYWxpdmUgYW5kIHRoZSBndWVzdCBpcyBmcm96ZW4gb3Igc3RhcnZlZC5cbiAqL1xuZXhwb3J0IHR5cGUgRW11bGF0b3JMaXZlbmVzc1ZlcmRpY3QgPSAnYWxpdmUnIHwgJ2RldmljZS1nb25lJyB8ICdlbXVsYXRvci13ZWRnZWQnIHwgJ2d1ZXN0LXVucmVzcG9uc2l2ZSc7XG5cbi8qKlxuICogUGFyYW1ldGVycyBmb3Ige0BsaW5rIHJlc29sdmVFbXVsYXRvckxpdmVuZXNzVmVyZGljdH0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb2x2ZUVtdWxhdG9yTGl2ZW5lc3NWZXJkaWN0UGFyYW1zIHtcbiAgLyoqXG4gICAqIFdoZXRoZXIgYGFkYiAtcyA8ZGV2aWNlPiBlbXUgYXZkIHN0YXR1c2Agc3VjY2VlZGVkIFx1MjAxNCByZWFjaGluZyB0aGVcbiAgICogKiplbXVsYXRvciBjb25zb2xlKiosIHNlcnZlZCBieSB0aGUgZW11bGF0b3IgcHJvY2VzcyByYXRoZXIgdGhhbiBieSB0aGVcbiAgICogZ3Vlc3QuXG4gICAqXG4gICAqIE1lYW5pbmdmdWwgb25seSBmb3IgYW4gYGVtdWxhdG9yLTxwb3J0PmAgZGV2aWNlOiB0aGUgY29uc29sZSBmYWlscyBhZ2FpbnN0IGFcbiAgICogcGh5c2ljYWwgaGFuZHNldCBob3dldmVyIGhlYWx0aHkgaXQgaXMsIHdoaWNoIGlzIHdoeSBhIG5vbi1lbXVsYXRvciBpcyBuZXZlclxuICAgKiBjb252aWN0ZWQgb24gaXQgKHRoZSB0cmFwIGBhdmQtcHJvYmUtdmVyZGljdC50c2AgZG9jdW1lbnRzKS5cbiAgICovXG4gIHJlYWRvbmx5IGNvbnNvbGVQcm9iZTogRW11bGF0b3JMaXZlbmVzc1Byb2JlT3V0Y29tZTtcblxuICAvKipcbiAgVGhlIGRldmljZSB0aGUgcnVuIGlzIGFib3V0IHRvIGRyaXZlLlxuICAgKi9cbiAgcmVhZG9ubHkgZGV2aWNlSWQ6IHN0cmluZztcblxuICAvKipcbiAgV2hldGhlciB0aGUgaG9zdCdzIGBhZGIgZGV2aWNlc2Agc3RpbGwgbGlzdHMgdGhlIGRldmljZSBpbiBhbnkgc3RhdGUuXG4gICAqL1xuICByZWFkb25seSBpc0xpc3RlZEJ5QWRiOiBib29sZWFuO1xuXG4gIC8qKlxuICBXaGF0IGBhZGIgLXMgPGRldmljZT4gc2hlbGwgdHJ1ZWAgYW5zd2VyZWQgXHUyMDE0IHRoZSAqKmd1ZXN0KiosIHZpYSBgYWRiZGAuXG4gICAqL1xuICByZWFkb25seSBzaGVsbFByb2JlOiBFbXVsYXRvckxpdmVuZXNzUHJvYmVPdXRjb21lO1xufVxuXG4vKipcbiAqIEJ1aWxkcyB0aGUgbWVzc2FnZSBmb3IgYSBkZXZpY2UgdGhhdCBzdG9wcGVkIGFuc3dlcmluZywgbmFtaW5nIHRoZSBsYXllciB0aGF0XG4gKiBmYWlsZWQgYW5kIHRoZSBldmlkZW5jZSBmb3IgaXQuXG4gKlxuICogQHBhcmFtIHBhcmFtcyAtIFRoZSB2ZXJkaWN0LCB0aGUgZGV2aWNlLCB0aGUgcHJvYmUgYnVkZ2V0IGFuZCB0aGUgZW11bGF0b3IncyBvdXRwdXQuXG4gKiBAcmV0dXJucyBUaGUgbWVzc2FnZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkRW11bGF0b3JMaXZlbmVzc01lc3NhZ2UocGFyYW1zOiBCdWlsZEVtdWxhdG9yTGl2ZW5lc3NNZXNzYWdlUGFyYW1zKTogc3RyaW5nIHtcbiAgY29uc3QgYnVkZ2V0ID0gYCR7U3RyaW5nKHBhcmFtcy5wcm9iZVRpbWVvdXRJbk1pbGxpc2Vjb25kcyl9bXNgO1xuXG4gIC8qXG4gICAqIEEgYFJlY29yZGAga2V5ZWQgYnkgdGhlIHZlcmRpY3QgcmF0aGVyIHRoYW4gYSBgc3dpdGNoYDogZXhoYXVzdGl2ZW5lc3MgY29tZXNcbiAgICogZnJvbSB0aGUgdHlwZSwgd2l0aCBubyB1bnJlYWNoYWJsZSBgZGVmYXVsdGAgYXJtIHRvIGV4cGxhaW4gb3IgdG8gY292ZXIuXG4gICAqIFNhbWUgc2hhcGUgYXMgYHJlc29sdmVSZW1lZHlBZHZpY2VgIGluIGB3ZWRnZWQtYXBwaXVtLXNlcnZlci50c2AuXG4gICAqL1xuICBjb25zdCBkaWFnbm9zaXM6IFJlY29yZDxFeGNsdWRlPEVtdWxhdG9yTGl2ZW5lc3NWZXJkaWN0LCAnYWxpdmUnPiwgc3RyaW5nW10+ID0ge1xuICAgICdkZXZpY2UtZ29uZSc6IFtcbiAgICAgICdUaGUgaG9zdFxcJ3MgYGFkYiBkZXZpY2VzYCBubyBsb25nZXIgbGlzdHMgaXQsIHNvIGl0IGlzIG5vIGxvbmdlciBzZXJ2aW5nIGFkYiBhdCBhbGwuJyxcbiAgICAgICdFaXRoZXIgdGhlIGVtdWxhdG9yIGV4aXRlZCwgb3IgaXQgd2VkZ2VkIGJhZGx5IGVub3VnaCB0byBkcm9wIG9mZiBhZGIgXHUyMDE0IHRoZSBlbXVsYXRvciBvdXRwdXQgYmVsb3cgaXMgdGhlIGJlc3QgZXZpZGVuY2UgZm9yIHdoaWNoLicsXG4gICAgICAnQW4gZW11bGF0b3IgdGhpcyBydW4gc3RhcnRlZCBpcyByZXBsYWNlZCBhdXRvbWF0aWNhbGx5IG9uY2UsIHNvIHNlZWluZyB0aGlzIG1lYW5zIHRoZSByZXBsYWNlbWVudCBkaWQgbm90IHN1cnZpdmUgZWl0aGVyLiBDaGVjayB0aGF0IG5vdGhpbmcgZWxzZSBvbiB0aGlzIGhvc3Qgc3RvcHMgZW11bGF0b3JzLCBhbmQgdGhhdCB0aGUgZW11bGF0b3IgYnVpbGQgYW5kIHN5c3RlbSBpbWFnZSBhcmUgc291bmQuJ1xuICAgIF0sXG4gICAgJ2VtdWxhdG9yLXdlZGdlZCc6IFtcbiAgICAgIGBOZWl0aGVyIHRoZSBndWVzdCAoXFxgYWRiIC1zICR7cGFyYW1zLmRldmljZUlkfSBzaGVsbFxcYCkgbm9yIHRoZSBlbXVsYXRvcidzIG93biBjb25zb2xlIChcXGBhZGIgLXMgJHtwYXJhbXMuZGV2aWNlSWR9IGVtdSBhdmQgc3RhdHVzXFxgKSBhbnN3ZXJlZCB3aXRoaW4gJHtidWRnZXR9LmAsXG4gICAgICAnVGhlIGNvbnNvbGUgaXMgc2VydmVkIGJ5IHRoZSBlbXVsYXRvciBwcm9jZXNzLCBub3QgYnkgdGhlIGd1ZXN0LCBzbyBpdHMgc2lsZW5jZSBtZWFucyB0aGUgRU1VTEFUT1IgaXMgd2VkZ2VkIFx1MjAxNCBub3QgdGhlIGd1ZXN0LCBub3QgdGhlIEFwcGl1bSBzZXJ2ZXIsIGFuZCBub3QgdGhlIGRldmljZSBiZWluZyBhYnNlbnQuJyxcbiAgICAgICdgYWRiIGRldmljZXNgIHdpbGwgc3RpbGwgbGlzdCB0aGlzIGRldmljZSBhbmQgd2lsbCBtaXNsZWFkIHlvdTogbm90aGluZyBpcyBsZWZ0IHJ1bm5pbmcgdG8gdXBkYXRlIHRoYXQgc3RhdGUuJyxcbiAgICAgICdBIHdlZGdlZCBlbXVsYXRvciBkb2VzIG5vdCBjb21lIGJhY2ssIHNvIHJldHJ5aW5nIHRoZSBzZXNzaW9uIGFnYWluc3QgaXQgY2Fubm90IHN1Y2NlZWQuIE9uZSB0aGlzIHJ1biBzdGFydGVkIGlzIHJlcGxhY2VkIHdpdGggYSBmcmVzaCBib290IGF1dG9tYXRpY2FsbHksIHdoaWNoIG1lYW5zIHNlZWluZyB0aGlzIG1lc3NhZ2UgaXMgdGhlIHJlcGxhY2VtZW50IGZhaWxpbmcgdG9vLicsXG4gICAgICAnSWYgaXQgcmVjdXJzIGF0IHRoZSBzYW1lIHBvaW50IGluIGV2ZXJ5IHJ1biwgdGhlIGZhdWx0IGlzIGJlbG93IHRoZSBoYXJuZXNzIFx1MjAxNCBhbmQgdGhlIGZpcnN0IHRoaW5nIHRvIGNoZWNrIGlzIHdoYXQgZmlsdGVycyB0aGlzIGhvc3RcXCdzIHNvY2tldHMuIEVuZHBvaW50LXNlY3VyaXR5LCBjb250ZW50LWJsb2NraW5nIGFuZCBWUE4gcHJvZHVjdHMgaW5zdGFsbCBzb2NrZXQgZmlsdGVyIGRyaXZlcnMgaW4gdGhlIHBhdGggb2YgZXZlcnkgc29ja2V0IGNhbGwgdGhlIGVtdWxhdG9yIG1ha2VzLCB0aGUgY29uc29sZVxcJ3MgbG9jYWxob3N0IGNvbm5lY3Rpb24gaW5jbHVkZWQ7IHN0b3BwaW5nIHRoYXQgc2VydmljZSBoYXMgZml4ZWQgZXhhY3RseSB0aGlzIHNpZ25hdHVyZS4gQWZ0ZXIgdGhhdCwgdGhlIGVtdWxhdG9yIGJ1aWxkLCB0aGUgc3lzdGVtIGltYWdlIGFuZCB0aGUgaG9zdCBoeXBlcnZpc29yLidcbiAgICBdLFxuICAgICdndWVzdC11bnJlc3BvbnNpdmUnOiBbXG4gICAgICBgVGhlIGd1ZXN0IChcXGBhZGIgLXMgJHtwYXJhbXMuZGV2aWNlSWR9IHNoZWxsXFxgKSBkaWQgbm90IGFuc3dlciB3aXRoaW4gJHtidWRnZXR9LCBidXQgdGhlIGVtdWxhdG9yJ3Mgb3duIGNvbnNvbGUgZGlkLmAsXG4gICAgICAnU28gdGhlIGVtdWxhdG9yIHByb2Nlc3MgaXMgaGVhbHRoeSBhbmQgdGhlIGd1ZXN0IGlzIGZyb3plbiBvciB0b28gc3RhcnZlZCB0byBzY2hlZHVsZSBgYWRiZGAuJyxcbiAgICAgICdBIGNvbnRlbmRlZCBob3N0IGlzIHRoZSB1c3VhbCBjYXVzZTsgYGRldmljZUlkbGVUaW1lb3V0SW5NaWxsaXNlY29uZHNgIGlzIHRoZSBidWRnZXQgdGhhdCBnb3Zlcm5zIHdhaXRpbmcgb25lIG91dC4nLFxuICAgICAgJ0J1dCBpZiBpdCByZWN1cnMgYXQgdGhlIHNhbWUgcG9pbnQgaW4gZXZlcnkgcnVuLCBubyB0aW1lb3V0IHdpbGwgZml4IGl0LCBhbmQgdGhlIGhvc3QgaXMgbm90IG1lcmVseSBidXN5LiBDaGVjayB3aGF0IGZpbHRlcnMgdGhpcyBob3N0XFwncyBzb2NrZXRzIGZpcnN0IFx1MjAxNCBhIGNvbnRlbnQtYmxvY2tpbmcsIGVuZHBvaW50LXNlY3VyaXR5IG9yIFZQTiBzb2NrZXQgZmlsdGVyIGRyaXZlciBwcm9kdWNlcyB0aGlzIHZlcmRpY3QgYXMgcmVhZGlseSBhcyBpdCBwcm9kdWNlcyBhIGZ1bGx5IHdlZGdlZCBlbXVsYXRvciBcdTIwMTQgdGhlbiB0aGUgZW11bGF0b3IgYnVpbGQsIHRoZSBzeXN0ZW0gaW1hZ2UgYW5kIHRoZSBob3N0IGh5cGVydmlzb3IuJ1xuICAgIF1cbiAgfTtcblxuICBjb25zdCBsaW5lcyA9IFtcbiAgICBgRGV2aWNlICR7cGFyYW1zLmRldmljZUlkfSBzdG9wcGVkIGFuc3dlcmluZyBiZWZvcmUgdGhlIEFwcGl1bSBzZXNzaW9uIGNvdWxkIGJlIGVzdGFibGlzaGVkLmAsXG4gICAgLi4uZGlhZ25vc2lzW3BhcmFtcy52ZXJkaWN0XVxuICBdO1xuXG4gIHJldHVybiBhcHBlbmRQcm9jZXNzT3V0cHV0VGFpbChsaW5lcy5qb2luKCdcXG4nKSwgeyBvdXRwdXQ6IHBhcmFtcy5lbXVsYXRvck91dHB1dCwgb3V0cHV0TGFiZWw6ICdFbXVsYXRvciBvdXRwdXQnIH0pO1xufVxuXG4vKipcbiAqIERlY2lkZXMgd2hldGhlciB0aGUgZGV2aWNlIGlzIHN0aWxsIHVzYWJsZSwgYW5kIHdoaWNoIGxheWVyIGZhaWxlZCB3aGVuIGl0IGlzXG4gKiBub3QuXG4gKlxuICogQSBndWVzdCB0aGF0IGFuc3dlcnMgc2V0dGxlcyBpdCBvbiBpdHMgb3duIFx1MjAxNCBhIGNvbnNvbGUgcHJvYmUgaXMgb25seSBjb25zdWx0ZWRcbiAqIHdoZW4gdGhlIGd1ZXN0IGhhcyBhbHJlYWR5IGdvbmUgcXVpZXQsIHNvIGEgaGVhbHRoeSBydW4gbmV2ZXIgcGF5cyBmb3IgaXQuXG4gKlxuICogQHBhcmFtIHBhcmFtcyAtIFdoYXQgZWFjaCBwcm9iZSBhbnN3ZXJlZCwgYW5kIHdoZXRoZXIgYWRiIHN0aWxsIGxpc3RzIHRoZSBkZXZpY2UuXG4gKiBAcmV0dXJucyBUaGUgdmVyZGljdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVFbXVsYXRvckxpdmVuZXNzVmVyZGljdChwYXJhbXM6IFJlc29sdmVFbXVsYXRvckxpdmVuZXNzVmVyZGljdFBhcmFtcyk6IEVtdWxhdG9yTGl2ZW5lc3NWZXJkaWN0IHtcbiAgaWYgKHBhcmFtcy5zaGVsbFByb2JlID09PSAnYW5zd2VyZWQnKSB7XG4gICAgcmV0dXJuICdhbGl2ZSc7XG4gIH1cblxuICBpZiAoIXBhcmFtcy5pc0xpc3RlZEJ5QWRiKSB7XG4gICAgcmV0dXJuICdkZXZpY2UtZ29uZSc7XG4gIH1cblxuICAvKlxuICAgKiBgYWRiIFx1MjAyNiBlbXVgIGVycm9ycyBhZ2FpbnN0IGEgcGh5c2ljYWwgaGFuZHNldCBob3dldmVyIGhlYWx0aHkgaXQgaXMsIHNvIGl0c1xuICAgKiBzaWxlbmNlIGlzIG5vdCBldmlkZW5jZSB0aGVyZS4gT25seSBhbiBgZW11bGF0b3ItPHBvcnQ+YCBkZXZpY2UgaGFzIGFcbiAgICogY29uc29sZSB3aG9zZSBzaWxlbmNlIGNhbiBjb252aWN0IGl0IFx1MjAxNCB0aGUgc2FtZSByZXN0cmFpbnQgYGNsYXNzaWZ5QXZkUHJvYmVgXG4gICAqIHNob3dzIGluIG5ldmVyIHByb2JpbmcgYSBub24tZW11bGF0b3IgYXQgYWxsLlxuICAgKi9cbiAgaWYgKCFjaGVja0lzRW11bGF0b3JEZXZpY2VJZChwYXJhbXMuZGV2aWNlSWQpKSB7XG4gICAgcmV0dXJuICdndWVzdC11bnJlc3BvbnNpdmUnO1xuICB9XG5cbiAgcmV0dXJuIHBhcmFtcy5jb25zb2xlUHJvYmUgPT09ICdhbnN3ZXJlZCcgPyAnZ3Vlc3QtdW5yZXNwb25zaXZlJyA6ICdlbXVsYXRvci13ZWRnZWQnO1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBa0VBLCtCQUF3QztBQUN4QyxrQ0FBd0M7QUE2RmpDLFNBQVMsNkJBQTZCLFFBQW9EO0FBQy9GLFFBQU0sU0FBUyxHQUFHLE9BQU8sT0FBTywwQkFBMEIsQ0FBQztBQU8zRCxRQUFNLFlBQXlFO0FBQUEsSUFDN0UsZUFBZTtBQUFBLE1BQ2I7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLElBQ0Y7QUFBQSxJQUNBLG1CQUFtQjtBQUFBLE1BQ2pCLCtCQUErQixPQUFPLFFBQVEsc0RBQXNELE9BQU8sUUFBUSxzQ0FBc0MsTUFBTTtBQUFBLE1BQy9KO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsSUFDRjtBQUFBLElBQ0Esc0JBQXNCO0FBQUEsTUFDcEIsdUJBQXVCLE9BQU8sUUFBUSxtQ0FBbUMsTUFBTTtBQUFBLE1BQy9FO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUVBLFFBQU0sUUFBUTtBQUFBLElBQ1osVUFBVSxPQUFPLFFBQVE7QUFBQSxJQUN6QixHQUFHLFVBQVUsT0FBTyxPQUFPO0FBQUEsRUFDN0I7QUFFQSxhQUFPLHFEQUF3QixNQUFNLEtBQUssSUFBSSxHQUFHLEVBQUUsUUFBUSxPQUFPLGdCQUFnQixhQUFhLGtCQUFrQixDQUFDO0FBQ3BIO0FBWU8sU0FBUywrQkFBK0IsUUFBdUU7QUFDcEgsTUFBSSxPQUFPLGVBQWUsWUFBWTtBQUNwQyxXQUFPO0FBQUEsRUFDVDtBQUVBLE1BQUksQ0FBQyxPQUFPLGVBQWU7QUFDekIsV0FBTztBQUFBLEVBQ1Q7QUFRQSxNQUFJLEtBQUMsa0RBQXdCLE9BQU8sUUFBUSxHQUFHO0FBQzdDLFdBQU87QUFBQSxFQUNUO0FBRUEsU0FBTyxPQUFPLGlCQUFpQixhQUFhLHVCQUF1QjtBQUNyRTsiLAogICJuYW1lcyI6IFtdCn0K
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Pure classification of whether the device a run is about to drive is still
5
+ * **there** — and, when it is not, which of the two very different things went
6
+ * wrong: the guest froze, or the emulator process itself did.
7
+ *
8
+ * The readiness gates in `device-readiness.ts` answer *"is the guest ready
9
+ * yet?"*, and both are deliberately best-effort: they warn and proceed. Nothing
10
+ * asked *"is it still answering?"* at the moment the session is created, so a
11
+ * guest that stopped answering between the last gate and `remote()` was handed
12
+ * to Appium anyway. What the operator then saw was Appium's own report:
13
+ *
14
+ * ```text
15
+ * WebDriverError: Device emulator-5554 was not in the list of connected devices
16
+ * when running "http://localhost:4723/session" with method "POST"
17
+ * ```
18
+ *
19
+ * — measured on this host 2026-09-05, after 515s. That names the device, and
20
+ * `adb devices` then lists the device happily, so it sends the reader to the one
21
+ * diagnostic that cannot help. The same trap `wedged-appium-server.ts` exists to
22
+ * escape, one layer further down.
23
+ *
24
+ * ## The console is what tells the two apart
25
+ *
26
+ * `adb -s <device> emu <command>` is served by the **emulator's own console**,
27
+ * not by the guest's `adbd`. So the two probes answer different questions, and
28
+ * the pair is a diagnosis where either alone is a guess:
29
+ *
30
+ * | `adb shell` | `adb emu` | What it means |
31
+ * | --- | --- | --- |
32
+ * | succeeds | — | Alive. Proceed. |
33
+ * | fails | succeeds | The **guest** is frozen or too busy to schedule `adbd`. |
34
+ * | fails | fails | The **emulator process** is unreachable. Nothing will recover it. |
35
+ *
36
+ * Only a *successful* console probe clears the emulator — see
37
+ * {@link EmulatorLivenessProbeOutcome} for why a failed one cannot be read as
38
+ * the emulator answering.
39
+ *
40
+ * Measured 2026-09-05 across six hand-boots on this host — two AVDs and five
41
+ * argument sets — every one of which reached the bottom row 64–92s after boot:
42
+ *
43
+ * ```text
44
+ * 80s emulator console (QEMU, not the guest): HUNG
45
+ * 80s qemu-system-x86_64-headless (pid 10004): cpu=0% rss=5.82GB
46
+ * ```
47
+ *
48
+ * **0% CPU** — blocked, not spinning — and a console that cannot answer. That is
49
+ * the QEMU main loop, below anything the guest, the GPU stack, the network
50
+ * simulator or the AVD's size can explain (each was eliminated by measurement,
51
+ * not by argument). It also explains the two downstream symptoms that had always
52
+ * looked like separate faults: `adb devices` keeps reporting `device` because
53
+ * nothing is left running to update that state, and the teardown's
54
+ * `adb emu kill` hangs for its full budget because it is asking the wedged
55
+ * console to shut itself down.
56
+ *
57
+ * The harness already *observed* the second half of this — teardown logs
58
+ * `Emulator console shutdown for emulator-5554 did not answer` — but only after
59
+ * the run had already failed with an error blaming the device. This module moves
60
+ * that observation to where it changes the verdict instead of annotating it.
61
+ *
62
+ * Kept separate from the integration-only `transport-factory` (excluded from
63
+ * unit tests) so the classification and the message stay unit-testable — the
64
+ * factory itself shells out to `adb`.
65
+ */
66
+ /**
67
+ * Parameters for {@link buildEmulatorLivenessMessage}.
68
+ */
69
+ export interface BuildEmulatorLivenessMessageParams {
70
+ /**
71
+ The device the run was about to drive.
72
+ */
73
+ readonly deviceId: string;
74
+ /**
75
+ The emulator's captured stdout+stderr tail, when this run started it (empty when none).
76
+ */
77
+ readonly emulatorOutput: string;
78
+ /**
79
+ The per-probe budget, so the message states what "did not answer" was measured against.
80
+ */
81
+ readonly probeTimeoutInMilliseconds: number;
82
+ /**
83
+ Which failure this is. `'alive'` is not a failure and has no message.
84
+ */
85
+ readonly verdict: Exclude<EmulatorLivenessVerdict, 'alive'>;
86
+ }
87
+ /**
88
+ * Whether a probe got an answer from the thing it was asking.
89
+ *
90
+ * Two-valued on purpose. An earlier draft split failures into "errored" and
91
+ * "timed out" and read an errored console as *the emulator speaking, with a
92
+ * refusal* — which is wrong, and wrong in the exact way this module exists to
93
+ * avoid. `adb -s <device> emu <command>` is routed by the **adb server**: when
94
+ * the device is `offline` adb refuses on the spot and never reaches the console,
95
+ * so the error is adb's, not the emulator's. Measured 2026-09-05, where that
96
+ * draft logged `shell=errored, console=errored -> guest-unresponsive` for a
97
+ * device whose emulator then had to be killed by PID because `adb emu kill`
98
+ * could not reach it either. Only a **successful** probe is an answer.
99
+ */
100
+ export type EmulatorLivenessProbeOutcome = 'answered' | 'no-answer';
101
+ /**
102
+ * Whether the device is still usable, and if not, which layer failed.
103
+ *
104
+ * - `'alive'` — the guest answered. Proceed.
105
+ * - `'device-gone'` — the host's adb no longer lists it at all.
106
+ * - `'emulator-wedged'` — neither the guest nor the emulator's own console
107
+ * answers. The emulator process is stuck; retrying anything against this
108
+ * device cannot succeed.
109
+ * - `'guest-unresponsive'` — the console answers but the guest does not, so the
110
+ * emulator is alive and the guest is frozen or starved.
111
+ */
112
+ export type EmulatorLivenessVerdict = 'alive' | 'device-gone' | 'emulator-wedged' | 'guest-unresponsive';
113
+ /**
114
+ * Parameters for {@link resolveEmulatorLivenessVerdict}.
115
+ */
116
+ export interface ResolveEmulatorLivenessVerdictParams {
117
+ /**
118
+ * Whether `adb -s <device> emu avd status` succeeded — reaching the
119
+ * **emulator console**, served by the emulator process rather than by the
120
+ * guest.
121
+ *
122
+ * Meaningful only for an `emulator-<port>` device: the console fails against a
123
+ * physical handset however healthy it is, which is why a non-emulator is never
124
+ * convicted on it (the trap `avd-probe-verdict.ts` documents).
125
+ */
126
+ readonly consoleProbe: EmulatorLivenessProbeOutcome;
127
+ /**
128
+ The device the run is about to drive.
129
+ */
130
+ readonly deviceId: string;
131
+ /**
132
+ Whether the host's `adb devices` still lists the device in any state.
133
+ */
134
+ readonly isListedByAdb: boolean;
135
+ /**
136
+ What `adb -s <device> shell true` answered — the **guest**, via `adbd`.
137
+ */
138
+ readonly shellProbe: EmulatorLivenessProbeOutcome;
139
+ }
140
+ /**
141
+ * Builds the message for a device that stopped answering, naming the layer that
142
+ * failed and the evidence for it.
143
+ *
144
+ * @param params - The verdict, the device, the probe budget and the emulator's output.
145
+ * @returns The message.
146
+ */
147
+ export declare function buildEmulatorLivenessMessage(params: BuildEmulatorLivenessMessageParams): string;
148
+ /**
149
+ * Decides whether the device is still usable, and which layer failed when it is
150
+ * not.
151
+ *
152
+ * A guest that answers settles it on its own — a console probe is only consulted
153
+ * when the guest has already gone quiet, so a healthy run never pays for it.
154
+ *
155
+ * @param params - What each probe answered, and whether adb still lists the device.
156
+ * @returns The verdict.
157
+ */
158
+ export declare function resolveEmulatorLivenessVerdict(params: ResolveEmulatorLivenessVerdictParams): EmulatorLivenessVerdict;