| 1 | #!/usr/bin/env python3 |
| 2 | """Measure native upload backlog against a delayed loopback server with fixture data.""" |
| 3 | import base64 |
| 4 | import http.server |
| 5 | import json |
| 6 | import os |
| 7 | from pathlib import Path |
| 8 | import subprocess |
| 9 | import sys |
| 10 | import threading |
| 11 | import time |
| 12 | |
| 13 | root = Path(__file__).resolve().parents[2] |
| 14 | fixture = (root / "internal/docs/seeddata/letter.update.bin").read_bytes() |
| 15 | updates = [] |
| 16 | lock = threading.Lock() |
| 17 | fail_once = "--fail-once" in sys.argv |
| 18 | |
| 19 | class Handler(http.server.BaseHTTPRequestHandler): |
| 20 | def do_GET(self): |
| 21 | if self.path.endswith("/stream"): |
| 22 | self.send_response(200) |
| 23 | self.send_header("Content-Type", "text/event-stream") |
| 24 | self.end_headers() |
| 25 | self.wfile.write(b"data: 0\n\n") |
| 26 | self.wfile.flush() |
| 27 | time.sleep(30) |
| 28 | return |
| 29 | if self.path == "/stats": |
| 30 | with lock: |
| 31 | data = json.dumps(dict(updates=updates)).encode() |
| 32 | elif "/updates" in self.path: |
| 33 | data = fixture |
| 34 | else: |
| 35 | data = json.dumps(dict(sqid="fixture", title="Sync fixture", guid="sync-fixture", |
| 36 | can_write=True, can_share=False)).encode() |
| 37 | self.send_response(200) |
| 38 | self.send_header("Docs-Seq", "0") |
| 39 | self.send_header("Content-Length", str(len(data))) |
| 40 | self.end_headers() |
| 41 | self.wfile.write(data) |
| 42 | |
| 43 | def do_POST(self): |
| 44 | body = self.rfile.read(int(self.headers.get("Content-Length", "0"))) |
| 45 | # Deterministic request latency, not an artificial client debounce. |
| 46 | time.sleep(0.25) |
| 47 | with lock: |
| 48 | updates.append(base64.b64encode(body).decode()) |
| 49 | self.send_response(503 if fail_once and len(updates) == 1 else 204) |
| 50 | self.end_headers() |
| 51 | |
| 52 | def log_message(self, *_): |
| 53 | pass |
| 54 | |
| 55 | with http.server.ThreadingHTTPServer(("127.0.0.1", 0), Handler) as server: |
| 56 | threading.Thread(target=server.serve_forever, daemon=True).start() |
| 57 | env = dict(os.environ, DOCS_SYNC_TEST_ORIGIN=f"http://127.0.0.1:{server.server_port}") |
| 58 | result = subprocess.run(["swift", "test", "--package-path", str(root / "native/apple"), |
| 59 | "--filter", "DocumentSyncIntegrationTests"], env=env, timeout=240) |
| 60 | server.shutdown() |
| 61 | raise SystemExit(result.returncode) |
| 62 | |