Skip to content

Commit 1af1f3b

Browse files
committed
test(android): mitigate android emulator startup and orchestration stabiity flakes
1 parent 2381efb commit 1af1f3b

6 files changed

Lines changed: 421 additions & 61 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
# android-emulator-runner executes each script: line via sh -c; keep orchestration here.
3+
set -uo pipefail
4+
5+
TEST_EXIT=0
6+
ADB="${ANDROID_HOME}/platform-tools/adb"
7+
8+
cleanup_android_e2e() {
9+
pkill -f resource-monitor-android.sh || true
10+
pkill -f "adb logcat" || true
11+
"$ADB" -s emulator-5554 emu kill 2>/dev/null || true
12+
sleep "${ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL:-5}"
13+
if pgrep -f qemu-system-x86_64-headless >/dev/null 2>&1; then
14+
echo "Emulator did not exit gracefully; force-killing qemu"
15+
pkill -9 -f qemu-system-x86_64-headless || true
16+
fi
17+
}
18+
19+
trap cleanup_android_e2e EXIT
20+
21+
"$ADB" devices
22+
nohup sh -c "$ADB logcat '*:D' > adb-log.txt" &
23+
chmod +x ./.github/workflows/scripts/resource-monitor-android.sh
24+
nohup ./.github/workflows/scripts/resource-monitor-android.sh &
25+
26+
yarn tests:android:test-cover --headless || TEST_EXIT=$?
27+
28+
cleanup_android_e2e
29+
trap - EXIT
30+
31+
yarn tests:android:post-e2e-coverage || true
32+
exit "$TEST_EXIT"

.github/workflows/tests_e2e_android.yml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -260,20 +260,14 @@ jobs:
260260
timeout-minutes: 45
261261
env:
262262
ANDROID_AVD_HOME: /mnt/avd
263+
ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL: 5
263264
with:
264265
api-level: ${{ matrix.api-level }}
265266
avd-name: TestingAVD
266267
target: ${{ matrix.target }}
267268
disable-spellchecker: true
268269
arch: ${{ matrix.arch }}
269-
script: |
270-
$ANDROID_HOME/platform-tools/adb devices
271-
nohup sh -c "$ANDROID_HOME/platform-tools/adb logcat '*:D' > adb-log.txt" &
272-
chmod +x ./.github/workflows/scripts/resource-monitor-android.sh
273-
nohup ./.github/workflows/scripts/resource-monitor-android.sh &
274-
yarn tests:android:test-cover --headless
275-
pkill -f resource-monitor-android.sh || true
276-
yarn tests:android:post-e2e-coverage
270+
script: bash ./.github/workflows/scripts/android-e2e-detox.sh
277271

278272
# https://github.com/codecov/codecov-action/releases
279273
- name: Upload Codecov e2e TS (Android)

