proto-socket/python/crosstest/python_dart_web.py

146 lines
4.5 KiB
Python

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())