Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions test/asynchronous/test_srv_polling.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import time
from typing import Any

from test.asynchronous.utils import flaky
from test.utils_shared import FunctionCallRecorder

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

await self.run_scenario(response_callback, False)

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

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

await self.run_scenario(response_callback, False)

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

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

@flaky(reason="PYTHON-5315")
async def test_recover_from_initially_empty_seedlist(self):
def empty_seedlist():
return []

await self._test_recover_from_initial(empty_seedlist)

@flaky(reason="PYTHON-5315")
async def test_recover_from_initially_erroring_seedlist(self):
def erroring_seedlist():
raise ConfigurationError
Expand Down
10 changes: 4 additions & 6 deletions test/test_srv_polling.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import time
from typing import Any

from test.utils import flaky
from test.utils_shared import FunctionCallRecorder

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

self.run_scenario(response_callback, False)

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

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

self.run_scenario(response_callback, False)

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

def test_dns_record_lookup_empty(self):
response: list = []
Expand Down Expand Up @@ -270,14 +270,12 @@ def final_callback():
# Nodelist should reflect new valid DNS resolver response.
self.assert_nodelist_change(response_final, client)

@flaky(reason="PYTHON-5315")
def test_recover_from_initially_empty_seedlist(self):
def empty_seedlist():
return []

self._test_recover_from_initial(empty_seedlist)

@flaky(reason="PYTHON-5315")
def test_recover_from_initially_erroring_seedlist(self):
def erroring_seedlist():
raise ConfigurationError
Expand Down
25 changes: 22 additions & 3 deletions test/utils_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,19 +338,38 @@ def __eq__(self, other):


class FunctionCallRecorder:
"""Utility class to wrap a callable and record its invocations."""
"""Utility class to wrap a callable and record its invocations.

A synchronous callable is recorded immediately, before it runs. A
coroutine function is recorded only once the returned coroutine
finishes running, whether it returns, raises, or is cancelled, so
callers can rely on a recorded call meaning the wrapped coroutine
actually ran rather than merely having been scheduled.
"""

def __init__(self, function):
self._function = function
self._call_list = []

def __call__(self, *args, **kwargs):
self._call_list.append((args, kwargs))
if iscoroutinefunction(self._function):
return self._function(*args, **kwargs)

async def _run_and_record():
try:
return await self._function(*args, **kwargs)
finally:
self._call_list.append((args, kwargs))

return _run_and_record()
else:
self._call_list.append((args, kwargs))
return self._function(*args, **kwargs)

def __get__(self, obj, objtype=None):
if obj is None:
return self
return functools.partial(self, obj)

def reset(self):
"""Wipes the call list."""
self._call_list = []
Expand Down
Loading