.yarn/patches/mocha-remote-client-npm-1.13.2-a2e7596aba.patch

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
diff --git a/dist/browser.bundle.mjs b/dist/browser.bundle.mjs
2-
index eb7d0bd7a2f43daa9124a2171dc708db76e8882e..083fbb440bc9e8494444d829bc410ecef984f695 100644
2+
index eb7d0bd7a2f43daa9124a2171dc708db76e8882e..20bc994ae6f7f5897882ae2730bd9ea46a865543 100644
33
--- a/dist/browser.bundle.mjs
44
+++ b/dist/browser.bundle.mjs
5-
@@ -11324,6 +11324,7 @@ class Client extends ClientEventEmitter {
5+
@@ -11284,6 +11284,7 @@ class Client extends ClientEventEmitter {
6+
_state = ClientState.DISCONNECTED;
7+
ws;
8+
reconnectTimeout;
9+
+ _outboundQueue = [];
10+
constructor(config = {}, debug = Client.nextDebug()) {
11+
super(Client.EventEmitter, debug.extend("events"));
12+
this.debug = debug;
13+
@@ -11324,6 +11325,8 @@ class Client extends ClientEventEmitter {
614
// No need to handle connection error now that we're connected
715
ws.removeEventListener("error", handleConnectionError);
816
this.emit("connection", ws);
917
+ this.startKeepalive();
18+
+ this.flushOutboundQueue();
1019
resolve();
1120
});
1221
if (!this.config.autoReconnect) {
13-
@@ -11517,6 +11518,7 @@ class Client extends ClientEventEmitter {
22+
@@ -11517,6 +11520,7 @@ class Client extends ClientEventEmitter {
1423
}
1524
}
1625
handleClose = ({ code, reason }) => {
1726
+ this.stopKeepalive();
1827
this.debug(`Connection closed: ${reason || "No reason"} (code=${code})`);
1928
// Forget about the socket
2029
delete this.ws;
21-
@@ -11563,6 +11565,20 @@ class Client extends ClientEventEmitter {
30+
@@ -11563,6 +11567,20 @@ class Client extends ClientEventEmitter {
2231
}
2332
});
2433
}
@@ -39,16 +48,23 @@ index eb7d0bd7a2f43daa9124a2171dc708db76e8882e..083fbb440bc9e8494444d829bc410ece
3948
else if (msg.action === "error") {
4049
if (typeof msg.message === "string") {
4150
this.emit("error", new Error(msg.message));
42-
@@ -11606,6 +11622,8 @@ class Client extends ClientEventEmitter {
51+
@@ -11605,7 +11623,15 @@ class Client extends ClientEventEmitter {
52+
const data = serialize(msg);
4353
this.ws.send(data);
4454
}
55+
+ else if (this.config.autoReconnect && this._state !== ClientState.DISCONNECTED && throwOnClosed) {
56+
+ const readyState = this.ws?.readyState ?? "no-socket";
57+
+ const data = serialize(msg);
58+
+ this._outboundQueue.push(data);
59+
+ console.warn(`[mocha-remote-ws] send queued action=${msg.action} readyState=${readyState} queue=${this._outboundQueue.length}`);
60+
+ }
4561
else if (throwOnClosed) {
4662
+ const readyState = this.ws?.readyState ?? "no-socket";
4763
+ console.warn(`[mocha-remote-ws] send failed action=${msg.action} readyState=${readyState}`);
4864
throw new SendMessageFailure(msg, "WebSocket is closed");
4965
}
5066
}
51-
@@ -11645,6 +11663,77 @@ class Client extends ClientEventEmitter {
67+
@@ -11645,6 +11671,88 @@ class Client extends ClientEventEmitter {
5268
},
5369
};
5470
}
@@ -122,31 +138,51 @@ index eb7d0bd7a2f43daa9124a2171dc708db76e8882e..083fbb440bc9e8494444d829bc410ece
122138
+ this._keepaliveTimer = null;
123139
+ }
124140
+ }
141+
+ flushOutboundQueue() {
142+
+ if (!this._outboundQueue.length || !this.ws || this.ws.readyState !== Client.WebSocket.OPEN) {
143+
+ return;
144+
+ }
145+
+ const queue = this._outboundQueue;
146+
+ this._outboundQueue = [];
147+
+ console.warn(`[mocha-remote-ws] flushing outbound queue count=${queue.length}`);
148+
+ for (const data of queue) {
149+
+ this.ws.send(data);
150+
+ }
151+
+ }
125152
+
126153
}
127154

