generated from mononoki/pyproj
[+!] all but small
some small modifications, tests added
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Integration checks using the same client as src/.test/start_client.bat."""
|
||||
"""Integration checks using the same client as examples/client.py."""
|
||||
|
||||
import asyncio
|
||||
from copy import deepcopy
|
||||
@@ -12,9 +12,13 @@ class RunningServer:
|
||||
self.clients = []
|
||||
self.handlers = []
|
||||
self.watchers = []
|
||||
self.records = []
|
||||
self.errors = []
|
||||
self.expected_errors = []
|
||||
self.peer_errors = {}
|
||||
|
||||
async def __aenter__(self):
|
||||
self.initial_tasks = asyncio.all_tasks()
|
||||
# Lazy imports keep client-only tests runnable if the server has an error.
|
||||
from router import Router
|
||||
from session import Sessions
|
||||
@@ -22,11 +26,14 @@ class RunningServer:
|
||||
|
||||
errors = self.errors
|
||||
watchers = self.watchers
|
||||
records = self.records
|
||||
peer_errors = self.peer_errors
|
||||
|
||||
class ObservedSessions(Sessions):
|
||||
async def new(self, connection):
|
||||
uid = await super().new(connection)
|
||||
watchers.append(self[uid].watcher_task)
|
||||
records.append(self[uid])
|
||||
return uid
|
||||
|
||||
async def feed(self, uid, packet):
|
||||
@@ -36,6 +43,7 @@ class RunningServer:
|
||||
# Preserve the original error before the handler enters
|
||||
# its delayed cleanup and closes the client's transport.
|
||||
errors.append(error)
|
||||
peer_errors.setdefault(asyncio.current_task(), []).append(error)
|
||||
raise
|
||||
|
||||
self.router = Router()
|
||||
@@ -55,6 +63,17 @@ class RunningServer:
|
||||
if not task.cancelled() and task.exception() is not None:
|
||||
self.errors.append(task.exception())
|
||||
|
||||
def accept_peer_rejection(self, task, error):
|
||||
# Accept only the checked exception from this particular client handler.
|
||||
assert task in self.handlers and task.done() and not task.cancelled()
|
||||
assert task.exception() is error or any(error is item for item in self.peer_errors.get(task, []))
|
||||
self.expected_errors.append(error)
|
||||
|
||||
def assert_no_unexpected_errors(self):
|
||||
for error in self.errors:
|
||||
if not any(error is expected for expected in self.expected_errors):
|
||||
raise error
|
||||
|
||||
async def __aexit__(self, exc_type, exc, traceback):
|
||||
self.server.close()
|
||||
await self.server.wait_closed()
|
||||
@@ -63,17 +82,23 @@ class RunningServer:
|
||||
)
|
||||
# Stop pending recovery-window waits after assertions, without modifying
|
||||
# the production timer or waiting 60 seconds for each test connection.
|
||||
tasks = self.handlers + self.watchers
|
||||
tasks = set(self.handlers + self.watchers)
|
||||
tasks.update(asyncio.all_tasks() - self.initial_tasks)
|
||||
for task in tasks:
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
# A cancelled handler can enter finally and start the recovery wait.
|
||||
# Let it close its transport, then cancel that new wait as well.
|
||||
await asyncio.sleep(0)
|
||||
for task in tasks:
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
results = await asyncio.wait_for(asyncio.gather(*tasks, return_exceptions=True), 1)
|
||||
for result in results:
|
||||
if isinstance(result, BaseException) and not isinstance(result, asyncio.CancelledError):
|
||||
self.errors.append(result)
|
||||
if exc_type is None and self.errors:
|
||||
raise self.errors[0]
|
||||
if exc_type is None:
|
||||
self.assert_no_unexpected_errors()
|
||||
for result in close_results:
|
||||
if isinstance(result, BaseException):
|
||||
raise result
|
||||
|
||||
Reference in New Issue
Block a user