Skip to content

Commit a706c06

Browse files
authored
PYTHON-5925 Fix flaky SRV polling monitor tests (#2924)
1 parent eb0b92f commit a706c06

3 files changed

Lines changed: 30 additions & 15 deletions

File tree

test/asynchronous/test_srv_polling.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import time
2222
from typing import Any
2323

24-
from test.asynchronous.utils import flaky
2524
from test.utils_shared import FunctionCallRecorder
2625

2726
sys.path[0:0] = [""]
@@ -226,7 +225,6 @@ def response_callback(*args):
226225

227226
await self.run_scenario(response_callback, False)
228227

229-
@flaky(reason="PYTHON-5500", max_runs=3)
230228
async def test_dns_failures_logging(self):
231229
from dns import exception
232230

@@ -237,8 +235,10 @@ def response_callback(*args):
237235

238236
await self.run_scenario(response_callback, False)
239237

240-
srv_failure_logs = [r for r in cm.records if "SRV monitor check failed" in r.getMessage()]
241-
self.assertEqual(len(srv_failure_logs), 1)
238+
await async_wait_until(
239+
lambda: any("SRV monitor check failed" in r.getMessage() for r in cm.records),
240+
"log the SRV monitor check failure",
241+
)
242242

243243
async def test_dns_record_lookup_empty(self):
244244
response: list = []
@@ -270,14 +270,12 @@ def final_callback():
270270
# Nodelist should reflect new valid DNS resolver response.
271271
await self.assert_nodelist_change(response_final, client)
272272

273-
@flaky(reason="PYTHON-5315")
274273
async def test_recover_from_initially_empty_seedlist(self):
275274
def empty_seedlist():
276275
return []
277276

278277
await self._test_recover_from_initial(empty_seedlist)
279278

280-
@flaky(reason="PYTHON-5315")
281279
async def test_recover_from_initially_erroring_seedlist(self):
282280
def erroring_seedlist():
283281
raise ConfigurationError

test/test_srv_polling.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import time
2222
from typing import Any
2323

24-
from test.utils import flaky
2524
from test.utils_shared import FunctionCallRecorder
2625

2726
sys.path[0:0] = [""]
@@ -226,7 +225,6 @@ def response_callback(*args):
226225

227226
self.run_scenario(response_callback, False)
228227

229-
@flaky(reason="PYTHON-5500", max_runs=3)
230228
def test_dns_failures_logging(self):
231229
from dns import exception
232230

@@ -237,8 +235,10 @@ def response_callback(*args):
237235

238236
self.run_scenario(response_callback, False)
239237

240-
srv_failure_logs = [r for r in cm.records if "SRV monitor check failed" in r.getMessage()]
241-
self.assertEqual(len(srv_failure_logs), 1)
238+
wait_until(
239+
lambda: any("SRV monitor check failed" in r.getMessage() for r in cm.records),
240+
"log the SRV monitor check failure",
241+
)
242242

243243
def test_dns_record_lookup_empty(self):
244244
response: list = []
@@ -270,14 +270,12 @@ def final_callback():
270270
# Nodelist should reflect new valid DNS resolver response.
271271
self.assert_nodelist_change(response_final, client)
272272

273-
@flaky(reason="PYTHON-5315")
274273
def test_recover_from_initially_empty_seedlist(self):
275274
def empty_seedlist():
276275
return []
277276

278277
self._test_recover_from_initial(empty_seedlist)
279278

280-
@flaky(reason="PYTHON-5315")
281279
def test_recover_from_initially_erroring_seedlist(self):
282280
def erroring_seedlist():
283281
raise ConfigurationError

test/utils_shared.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,19 +338,38 @@ def __eq__(self, other):
338338

339339

340340
class FunctionCallRecorder:
341-
"""Utility class to wrap a callable and record its invocations."""
341+
"""Utility class to wrap a callable and record its invocations.
342+
343+
A synchronous callable is recorded immediately, before it runs. A
344+
coroutine function is recorded only once the returned coroutine
345+
finishes running, whether it returns, raises, or is cancelled, so
346+
callers can rely on a recorded call meaning the wrapped coroutine
347+
actually ran rather than merely having been scheduled.
348+
"""
342349

343350
def __init__(self, function):
344351
self._function = function
345352
self._call_list = []
346353

347354
def __call__(self, *args, **kwargs):
348-
self._call_list.append((args, kwargs))
349355
if iscoroutinefunction(self._function):
350-
return self._function(*args, **kwargs)
356+
357+
async def _run_and_record():
358+
try:
359+
return await self._function(*args, **kwargs)
360+
finally:
361+
self._call_list.append((args, kwargs))
362+
363+
return _run_and_record()
351364
else:
365+
self._call_list.append((args, kwargs))
352366
return self._function(*args, **kwargs)
353367

368+
def __get__(self, obj, objtype=None):
369+
if obj is None:
370+
return self
371+
return functools.partial(self, obj)
372+
354373
def reset(self):
355374
"""Wipes the call list."""
356375
self._call_list = []

0 commit comments

Comments
 (0)