from __future__ import annotations import asyncio import ssl import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from proto_socket.communicator import type_name_of from proto_socket.packets.message_common_pb2 import TestData from proto_socket.ws_server import WsServer HOST = "127.0.0.1" WS_PORT = 29698 WSS_PORT = 29699 WS_PATH = "/" PROCESS_TIMEOUT = 60.0 SERVER_OBSERVATION_WINDOW = 0.5 CERT_PATH = Path(__file__).resolve().parents[1] / "test" / "certs" / "server.crt" KEY_PATH = Path(__file__).resolve().parents[1] / "test" / "certs" / "server.key" def parser_map(): return {type_name_of(TestData): TestData.FromString} async def main() -> None: print(f"INFO typeName python={type_name_of(TestData)}") try: await run_web_suites() except Exception as exc: print(f"FAIL crosstest error={exc}", file=sys.stderr) raise SystemExit(1) from exc print("PASS scenario=ws-send-push") print("PASS scenario=ws-request-response") print("PASS scenario=wss-send-push") print("PASS scenario=wss-request-response") print("PASS all python-server/dart-web-client crosstests passed") async def run_web_suites() -> None: loop = asyncio.get_running_loop() ws_received = loop.create_future() wss_received = loop.create_future() ws_server = make_web_server(WS_PORT, None, ws_received) wss_server = make_web_server(WSS_PORT, make_server_ssl_context(), wss_received) await ws_server.start() await wss_server.start() try: await run_dart_browser_test("test/browser_ws_python_test.dart") await expect_received("WS", ws_received) await expect_received("WSS", wss_received) finally: await wss_server.stop() await ws_server.stop() def make_web_server( port: int, ssl_context: ssl.SSLContext | None, received: asyncio.Future[bool], ) -> WsServer: server = WsServer( HOST, port, WS_PATH, interval_sec=0, wait_sec=0, parser_map=parser_map(), ssl_context=ssl_context, ) def on_connected(client): def on_request(req): print(f"SERVER_RECEIVED index={req.index} message={req.message}") if req.index == 101 and req.message == "fire from dart web client": if not received.done(): received.set_result(True) asyncio.create_task( client.communicator.send( TestData(index=200, message="push from python server") ) ) return TestData(index=req.index * 2, message="echo: " + req.message) client.communicator.add_request_listener(type_name_of(TestData), on_request) server.on_client_connected = on_connected return server def make_server_ssl_context() -> ssl.SSLContext: ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx.load_cert_chain(CERT_PATH, KEY_PATH) return ctx async def expect_received(transport: str, received: asyncio.Future[bool]) -> None: try: ok = await asyncio.wait_for(received, SERVER_OBSERVATION_WINDOW) except TimeoutError as exc: raise RuntimeError( f"{transport} web send-push server did not receive expected data" ) from exc if not ok: raise RuntimeError(f"{transport} web send-push server received unexpected data") async def run_dart_browser_test(test_path: str) -> None: dart_dir = dart_package_dir() process = await asyncio.create_subprocess_exec( "dart", "test", "-p", "chrome", test_path, cwd=dart_dir, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) try: stdout, stderr = await asyncio.wait_for(process.communicate(), PROCESS_TIMEOUT) except TimeoutError as exc: process.kill() await process.wait() raise TimeoutError("dart browser test timed out") from exc for line in stdout.decode().splitlines(): print(line) for line in stderr.decode().splitlines(): print(line, file=sys.stderr) if process.returncode != 0: raise RuntimeError(f"dart browser test failed exitCode={process.returncode}") def dart_package_dir() -> Path: repo_root = Path(__file__).resolve().parents[2] candidate = repo_root / "dart" if (candidate / "pubspec.yaml").is_file(): return candidate raise RuntimeError(f"cannot resolve dart package directory from {candidate}") if __name__ == "__main__": asyncio.run(main())