summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/harness
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/harness')
-rw-r--r--packages/meshbay-hub/tests/harness/chat_scroll_probe.py11
-rw-r--r--packages/meshbay-hub/tests/harness/chat_send_probe.py11
-rw-r--r--packages/meshbay-hub/tests/harness/group_tab_probe.py11
-rw-r--r--packages/meshbay-hub/tests/harness/layout_probe.py12
-rw-r--r--packages/meshbay-hub/tests/harness/scroll_probe.py10
-rw-r--r--packages/meshbay-hub/tests/harness/upload_seal_probe.mjs25
6 files changed, 73 insertions, 7 deletions
diff --git a/packages/meshbay-hub/tests/harness/chat_scroll_probe.py b/packages/meshbay-hub/tests/harness/chat_scroll_probe.py
index 7373976..17ec554 100644
--- a/packages/meshbay-hub/tests/harness/chat_scroll_probe.py
+++ b/packages/meshbay-hub/tests/harness/chat_scroll_probe.py
@@ -193,7 +193,15 @@ class H(http.server.BaseHTTPRequestHandler):
def main() -> int:
with socketserver.TCPServer(("127.0.0.1", PORT), H) as srv:
threading.Thread(target=srv.serve_forever, daemon=True).start()
- with tempfile.TemporaryDirectory() as profile:
+ # ignore_cleanup_errors: Chrome's children (zygote, renderer, gpu)
+ # outlive terminate() on the parent by a moment and go on writing into
+ # the profile. rmtree then walks a directory that gains a file between
+ # its readdir and its rmdir and raises "Directory not empty" -- which
+ # failed the probe, which failed every test in the file, intermittently
+ # and for a reason nowhere near the chat code they were testing. A few
+ # bytes left in a throwaway profile are harmless; failing the run is not.
+ with tempfile.TemporaryDirectory(
+ ignore_cleanup_errors=True) as profile:
# Real time, not `--virtual-time-budget`: the defect is a feedback
# loop between layout and an event, and a virtual clock does not
# run it.
@@ -211,6 +219,7 @@ def main() -> int:
proc.wait(timeout=10)
except subprocess.TimeoutExpired:
proc.kill()
+ proc.wait()
if not RECORDS:
print(json.dumps({"error": "no measurement"}), file=sys.stderr)
return 1
diff --git a/packages/meshbay-hub/tests/harness/chat_send_probe.py b/packages/meshbay-hub/tests/harness/chat_send_probe.py
index 5f99beb..28635b0 100644
--- a/packages/meshbay-hub/tests/harness/chat_send_probe.py
+++ b/packages/meshbay-hub/tests/harness/chat_send_probe.py
@@ -297,7 +297,15 @@ class H(http.server.BaseHTTPRequestHandler):
def main() -> int:
with socketserver.TCPServer(("127.0.0.1", PORT), H) as srv:
threading.Thread(target=srv.serve_forever, daemon=True).start()
- with tempfile.TemporaryDirectory() as profile:
+ # ignore_cleanup_errors: Chrome's children (zygote, renderer, gpu)
+ # outlive terminate() on the parent by a moment and go on writing into
+ # the profile. rmtree then walks a directory that gains a file between
+ # its readdir and its rmdir and raises "Directory not empty" -- which
+ # failed the probe, which failed every test in the file, intermittently
+ # and for a reason nowhere near the chat code they were testing. A few
+ # bytes left in a throwaway profile are harmless; failing the run is not.
+ with tempfile.TemporaryDirectory(
+ ignore_cleanup_errors=True) as profile:
proc = subprocess.Popen(
["google-chrome", "--headless=new", "--disable-gpu", "--no-sandbox",
f"--user-data-dir={profile}", "--window-size=1100,800",
@@ -312,6 +320,7 @@ def main() -> int:
proc.wait(timeout=10)
except subprocess.TimeoutExpired:
proc.kill()
+ proc.wait()
if not RECORDS:
print(json.dumps({"error": "no measurement"}), file=sys.stderr)
return 1
diff --git a/packages/meshbay-hub/tests/harness/group_tab_probe.py b/packages/meshbay-hub/tests/harness/group_tab_probe.py
index 5e4e452..b6d2bcc 100644
--- a/packages/meshbay-hub/tests/harness/group_tab_probe.py
+++ b/packages/meshbay-hub/tests/harness/group_tab_probe.py
@@ -156,7 +156,15 @@ class H(http.server.BaseHTTPRequestHandler):
def main() -> int:
with socketserver.TCPServer(("127.0.0.1", PORT), H) as srv:
threading.Thread(target=srv.serve_forever, daemon=True).start()
- with tempfile.TemporaryDirectory() as profile:
+ # ignore_cleanup_errors: Chrome's children (zygote, renderer, gpu)
+ # outlive terminate() on the parent by a moment and go on writing into
+ # the profile. rmtree then walks a directory that gains a file between
+ # its readdir and its rmdir and raises "Directory not empty" -- which
+ # failed the probe, which failed every test in the file, intermittently
+ # and for a reason nowhere near the chat code they were testing. A few
+ # bytes left in a throwaway profile are harmless; failing the run is not.
+ with tempfile.TemporaryDirectory(
+ ignore_cleanup_errors=True) as profile:
proc = subprocess.Popen(
["google-chrome", "--headless=new", "--disable-gpu", "--no-sandbox",
f"--user-data-dir={profile}", "--window-size=1100,900",
@@ -171,6 +179,7 @@ def main() -> int:
proc.wait(timeout=10)
except subprocess.TimeoutExpired:
proc.kill()
+ proc.wait()
if not RECORDS:
print(json.dumps({"error": "no measurement"}), file=sys.stderr)
return 1
diff --git a/packages/meshbay-hub/tests/harness/layout_probe.py b/packages/meshbay-hub/tests/harness/layout_probe.py
index 530b3f0..65b3083 100644
--- a/packages/meshbay-hub/tests/harness/layout_probe.py
+++ b/packages/meshbay-hub/tests/harness/layout_probe.py
@@ -19,6 +19,7 @@ single pass. Launching Chrome per width put three minutes on the test suite.
"""
import http.server
import json
+import shutil
import socketserver
import subprocess
import sys
@@ -118,16 +119,25 @@ def main() -> int:
srv = S(("127.0.0.1", PORT), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
+ # mkdtemp left a Chrome profile in /tmp on every run, for ever, and nothing
+ # waited for Chrome to exit. Same cleanup rule as the other probes.
+ profile = tempfile.mkdtemp(prefix="chrome-layout-")
chrome = subprocess.Popen([
"google-chrome", "--headless=new", "--no-sandbox",
"--window-size=1000,900",
- "--user-data-dir=" + tempfile.mkdtemp(prefix="chrome-layout-"),
+ "--user-data-dir=" + profile,
f"http://127.0.0.1:{PORT}/",
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
deadline = time.time() + 45
while time.time() < deadline and not RECORDS:
time.sleep(0.2)
chrome.terminate()
+ try:
+ chrome.wait(timeout=10)
+ except subprocess.TimeoutExpired:
+ chrome.kill()
+ chrome.wait()
+ shutil.rmtree(profile, ignore_errors=True)
srv.shutdown()
if not RECORDS:
print(json.dumps({"error": "no measurement"}))
diff --git a/packages/meshbay-hub/tests/harness/scroll_probe.py b/packages/meshbay-hub/tests/harness/scroll_probe.py
index 46d8357..ae407b8 100644
--- a/packages/meshbay-hub/tests/harness/scroll_probe.py
+++ b/packages/meshbay-hub/tests/harness/scroll_probe.py
@@ -151,7 +151,15 @@ class H(http.server.BaseHTTPRequestHandler):
def main() -> int:
with socketserver.TCPServer(("127.0.0.1", PORT), H) as srv:
threading.Thread(target=srv.serve_forever, daemon=True).start()
- with tempfile.TemporaryDirectory() as profile:
+ # ignore_cleanup_errors: Chrome's children (zygote, renderer, gpu)
+ # outlive terminate() on the parent by a moment and go on writing into
+ # the profile. rmtree then walks a directory that gains a file between
+ # its readdir and its rmdir and raises "Directory not empty" -- which
+ # failed the probe, which failed every test in the file, intermittently
+ # and for a reason nowhere near the chat code they were testing. A few
+ # bytes left in a throwaway profile are harmless; failing the run is not.
+ with tempfile.TemporaryDirectory(
+ ignore_cleanup_errors=True) as profile:
subprocess.run(
["google-chrome", "--headless", "--disable-gpu", "--no-sandbox",
f"--user-data-dir={profile}", "--window-size=1100,1300",
diff --git a/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs b/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs
index 0b77e42..a6008c2 100644
--- a/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs
+++ b/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs
@@ -72,13 +72,34 @@ tp._nodeVersion = input.node_version;
const frames = [];
let uploadId = null;
+let answered = 0;
tp._send = (msg) => {
frames.push(toHex(msgpack_encode(msg)));
if (msg.upload_id) uploadId = msg.upload_id;
- if (input.mode !== 'receive') return;
+ if (input.mode !== 'receive') {
+ // Nothing answers in this mode -- except the probe, which the client waits
+ // five seconds for. A node that predates it refuses the index, and that
+ // refusal is a plain error rather than a sealed ack, so the harness can
+ // produce it honestly. It is also the degradation path worth exercising.
+ if (msg.chunk_index === -1) {
+ // With `probe_ack`, answer it the way a node holding part of this file
+ // does; without, the way one that predates the probe does.
+ const reply = input.probe_ack
+ ? Object.assign(msgpack_decode(hex(input.probe_ack)),
+ { upload_id: uploadId })
+ : { type: 'error', upload_id: uploadId,
+ code: 'bad_chunk_index', detail: 'Unexpected chunk index' };
+ setImmediate(() => tp._dispatch(reply));
+ }
+ return;
+ }
// Answer as the node did, on the next turn of the loop so the send path
// finishes first — which is also how a real ack arrives.
- const ack = msgpack_decode(hex(input.acks[msg.chunk_index]));
+ //
+ // By position, not by `chunk_index`: the node answers every frame including
+ // the probe, whose index is -1, and the two lists are built from the same
+ // sequence of frames.
+ const ack = msgpack_decode(hex(input.acks[answered++]));
ack.upload_id = uploadId;
// Through the real `_dispatch`, so the routing under test — matching an
// ack to its uploader by `upload_id` — is the shipped one.