diff options
Diffstat (limited to 'packages/meshbay-hub/tests/harness')
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. |