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.
- package/dist/lib/cjs/adb-device-list.cjs +54 -0
- package/dist/lib/cjs/adb-device-list.d.cts +72 -0
- package/dist/lib/cjs/android-sdk.cjs +52 -0
- package/dist/lib/cjs/android-sdk.d.cts +29 -0
- package/dist/lib/cjs/app-startup-progress.cjs +87 -0
- package/dist/lib/cjs/app-startup-progress.d.cts +128 -0
- package/dist/lib/cjs/appium-server-marker.cjs +29 -32
- package/dist/lib/cjs/appium-server-marker.d.cts +22 -0
- package/dist/lib/cjs/avd-probe-verdict.cjs +90 -0
- package/dist/lib/cjs/avd-probe-verdict.d.cts +175 -0
- package/dist/lib/cjs/community-plugin-registry.cjs +1 -1
- package/dist/lib/cjs/community-plugin-registry.d.cts +1 -1
- package/dist/lib/cjs/config-directory-fallback-error.cjs +57 -0
- package/dist/lib/cjs/config-directory-fallback-error.d.cts +64 -0
- package/dist/lib/cjs/config-directory.cjs +46 -0
- package/dist/lib/cjs/config-directory.d.cts +45 -0
- package/dist/lib/cjs/connect-to-cdp.cjs +13 -7
- package/dist/lib/cjs/connect-to-cdp.d.cts +19 -0
- package/dist/lib/cjs/demo-vault-bootstrap.cjs +1 -1
- package/dist/lib/cjs/demo-vault-bootstrap.d.cts +3 -2
- package/dist/lib/cjs/demo-vault-populate.cjs +3 -2
- package/dist/lib/cjs/device-readiness.cjs +38 -3
- package/dist/lib/cjs/device-readiness.d.cts +83 -9
- package/dist/lib/cjs/emulator-arguments.cjs +6 -2
- package/dist/lib/cjs/emulator-arguments.d.cts +26 -0
- package/dist/lib/cjs/emulator-backend.cjs +71 -0
- package/dist/lib/cjs/emulator-backend.d.cts +76 -0
- package/dist/lib/cjs/emulator-liveness.cjs +72 -0
- package/dist/lib/cjs/emulator-liveness.d.cts +158 -0
- package/dist/lib/cjs/emulator-snapshot.cjs +57 -0
- package/dist/lib/cjs/emulator-snapshot.d.cts +68 -0
- package/dist/lib/cjs/global-setup-core.cjs +30 -30
- package/dist/lib/cjs/global-setup-core.d.cts +27 -2
- package/dist/lib/cjs/headless-vault-config.cjs +51 -0
- package/dist/lib/cjs/headless-vault-config.d.cts +67 -0
- package/dist/lib/cjs/index.cjs +12 -1
- package/dist/lib/cjs/index.d.cts +6 -1
- package/dist/lib/cjs/library.cjs +1 -1
- package/dist/lib/cjs/mobile-input.cjs +14 -2
- package/dist/lib/cjs/mobile-input.d.cts +5 -9
- package/dist/lib/cjs/namespace-bootstrap.cjs +1 -1
- package/dist/lib/cjs/obsidian-instance.cjs +52 -3
- package/dist/lib/cjs/obsidian-instance.d.cts +12 -0
- package/dist/lib/cjs/open-obsidian-settings-tab.cjs +1 -1
- package/dist/lib/cjs/open-obsidian-settings-tab.d.cts +3 -2
- package/dist/lib/cjs/owned-instance-exit-marker.cjs +99 -0
- package/dist/lib/cjs/owned-instance-exit-marker.d.cts +106 -0
- package/dist/lib/cjs/owned-instance-exited-error.cjs +106 -0
- package/dist/lib/cjs/owned-instance-exited-error.d.cts +98 -0
- package/dist/lib/cjs/owned-vault-seed.cjs +9 -5
- package/dist/lib/cjs/owned-vault-seed.d.cts +24 -3
- package/dist/lib/cjs/parent-liveness.cjs +42 -5
- package/dist/lib/cjs/parent-liveness.d.cts +37 -5
- package/dist/lib/cjs/port-owner.cjs +61 -0
- package/dist/lib/cjs/port-owner.d.cts +47 -0
- package/dist/lib/cjs/process-capture.cjs +62 -0
- package/dist/lib/cjs/process-capture.d.cts +83 -0
- package/dist/lib/cjs/process-exit-message.cjs +14 -8
- package/dist/lib/cjs/process-exit-message.d.cts +37 -3
- package/dist/lib/cjs/process-liveness.cjs +51 -0
- package/dist/lib/cjs/process-liveness.d.cts +18 -0
- package/dist/lib/cjs/run-version-matrix.cjs +1 -1
- package/dist/lib/cjs/run-version-matrix.d.cts +1 -1
- package/dist/lib/cjs/teardown-verdict.cjs +53 -0
- package/dist/lib/cjs/teardown-verdict.d.cts +69 -0
- package/dist/lib/cjs/temporary-vault.cjs +43 -8
- package/dist/lib/cjs/temporary-vault.d.cts +47 -5
- package/dist/lib/cjs/transport-appium.cjs +102 -17
- package/dist/lib/cjs/transport-appium.d.cts +53 -3
- package/dist/lib/cjs/transport-desktop-cdp.cjs +195 -6
- package/dist/lib/cjs/transport-desktop-cdp.d.cts +99 -0
- package/dist/lib/cjs/transport-factory.cjs +951 -133
- package/dist/lib/cjs/transport-options.cjs +11 -3
- package/dist/lib/cjs/transport-options.d.cts +131 -12
- package/dist/lib/cjs/transport.cjs +1 -1
- package/dist/lib/cjs/transport.d.cts +7 -1
- package/dist/lib/cjs/vault-plugin-install.cjs +40 -0
- package/dist/lib/cjs/vault-plugin-install.d.cts +64 -0
- package/dist/lib/cjs/vault-teardown-verdict.cjs +42 -0
- package/dist/lib/cjs/vault-teardown-verdict.d.cts +57 -0
- package/dist/lib/cjs/version-matrix.cjs +1 -1
- package/dist/lib/cjs/version-matrix.d.cts +2 -2
- package/dist/lib/cjs/webview-cdp.cjs +6 -7
- package/dist/lib/cjs/webview-cdp.d.cts +5 -1
- package/dist/lib/esm/adb-device-list.d.mts +72 -0
- package/dist/lib/esm/adb-device-list.mjs +28 -0
- package/dist/lib/esm/android-sdk.d.mts +29 -0
- package/dist/lib/esm/android-sdk.mjs +18 -0
- package/dist/lib/esm/app-startup-progress.d.mts +128 -0
- package/dist/lib/esm/app-startup-progress.mjs +59 -0
- package/dist/lib/esm/appium-server-marker.d.mts +22 -0
- package/dist/lib/esm/appium-server-marker.mjs +28 -22
- package/dist/lib/esm/avd-probe-verdict.d.mts +175 -0
- package/dist/lib/esm/avd-probe-verdict.mjs +62 -0
- package/dist/lib/esm/community-plugin-registry.d.mts +1 -1
- package/dist/lib/esm/community-plugin-registry.mjs +1 -1
- package/dist/lib/esm/config-directory-fallback-error.d.mts +64 -0
- package/dist/lib/esm/config-directory-fallback-error.mjs +33 -0
- package/dist/lib/esm/config-directory.d.mts +45 -0
- package/dist/lib/esm/config-directory.mjs +21 -0
- package/dist/lib/esm/connect-to-cdp.d.mts +19 -0
- package/dist/lib/esm/connect-to-cdp.mjs +13 -7
- package/dist/lib/esm/demo-vault-bootstrap.d.mts +3 -2
- package/dist/lib/esm/demo-vault-bootstrap.mjs +1 -1
- package/dist/lib/esm/demo-vault-populate.mjs +3 -2
- package/dist/lib/esm/device-readiness.d.mts +83 -9
- package/dist/lib/esm/device-readiness.mjs +34 -2
- package/dist/lib/esm/emulator-arguments.d.mts +26 -0
- package/dist/lib/esm/emulator-arguments.mjs +6 -2
- package/dist/lib/esm/emulator-backend.d.mts +76 -0
- package/dist/lib/esm/emulator-backend.mjs +45 -0
- package/dist/lib/esm/emulator-liveness.d.mts +158 -0
- package/dist/lib/esm/emulator-liveness.mjs +47 -0
- package/dist/lib/esm/emulator-snapshot.d.mts +68 -0
- package/dist/lib/esm/emulator-snapshot.mjs +32 -0
- package/dist/lib/esm/global-setup-core.d.mts +27 -2
- package/dist/lib/esm/global-setup-core.mjs +34 -35
- package/dist/lib/esm/headless-vault-config.d.mts +67 -0
- package/dist/lib/esm/headless-vault-config.mjs +31 -0
- package/dist/lib/esm/index.d.mts +6 -1
- package/dist/lib/esm/index.mjs +11 -1
- package/dist/lib/esm/library.mjs +1 -1
- package/dist/lib/esm/mobile-input.d.mts +5 -9
- package/dist/lib/esm/mobile-input.mjs +14 -2
- package/dist/lib/esm/namespace-bootstrap.mjs +1 -1
- package/dist/lib/esm/obsidian-instance.d.mts +12 -0
- package/dist/lib/esm/obsidian-instance.mjs +55 -3
- package/dist/lib/esm/open-obsidian-settings-tab.d.mts +3 -2
- package/dist/lib/esm/open-obsidian-settings-tab.mjs +1 -1
- package/dist/lib/esm/owned-instance-exit-marker.d.mts +106 -0
- package/dist/lib/esm/owned-instance-exit-marker.mjs +78 -0
- package/dist/lib/esm/owned-instance-exited-error.d.mts +98 -0
- package/dist/lib/esm/owned-instance-exited-error.mjs +81 -0
- package/dist/lib/esm/owned-vault-seed.d.mts +24 -3
- package/dist/lib/esm/owned-vault-seed.mjs +9 -5
- package/dist/lib/esm/parent-liveness.d.mts +37 -5
- package/dist/lib/esm/parent-liveness.mjs +42 -5
- package/dist/lib/esm/port-owner.d.mts +47 -0
- package/dist/lib/esm/port-owner.mjs +36 -0
- package/dist/lib/esm/process-capture.d.mts +83 -0
- package/dist/lib/esm/process-capture.mjs +38 -0
- package/dist/lib/esm/process-exit-message.d.mts +37 -3
- package/dist/lib/esm/process-exit-message.mjs +13 -8
- package/dist/lib/esm/process-liveness.d.mts +18 -0
- package/dist/lib/esm/process-liveness.mjs +17 -0
- package/dist/lib/esm/run-version-matrix.d.mts +1 -1
- package/dist/lib/esm/run-version-matrix.mjs +1 -1
- package/dist/lib/esm/teardown-verdict.d.mts +69 -0
- package/dist/lib/esm/teardown-verdict.mjs +28 -0
- package/dist/lib/esm/temporary-vault.d.mts +47 -5
- package/dist/lib/esm/temporary-vault.mjs +43 -8
- package/dist/lib/esm/transport-appium.d.mts +53 -3
- package/dist/lib/esm/transport-appium.mjs +108 -17
- package/dist/lib/esm/transport-desktop-cdp.d.mts +99 -0
- package/dist/lib/esm/transport-desktop-cdp.mjs +202 -7
- package/dist/lib/esm/transport-factory.mjs +995 -138
- package/dist/lib/esm/transport-options.d.mts +131 -12
- package/dist/lib/esm/transport-options.mjs +9 -2
- package/dist/lib/esm/transport.d.mts +7 -1
- package/dist/lib/esm/vault-plugin-install.d.mts +64 -0
- package/dist/lib/esm/vault-plugin-install.mjs +20 -0
- package/dist/lib/esm/vault-teardown-verdict.d.mts +57 -0
- package/dist/lib/esm/vault-teardown-verdict.mjs +18 -0
- package/dist/lib/esm/version-matrix.d.mts +2 -2
- package/dist/lib/esm/version-matrix.mjs +1 -1
- package/dist/lib/esm/webview-cdp.d.mts +5 -1
- package/dist/lib/esm/webview-cdp.mjs +6 -7
- package/package.json +24 -23
- package/dist/lib/cjs/native-dialog-monitor.cjs +0 -192
- package/dist/lib/cjs/native-dialog-monitor.d.cts +0 -66
- package/dist/lib/cjs/obsidian-namespace.cjs +0 -17
- package/dist/lib/cjs/obsidian-namespace.d.cts +0 -128
- package/dist/lib/esm/native-dialog-monitor.d.mts +0 -66
- package/dist/lib/esm/native-dialog-monitor.mjs +0 -158
- package/dist/lib/esm/obsidian-namespace.d.mts +0 -128
- 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
|
|
5
|
-
* enough to establish an Appium session, plus resolving the
|
|
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
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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
|
|
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,
|
|
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;
|