Skip to content

Commit 7defae9

Browse files
cartertinneyCopilot
andcommitted
tests: calibrate polling deadlines
Use the 100 ms prompt deadline for single-hop pipeline callbacks and a one-second eventual deadline for handler work crossing queues, event loops, and worker threads. Keep five-second bounds only for deliberate deadlock-safety orchestration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3e01116 commit 7defae9

4 files changed

Lines changed: 59 additions & 31 deletions

File tree

‎tests/unit/common/pipeline/test_pipeline_stages_base.py‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .helpers import StageRunOpTestBase, StageHandlePipelineEventTestBase
2828
from .fixtures import ArbitraryOperation
2929
from tests.unit.common.pipeline import pipeline_stage_test
30+
from tests.unit.helpers import PROMPT_TIMEOUT
3031

3132
this_module = sys.modules[__name__]
3233
logging.basicConfig(level=logging.DEBUG)
@@ -188,7 +189,7 @@ def test_invoke_handler(self, mocker, stage, event, poll_until):
188189
stage.on_connected_handler = mock_handler
189190
stage.handle_pipeline_event(event)
190191
# Wait for the handler invocation to complete
191-
poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
192+
poll_until(lambda: mock_handler.call_count >= 1, timeout=PROMPT_TIMEOUT)
192193
assert mock_handler.call_count == 1
193194
assert mock_handler.call_args == mocker.call()
194195

@@ -209,7 +210,7 @@ def test_invoke_handler(self, mocker, stage, event, poll_until):
209210
stage.on_disconnected_handler = mock_handler
210211
stage.handle_pipeline_event(event)
211212
# Wait for the handler invocation to complete
212-
poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
213+
poll_until(lambda: mock_handler.call_count >= 1, timeout=PROMPT_TIMEOUT)
213214
assert mock_handler.call_count == 1
214215
assert mock_handler.call_args == mocker.call()
215216

@@ -230,7 +231,7 @@ def test_invoke_handler(self, mocker, stage, event, poll_until):
230231
stage.on_new_sastoken_required_handler = mock_handler
231232
stage.handle_pipeline_event(event)
232233
# Wait for the handler invocation to complete
233-
poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
234+
poll_until(lambda: mock_handler.call_count >= 1, timeout=PROMPT_TIMEOUT)
234235
assert mock_handler.call_count == 1
235236
assert mock_handler.call_args == mocker.call()
236237

@@ -253,7 +254,7 @@ def test_invoke_handler(self, mocker, stage, event, poll_until):
253254
stage.on_background_exception_handler = mock_handler
254255
stage.handle_pipeline_event(event)
255256
# Wait for the handler invocation to complete
256-
poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
257+
poll_until(lambda: mock_handler.call_count >= 1, timeout=PROMPT_TIMEOUT)
257258
assert mock_handler.call_count == 1
258259
assert mock_handler.call_args == mocker.call(event.e)
259260

@@ -274,7 +275,7 @@ def test_invoke_handler(self, mocker, stage, event, poll_until):
274275
stage.on_pipeline_event_handler = mock_handler
275276
stage.handle_pipeline_event(event)
276277
# Wait for the handler invocation to complete
277-
poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
278+
poll_until(lambda: mock_handler.call_count >= 1, timeout=PROMPT_TIMEOUT)
278279
assert mock_handler.call_count == 1
279280
assert mock_handler.call_args == mocker.call(event)
280281

‎tests/unit/helpers.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@
66

77
# Maximum time allowed for an operation expected to complete promptly.
88
PROMPT_TIMEOUT = 0.1
9+
10+
# Maximum time allowed for asynchronous work crossing threads or event loops.
11+
EVENTUAL_TIMEOUT = 1

‎tests/unit/iothub/aio/test_async_handler_manager.py‎

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from azure.iot.device.iothub.sync_handler_manager import MESSAGE, METHOD, TWIN_DP_PATCH
1919
from azure.iot.device.iothub.inbox_manager import InboxManager
2020
from azure.iot.device.iothub.aio.async_inbox import AsyncClientInbox
21+
from tests.unit.helpers import EVENTUAL_TIMEOUT
2122

2223
logging.basicConfig(level=logging.DEBUG)
2324

