5
5
from collections .abc import AsyncGenerator , Callable , Generator
6
6
from contextlib import asynccontextmanager
7
7
import functools
8
+ import gc
8
9
from json import JSONDecoder , loads
9
10
import logging
10
11
import sqlite3
11
12
import ssl
12
13
import threading
13
14
from typing import Any
14
15
from unittest .mock import AsyncMock , MagicMock , Mock , patch
16
+ import warnings
15
17
16
18
from aiohttp import client
17
19
from aiohttp .pytest_plugin import AiohttpClient
@@ -187,12 +189,14 @@ async def guard_func(*args, **kwargs):
187
189
188
190
189
191
@pytest .fixture (autouse = True )
190
- def verify_cleanup ():
192
+ def verify_cleanup (event_loop : asyncio . AbstractEventLoop ):
191
193
"""Verify that the test has cleaned up resources correctly."""
192
194
threads_before = frozenset (threading .enumerate ())
193
-
195
+ tasks_before = asyncio . all_tasks ( event_loop )
194
196
yield
195
197
198
+ event_loop .run_until_complete (event_loop .shutdown_default_executor ())
199
+
196
200
if len (INSTANCES ) >= 2 :
197
201
count = len (INSTANCES )
198
202
for inst in INSTANCES :
@@ -203,6 +207,26 @@ def verify_cleanup():
203
207
for thread in threads :
204
208
assert isinstance (thread , threading ._DummyThread )
205
209
210
+ # Warn and clean-up lingering tasks and timers
211
+ # before moving on to the next test.
212
+ tasks = asyncio .all_tasks (event_loop ) - tasks_before
213
+ for task in tasks :
214
+ warnings .warn (f"Linger task after test { task } " )
215
+ task .cancel ()
216
+ if tasks :
217
+ event_loop .run_until_complete (asyncio .wait (tasks ))
218
+
219
+ for handle in event_loop ._scheduled : # pylint: disable=protected-access
220
+ if not handle .cancelled ():
221
+ warnings .warn (f"Lingering timer after test { handle } " )
222
+ handle .cancel ()
223
+
224
+ # Make sure garbage collect run in same test as allocation
225
+ # this is to mimic the behavior of pytest-aiohttp, and is
226
+ # required to avoid warnings from spilling over into next
227
+ # test case.
228
+ gc .collect ()
229
+
206
230
207
231
@pytest .fixture (autouse = True )
208
232
def bcrypt_cost ():
@@ -381,7 +405,7 @@ def exc_handle(loop, context):
381
405
382
406
383
407
@pytest .fixture
384
- async def stop_hass ():
408
+ async def stop_hass (event_loop ):
385
409
"""Make sure all hass are stopped."""
386
410
orig_hass = ha .HomeAssistant
387
411
@@ -402,6 +426,7 @@ def mock_hass():
402
426
with patch .object (hass_inst .loop , "stop" ):
403
427
await hass_inst .async_block_till_done ()
404
428
await hass_inst .async_stop (force = True )
429
+ await event_loop .shutdown_default_executor ()
405
430
406
431
407
432
@pytest .fixture
0 commit comments