128155
Client.WebSocket = WebSocket;
129156
diff --git a/dist/node.bundle.cjs b/dist/node.bundle.cjs
130-
index a692e25a30f24e8299661c190ec09c3be5e5e2da..08fba4ed4909812c96ddc23e0a63b3a11f3388ac 100644
157+
index a692e25a30f24e8299661c190ec09c3be5e5e2da..4fff06db5f8d8c679337a689fe54157f169bec32 100644
131158
--- a/dist/node.bundle.cjs
132159
+++ b/dist/node.bundle.cjs
133-
@@ -5661,6 +5661,7 @@ class Client extends ClientEventEmitter {
160+
@@ -5621,6 +5621,7 @@ class Client extends ClientEventEmitter {
161+
_state = exports.ClientState.DISCONNECTED;
162+
ws;
163+
reconnectTimeout;
164+
+ _outboundQueue = [];
165+
constructor(config = {}, debug = Client.nextDebug()) {
166+
super(Client.EventEmitter, debug.extend("events"));
167+
this.debug = debug;
168+
@@ -5661,6 +5662,8 @@ class Client extends ClientEventEmitter {
134169
// No need to handle connection error now that we're connected
135170
ws.removeEventListener("error", handleConnectionError);
136171
this.emit("connection", ws);
137172
+ this.startKeepalive();
173+
+ this.flushOutboundQueue();
138174
resolve();
139175
});
140176
if (!this.config.autoReconnect) {
141-
@@ -5854,6 +5855,7 @@ class Client extends ClientEventEmitter {
177+
@@ -5854,6 +5857,7 @@ class Client extends ClientEventEmitter {
142178
}
143179
}
144180
handleClose = ({ code, reason }) => {
145181
+ this.stopKeepalive();
146182
this.debug(`Connection closed: ${reason || "No reason"} (code=${code})`);
147183
// Forget about the socket
148184
delete this.ws;
149-
@@ -5900,6 +5902,20 @@ class Client extends ClientEventEmitter {
185+
@@ -5900,6 +5904,20 @@ class Client extends ClientEventEmitter {
150186
}
151187
});
152188
}
@@ -167,16 +203,23 @@ index a692e25a30f24e8299661c190ec09c3be5e5e2da..08fba4ed4909812c96ddc23e0a63b3a1
167203
else if (msg.action === "error") {
168204
if (typeof msg.message === "string") {
169205
this.emit("error", new Error(msg.message));
170-
@@ -5943,6 +5959,8 @@ class Client extends ClientEventEmitter {
206+
@@ -5942,7 +5960,15 @@ class Client extends ClientEventEmitter {
207+
const data = serialize(msg);
171208
this.ws.send(data);
172209
}
210+
+ else if (this.config.autoReconnect && this._state !== ClientState.DISCONNECTED && throwOnClosed) {
211+
+ const readyState = this.ws?.readyState ?? "no-socket";
212+
+ const data = serialize(msg);
213+
+ this._outboundQueue.push(data);
214+
+ console.warn(`[mocha-remote-ws] send queued action=${msg.action} readyState=${readyState} queue=${this._outboundQueue.length}`);
215+
+ }
173216
else if (throwOnClosed) {
174217
+ const readyState = this.ws?.readyState ?? "no-socket";
175218
+ console.warn(`[mocha-remote-ws] send failed action=${msg.action} readyState=${readyState}`);
176219
throw new SendMessageFailure(msg, "WebSocket is closed");
177220
}
178221
}
179-
@@ -5982,6 +6000,77 @@ class Client extends ClientEventEmitter {
222+
@@ -5982,6 +6008,88 @@ class Client extends ClientEventEmitter {
180223
},
181224
};
182225
}
@@ -250,31 +293,51 @@ index a692e25a30f24e8299661c190ec09c3be5e5e2da..08fba4ed4909812c96ddc23e0a63b3a1
250293
+ this._keepaliveTimer = null;
251294
+ }
252295
+ }
296+
+ flushOutboundQueue() {
297+
+ if (!this._outboundQueue.length || !this.ws || this.ws.readyState !== Client.WebSocket.OPEN) {
298+
+ return;
299+
+ }
300+
+ const queue = this._outboundQueue;
301+
+ this._outboundQueue = [];
302+
+ console.warn(`[mocha-remote-ws] flushing outbound queue count=${queue.length}`);
303+
+ for (const data of queue) {
304+
+ this.ws.send(data);
305+
+ }
306+
+ }
253307
+
254308
}
255309

