|
20 | 20 | 'mock_open',
|
21 | 21 | 'PropertyMock',
|
22 | 22 | 'seal',
|
| 23 | + 'CallEvent' |
23 | 24 | )
|
24 | 25 |
|
25 | 26 |
|
|
31 | 32 | import sys
|
32 | 33 | import builtins
|
33 | 34 | import pkgutil
|
| 35 | +import threading |
34 | 36 | from asyncio import iscoroutinefunction
|
35 | 37 | from types import CodeType, ModuleType, MethodType
|
36 | 38 | from unittest.util import safe_repr
|
@@ -224,6 +226,7 @@ def reset_mock():
|
224 | 226 | ret.reset_mock()
|
225 | 227 |
|
226 | 228 | funcopy.called = False
|
| 229 | + funcopy.call_event = CallEvent(mock) |
227 | 230 | funcopy.call_count = 0
|
228 | 231 | funcopy.call_args = None
|
229 | 232 | funcopy.call_args_list = _CallList()
|
@@ -446,6 +449,7 @@ def __init__(
|
446 | 449 | __dict__['_mock_delegate'] = None
|
447 | 450 |
|
448 | 451 | __dict__['_mock_called'] = False
|
| 452 | + __dict__['_mock_call_event'] = CallEvent(self) |
449 | 453 | __dict__['_mock_call_args'] = None
|
450 | 454 | __dict__['_mock_call_count'] = 0
|
451 | 455 | __dict__['_mock_call_args_list'] = _CallList()
|
@@ -547,6 +551,7 @@ def __class__(self):
|
547 | 551 | return self._spec_class
|
548 | 552 |
|
549 | 553 | called = _delegating_property('called')
|
| 554 | + call_event = _delegating_property('call_event') |
550 | 555 | call_count = _delegating_property('call_count')
|
551 | 556 | call_args = _delegating_property('call_args')
|
552 | 557 | call_args_list = _delegating_property('call_args_list')
|
@@ -584,6 +589,7 @@ def reset_mock(self, visited=None,*, return_value=False, side_effect=False):
|
584 | 589 | visited.append(id(self))
|
585 | 590 |
|
586 | 591 | self.called = False
|
| 592 | + self.call_event = CallEvent(self) |
587 | 593 | self.call_args = None
|
588 | 594 | self.call_count = 0
|
589 | 595 | self.mock_calls = _CallList()
|
@@ -1111,6 +1117,7 @@ def _mock_call(self, /, *args, **kwargs):
|
1111 | 1117 | def _increment_mock_call(self, /, *args, **kwargs):
|
1112 | 1118 | self.called = True
|
1113 | 1119 | self.call_count += 1
|
| 1120 | + self.call_event._notify() |
1114 | 1121 |
|
1115 | 1122 | # handle call_args
|
1116 | 1123 | # needs to be set here so assertions on call arguments pass before
|
@@ -2411,6 +2418,73 @@ def _format_call_signature(name, args, kwargs):
|
2411 | 2418 | return message % formatted_args
|
2412 | 2419 |
|
2413 | 2420 |
|
| 2421 | +class CallEvent(object): |
| 2422 | + def __init__(self, mock): |
| 2423 | + self._mock = mock |
| 2424 | + self._condition = threading.Condition() |
| 2425 | + |
| 2426 | + def wait(self, /, skip=0, timeout=None): |
| 2427 | + """ |
| 2428 | + Wait for any call. |
| 2429 | +
|
| 2430 | + :param skip: How many calls will be skipped. |
| 2431 | + As a result, the mock should be called at least |
| 2432 | + ``skip + 1`` times. |
| 2433 | +
|
| 2434 | + :param timeout: See :meth:`threading.Condition.wait`. |
| 2435 | + """ |
| 2436 | + def predicate(mock): |
| 2437 | + return mock.call_count > skip |
| 2438 | + |
| 2439 | + self.wait_for(predicate, timeout=timeout) |
| 2440 | + |
| 2441 | + def wait_for_call(self, call, /, skip=0, timeout=None): |
| 2442 | + """ |
| 2443 | + Wait for a given call. |
| 2444 | +
|
| 2445 | + :param skip: How many calls will be skipped. |
| 2446 | + As a result, the call should happen at least |
| 2447 | + ``skip + 1`` times. |
| 2448 | +
|
| 2449 | + :param timeout: See :meth:`threading.Condition.wait`. |
| 2450 | + """ |
| 2451 | + def predicate(mock): |
| 2452 | + return mock.call_args_list.count(call) > skip |
| 2453 | + |
| 2454 | + self.wait_for(predicate, timeout=timeout) |
| 2455 | + |
| 2456 | + def wait_for(self, predicate, /, timeout=None): |
| 2457 | + """ |
| 2458 | + Wait for a given predicate to become True. |
| 2459 | +
|
| 2460 | + :param predicate: A callable that receives mock which result |
| 2461 | + will be interpreted as a boolean value. |
| 2462 | + The final predicate value is the return value. |
| 2463 | +
|
| 2464 | + :param timeout: See :meth:`threading.Condition.wait`. |
| 2465 | + """ |
| 2466 | + try: |
| 2467 | + self._condition.acquire() |
| 2468 | + |
| 2469 | + def _predicate(): |
| 2470 | + return predicate(self._mock) |
| 2471 | + |
| 2472 | + b = self._condition.wait_for(_predicate, timeout) |
| 2473 | + |
| 2474 | + if not b: |
| 2475 | + msg = (f"{self._mock._mock_name or 'mock'} was not called before" |
| 2476 | + f" timeout({timeout}).") |
| 2477 | + raise AssertionError(msg) |
| 2478 | + finally: |
| 2479 | + self._condition.release() |
| 2480 | + |
| 2481 | + def _notify(self): |
| 2482 | + try: |
| 2483 | + self._condition.acquire() |
| 2484 | + self._condition.notify_all() |
| 2485 | + finally: |
| 2486 | + self._condition.release() |
| 2487 | + |
2414 | 2488 |
|
2415 | 2489 | class _Call(tuple):
|
2416 | 2490 | """
|
|
0 commit comments