test(e2e): preserve remote agent diagnostics (#1457)

* test(e2e): lower s3 wake version gate

* test(e2e): preserve device logs across restarts

Capture failing device logs before later restarts can truncate last.log, and keep a rotated copy available during global teardown.
This commit is contained in:
Adam Shiervani
2026-05-19 10:34:32 +02:00
committed by GitHub
parent 6419d049a2
commit 5427b80248
3 changed files with 30 additions and 3 deletions
+4
View File
@@ -21,6 +21,10 @@ export default async function globalTeardown() {
const logs: Record<string, string> = {
"device-last.log": "cat /userdata/jetkvm/last.log",
// Rotated by restartAppViaSSH — preserves the failing session's output
// when a later test restarts the app before teardown captures logs.
// sshExec(_, true) returns "" if the file is missing, so no shell guard needed.
"device-prev.log": "cat /userdata/jetkvm/last.log.prev",
"device-config.json": "cat /userdata/kvm_config.json",
"device-dmesg.txt": "dmesg | tail -200",
};
+5 -1
View File
@@ -750,8 +750,12 @@ export async function restoreSSHDevState(state: SSHDevState): Promise<void> {
export async function restartAppViaSSH(): Promise<void> {
await sshExec("killall jetkvm_app", true);
await new Promise(r => setTimeout(r, 500));
// Rotate last.log into last.log.prev before respawning so a later teardown
// can still recover the previous session's output if a subsequent restart
// truncates the live log. Combined into one SSH call to save a round-trip.
await sshExec(
"setsid env LD_LIBRARY_PATH=/oem/usr/lib:/oem/lib /userdata/jetkvm/bin/jetkvm_app > /userdata/jetkvm/last.log 2>&1 &",
"[ -s /userdata/jetkvm/last.log ] && mv /userdata/jetkvm/last.log /userdata/jetkvm/last.log.prev; " +
"setsid env LD_LIBRARY_PATH=/oem/usr/lib:/oem/lib /userdata/jetkvm/bin/jetkvm_app > /userdata/jetkvm/last.log 2>&1 &",
true,
);
await new Promise(r => setTimeout(r, 1000));
+21 -2
View File
@@ -489,6 +489,24 @@ test.afterAll(async () => {
if (sharedPage) await sharedPage.close();
});
// Snapshot /userdata/jetkvm/last.log into the failing test's output dir before
// any subsequent test reboots the device (RkLunch's `> last.log` at boot wipes
// the log, and /oem is read-only so we can't change that). This makes the
// capture race-free regardless of what later tests do.
// Empty fixture destructure is required by Playwright; `_` would fail the
// runtime "destructuring pattern" check.
// oxlint-disable-next-line no-empty-pattern
test.afterEach(async ({}, testInfo) => {
if (!agent) return;
if (testInfo.status === testInfo.expectedStatus) return;
const log = await sshExec("cat /userdata/jetkvm/last.log", true);
try {
await testInfo.attach("device-last.log", { body: log, contentType: "text/plain" });
} catch {
// attach can throw if the worker is already tearing down; sshExec(_, true) won't.
}
});
test.describe("Remote Host Agent", () => {
// ═══════════════════════════════════════════
// KEYBOARD: TOGGLE KEYS + LED ROUND-TRIP
@@ -2115,8 +2133,9 @@ test.describe("Remote Host Agent", () => {
const postMountEvents = await waitForKeyboardReady(agent!, sharedPage);
expect(postMountEvents.length, "keyboard should work after disk mount").toBeGreaterThan(0);
// Unmount
await callJsonRpc(sharedPage, "unmountImage");
// Unmount — Disk-mode unmount can hit the EBUSY rebind path plus NBD
// disconnect drain, which routinely runs past the default 10s RPC timeout.
await callJsonRpc(sharedPage, "unmountImage", {}, 30_000);
const stateEnd = (await callJsonRpc(sharedPage, "getVirtualMediaState")) as null | object;
expect(stateEnd).toBeNull();