256310
Client.WebSocket = WebSocket;
257311
diff --git a/dist/node.bundle.mjs b/dist/node.bundle.mjs
258-
index 77d3d8299505196a826971ba46e0ff14b75c3d0a..373b3a7641a15d4cef6ce2c5b7e29cd1716c5a6b 100644
312+
index 77d3d8299505196a826971ba46e0ff14b75c3d0a..f0983009d144febeb4dfb2c5cb7c8cfa29789981 100644
259313
--- a/dist/node.bundle.mjs
260314
+++ b/dist/node.bundle.mjs
261-
@@ -5640,6 +5640,7 @@ class Client extends ClientEventEmitter {
315+
@@ -5600,6 +5600,7 @@ class Client extends ClientEventEmitter {
316+
_state = ClientState.DISCONNECTED;
317+
ws;
318+
reconnectTimeout;
319+
+ _outboundQueue = [];
320+
constructor(config = {}, debug = Client.nextDebug()) {
321+
super(Client.EventEmitter, debug.extend("events"));
322+
this.debug = debug;
323+
@@ -5640,6 +5641,8 @@ class Client extends ClientEventEmitter {
262324
// No need to handle connection error now that we're connected
263325
ws.removeEventListener("error", handleConnectionError);
264326
this.emit("connection", ws);
265327
+ this.startKeepalive();
328+
+ this.flushOutboundQueue();
266329
resolve();
267330
});
268331
if (!this.config.autoReconnect) {
269-
@@ -5833,6 +5834,7 @@ class Client extends ClientEventEmitter {
332+
@@ -5833,6 +5836,7 @@ class Client extends ClientEventEmitter {
270333
}
271334
}
272335
handleClose = ({ code, reason }) => {
273336
+ this.stopKeepalive();
274337
this.debug(`Connection closed: ${reason || "No reason"} (code=${code})`);
275338
// Forget about the socket
276339
delete this.ws;
277-
@@ -5879,6 +5881,20 @@ class Client extends ClientEventEmitter {
340+
@@ -5879,6 +5883,20 @@ class Client extends ClientEventEmitter {
278341
}
279342
});
280343
}
@@ -295,16 +358,23 @@ index 77d3d8299505196a826971ba46e0ff14b75c3d0a..373b3a7641a15d4cef6ce2c5b7e29cd1
295358
else if (msg.action === "error") {
296359
if (typeof msg.message === "string") {
297360
this.emit("error", new Error(msg.message));
298-
@@ -5922,6 +5938,8 @@ class Client extends ClientEventEmitter {
361+
@@ -5921,7 +5939,15 @@ class Client extends ClientEventEmitter {
362+
const data = serialize(msg);
299363
this.ws.send(data);
300364
}
365+
+ else if (this.config.autoReconnect && this._state !== ClientState.DISCONNECTED && throwOnClosed) {
366+
+ const readyState = this.ws?.readyState ?? "no-socket";
367+
+ const data = serialize(msg);
368+
+ this._outboundQueue.push(data);
369+
+ console.warn(`[mocha-remote-ws] send queued action=${msg.action} readyState=${readyState} queue=${this._outboundQueue.length}`);
370+
+ }
301371
else if (throwOnClosed) {
302372
+ const readyState = this.ws?.readyState ?? "no-socket";
303373
+ console.warn(`[mocha-remote-ws] send failed action=${msg.action} readyState=${readyState}`);
304374
throw new SendMessageFailure(msg, "WebSocket is closed");
305375
}
306376
}
307-
@@ -5961,6 +5979,77 @@ class Client extends ClientEventEmitter {
377+
@@ -5961,6 +5987,88 @@ class Client extends ClientEventEmitter {
308378
},
309379
};
310380
}
@@ -378,6 +448,17 @@ index 77d3d8299505196a826971ba46e0ff14b75c3d0a..373b3a7641a15d4cef6ce2c5b7e29cd1
378448
+ this._keepaliveTimer = null;
379449
+ }
380450
+ }
451+
+ flushOutboundQueue() {
452+
+ if (!this._outboundQueue.length || !this.ws || this.ws.readyState !== Client.WebSocket.OPEN) {
453+
+ return;
454+
+ }
455+
+ const queue = this._outboundQueue;
456+
+ this._outboundQueue = [];
457+
+ console.warn(`[mocha-remote-ws] flushing outbound queue count=${queue.length}`);
458+
+ for (const data of queue) {
459+
+ this.ws.send(data);
460+
+ }
461+
+ }
381462
+
382463
}
383464

0 commit comments

Comments
 (0)