@@ -413,7 +414,7 @@ async def test_handler_invoked(
413414
mock_obj = mocker.MagicMock()
414415
inbox.put(mock_obj)
415416
# Wait for the handler invocation to complete
416-
await async_poll_until(lambda: handler_checker.handler_called, timeout=5)
417+
await async_poll_until(lambda: handler_checker.handler_called, timeout=EVENTUAL_TIMEOUT)
417418

418419
# Handler has been called with the item from the inbox
419420
assert handler_checker.handler_called is True
@@ -441,7 +442,9 @@ async def test_handler_invoked_multiple(
441442
for _ in range(5):
442443
inbox.put(mocker.MagicMock())
443444
# Wait for all handler invocations to complete
444-
await async_poll_until(lambda: handler_checker.handler_call_count >= 5, timeout=5)
445+
await async_poll_until(
446+
lambda: handler_checker.handler_call_count >= 5, timeout=EVENTUAL_TIMEOUT
447+
)
445448

446449
# Handler has been called 5 times
447450
assert handler_checker.handler_call_count == 5
@@ -550,7 +553,7 @@ async def coro_handler(arg):
550553
# Add an item to corresponding inbox, triggering the handler
551554
inbox.put(mocker.MagicMock())
552555
# Wait for the background exception handler to be called
553-
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=5)
556+
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
554557
# Background exception handler was called
555558
assert background_exc_spy.call_count == 1
556559
e = background_exc_spy.call_args[0][0]
@@ -567,7 +570,7 @@ async def coro_handler(arg):
567570
# Add an item to corresponding inbox, triggering the handler
568571
inbox.put(mocker.MagicMock())
569572
# Wait for the background exception handler to be called
570-
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=5)
573+
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
571574
# Background exception handler was called
572575
assert background_exc_spy.call_count == 1
573576
e = background_exc_spy.call_args[0][0]
@@ -596,7 +599,8 @@ def handler1(arg):
596599
inbox.put(mocker.MagicMock())
597600
# Wait for handler1 to replace itself with handler2
598601
await async_poll_until(
599-
lambda: getattr(handler_manager, handler_name) is handler2, timeout=5
602+
lambda: getattr(handler_manager, handler_name) is handler2,
603+
timeout=EVENTUAL_TIMEOUT,
600604
)
601605
# The set handler (handler1) has been replaced with a new handler (handler2)
602606
assert getattr(handler_manager, handler_name) is not handler1
@@ -605,15 +609,16 @@ def handler1(arg):
605609
inbox.put(mocker.MagicMock())
606610
# Wait for handler2 to replace itself with the mock
607611
await async_poll_until(
608-
lambda: getattr(handler_manager, handler_name) is mock_handler, timeout=5
612+
lambda: getattr(handler_manager, handler_name) is mock_handler,
613+
timeout=EVENTUAL_TIMEOUT,
609614
)
610615
# The set handler (handler2) has now been replaced by a mock handler
611616
assert getattr(handler_manager, handler_name) is not handler2
612617
assert getattr(handler_manager, handler_name) is mock_handler
613618
# Add a new item to the inbox
614619
inbox.put(mocker.MagicMock())
615620
# Wait for the mock handler invocation to complete
616-
await async_poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
621+
await async_poll_until(lambda: mock_handler.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
617622
# The mock was now called
618623
assert getattr(handler_manager, handler_name).call_count == 1
619624

@@ -706,7 +711,9 @@ async def test_handler_invoked(
706711
# Add the event to the client inbox
707712
inbox.put(event)
708713
# Wait for the handler invocation to complete
709-
await async_poll_until(lambda: handler_checker.handler_call_count >= 1, timeout=5)
714+
await async_poll_until(
715+
lambda: handler_checker.handler_call_count >= 1, timeout=EVENTUAL_TIMEOUT
716+
)
710717

711718
# Handler has been called with the arguments from the event
712719
assert handler_checker.handler_call_count == 1
@@ -716,7 +723,7 @@ async def test_handler_invoked(
716723
non_matching_event = client_event.ClientEvent("NON_MATCHING_EVENT")
717724
inbox.put(non_matching_event)
718725
# Wait for the runner to consume the non-matching event
719-
await async_poll_until(inbox.empty, timeout=5)
726+
await async_poll_until(inbox.empty, timeout=EVENTUAL_TIMEOUT)
720727

721728
# Handler has not been called again
722729
assert handler_checker.handler_call_count == 1
@@ -743,7 +750,9 @@ async def test_handler_invoked_multiple(
743750
for _ in range(5):
744751
inbox.put(event)
745752
# Wait for all handler invocations to complete
746-
await async_poll_until(lambda: handler_checker.handler_call_count >= 5, timeout=5)
753+
await async_poll_until(
754+
lambda: handler_checker.handler_call_count >= 5, timeout=EVENTUAL_TIMEOUT
755+
)
747756

748757
# Handler has been called 5 times
749758
assert handler_checker.handler_call_count == 5
@@ -777,7 +786,7 @@ async def coro_handler(*args):
777786
# Add an item to corresponding inbox, triggering the handler
778787
inbox.put(event)
779788
# Wait for the background exception handler to be called
780-
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=5)
789+
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
781790
# Background exception handler was called
782791
assert background_exc_spy.call_count == 1
783792
e = background_exc_spy.call_args[0][0]
@@ -794,7 +803,7 @@ async def coro_handler(*args):
794803
# Add an item to corresponding inbox, triggering the handler
795804
inbox.put(event)
796805
# Wait for the background exception handler to be called
797-
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=5)
806+
await async_poll_until(lambda: background_exc_spy.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
798807
# Background exception handler was called
799808
assert background_exc_spy.call_count == 1
800809
e = background_exc_spy.call_args[0][0]
@@ -823,7 +832,8 @@ def handler1(*args):
823832
inbox.put(event)
824833
# Wait for handler1 to replace itself with handler2
825834
await async_poll_until(
826-
lambda: getattr(handler_manager, handler_name) is handler2, timeout=5
835+
lambda: getattr(handler_manager, handler_name) is handler2,
836+
timeout=EVENTUAL_TIMEOUT,
827837
)
828838
# The set handler (handler1) has been replaced with a new handler (handler2)
829839
assert getattr(handler_manager, handler_name) is not handler1
@@ -832,15 +842,16 @@ def handler1(*args):
832842
inbox.put(event)
833843
# Wait for handler2 to replace itself with the mock
834844
await async_poll_until(
835-
lambda: getattr(handler_manager, handler_name) is mock_handler, timeout=5
845+
lambda: getattr(handler_manager, handler_name) is mock_handler,
846+
timeout=EVENTUAL_TIMEOUT,
836847
)
837848
# The set handler (handler2) has now been replaced by a mock handler
838849
assert getattr(handler_manager, handler_name) is not handler2
839850
assert getattr(handler_manager, handler_name) is mock_handler
840851
# Add a new item to the inbox
841852
inbox.put(event)
842853
# Wait for the mock handler invocation to complete
843-
await async_poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
854+
await async_poll_until(lambda: mock_handler.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
844855
# The mock was now called
845856
assert getattr(handler_manager, handler_name).call_count == 1
846857

‎tests/unit/iothub/test_sync_handler_manager.py‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from azure.iot.device.iothub.sync_handler_manager import MESSAGE, METHOD, TWIN_DP_PATCH
1818
from azure.iot.device.iothub.inbox_manager import InboxManager
1919
from azure.iot.device.iothub.sync_inbox import SyncClientInbox
20+
from tests.unit.helpers import EVENTUAL_TIMEOUT
2021

2122
logging.basicConfig(level=logging.DEBUG)
2223

@@ -363,7 +364,7 @@ def test_handler_invoked(self, mocker, handler_name, handler_manager, inbox, pol
363364
mock_obj = mocker.MagicMock()
364365
inbox.put(mock_obj)
365366
# Wait for the handler invocation to complete
366-
poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
367+
poll_until(lambda: mock_handler.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
367368

368369
# Handler has been called with the item from the inbox
369370
assert mock_handler.call_count == 1
@@ -385,7 +386,7 @@ def test_handler_invoked_multiple(
385386
for _ in range(5):
386387
inbox.put(mocker.MagicMock())
387388
# Wait for all handler invocations to complete
388-
poll_until(lambda: mock_handler.call_count >= 5, timeout=5)
389+
poll_until(lambda: mock_handler.call_count >= 5, timeout=EVENTUAL_TIMEOUT)
389390

390391
# Handler has been called 5 times
391392
assert mock_handler.call_count == 5
@@ -485,7 +486,7 @@ def test_exception_in_handler(
485486
# Add an item to corresponding inbox, triggering the handler
486487
inbox.put(mocker.MagicMock())
487488
# Wait for the background exception handler to be called
488-
poll_until(lambda: background_exc_spy.call_count >= 1, timeout=5)
489+
poll_until(lambda: background_exc_spy.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
489490
# Handler has now been called
490491
assert mock_handler.call_count == 1
491492
# Background exception handler was called
@@ -506,14 +507,20 @@ def handler(arg):
506507

507508
inbox.put(mocker.MagicMock())
508509
# Wait for the handler to replace itself with the mock
509-
poll_until(lambda: getattr(handler_manager, handler_name) is not handler, timeout=5)
510+
poll_until(
511+
lambda: getattr(handler_manager, handler_name) is not handler,
512+
timeout=EVENTUAL_TIMEOUT,
513+
)
510514
# Handler has been replaced with a mock, but the mock has not been invoked
511515
assert getattr(handler_manager, handler_name) is not handler
512516
assert getattr(handler_manager, handler_name).call_count == 0
513517
# Add a new item to the inbox
514518
inbox.put(mocker.MagicMock())
515519
# Wait for the mock handler invocation to complete
516-
poll_until(lambda: getattr(handler_manager, handler_name).call_count >= 1, timeout=5)
520+
poll_until(
521+
lambda: getattr(handler_manager, handler_name).call_count >= 1,
522+
timeout=EVENTUAL_TIMEOUT,
523+
)
517524
# The mock was now called
518525
assert getattr(handler_manager, handler_name).call_count == 1
519526

@@ -597,7 +604,7 @@ def test_handler_invoked(self, mocker, handler_name, handler_manager, inbox, eve
597604
# Add the event to the client event inbox
598605
inbox.put(event)
599606
# Wait for the handler invocation to complete
600-
poll_until(lambda: mock_handler.call_count >= 1, timeout=5)
607+
poll_until(lambda: mock_handler.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
601608

602609
# Handler has been called with the arguments from the event
603610
assert mock_handler.call_count == 1
@@ -607,7 +614,7 @@ def test_handler_invoked(self, mocker, handler_name, handler_manager, inbox, eve
607614
non_matching_event = client_event.ClientEvent("NON_MATCHING_EVENT")
608615
inbox.put(non_matching_event)
609616
# Wait for the runner to consume the non-matching event
610-
poll_until(inbox.empty, timeout=5)
617+
poll_until(inbox.empty, timeout=EVENTUAL_TIMEOUT)
611618

612619
# Handler has not been called again
613620
assert mock_handler.call_count == 1
@@ -628,7 +635,7 @@ def test_handler_invoked_multiple(
628635
for _ in range(5):
629636
inbox.put(event)
630637
# Wait for all handler invocations to complete
631-
poll_until(lambda: mock_handler.call_count >= 5, timeout=5)
638+
poll_until(lambda: mock_handler.call_count >= 5, timeout=EVENTUAL_TIMEOUT)
632639

633640
# Handler has been called 5 times
634641
assert mock_handler.call_count == 5
@@ -652,7 +659,7 @@ def test_exception_in_handler(
652659
# Add the event to the client event inbox, triggering the handler
653660
inbox.put(event)
654661
# Wait for the background exception handler to be called
655-
poll_until(lambda: background_exc_spy.call_count >= 1, timeout=5)
662+
poll_until(lambda: background_exc_spy.call_count >= 1, timeout=EVENTUAL_TIMEOUT)
656663
# Handler has now been called
657664
assert mock_handler.call_count == 1
658665
# Background exception handler was called
@@ -673,14 +680,20 @@ def handler(*args):
673680

674681
inbox.put(event)
675682
# Wait for the handler to replace itself with the mock
676-
poll_until(lambda: getattr(handler_manager, handler_name) is not handler, timeout=5)
683+
poll_until(
684+
lambda: getattr(handler_manager, handler_name) is not handler,
685+
timeout=EVENTUAL_TIMEOUT,
686+
)
677687
# Handler has been replaced with a mock, but the mock has not been invoked
678688
assert getattr(handler_manager, handler_name) is not handler
679689
assert getattr(handler_manager, handler_name).call_count == 0
680690
# Add a new event to the inbox
681691
inbox.put(event)
682692
# Wait for the mock handler invocation to complete
683-
poll_until(lambda: getattr(handler_manager, handler_name).call_count >= 1, timeout=5)
693+
poll_until(
694+
lambda: getattr(handler_manager, handler_name).call_count >= 1,
695+
timeout=EVENTUAL_TIMEOUT,
696+
)
684697
# The mock was now called
685698
assert getattr(handler_manager, handler_name).call_count == 1
686699

0 commit comments

Comments
 (0)