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