@@ -627,14 +627,12 @@ def _impl_test_interactive_timers():
627
627
# NOTE: We run the timer tests in parallel to avoid longer sequential
628
628
# delays which adds to the testing time. Add new tests to one of
629
629
# the current event loop iterations if possible.
630
+ import time
630
631
from unittest .mock import Mock
631
632
import matplotlib .pyplot as plt
632
633
633
634
fig = plt .figure ()
634
- event_loop_time = 1 # in seconds
635
- expected_200ms_calls = int (event_loop_time / 0.2 )
636
-
637
- # Start at 2s interval (would only get one firing), then update to 200ms
635
+ # Start at 2s interval (wouldn't get any firings), then update to 100ms
638
636
timer_repeating = fig .canvas .new_timer (2000 )
639
637
mock_repeating = Mock ()
640
638
timer_repeating .add_callback (mock_repeating )
@@ -645,42 +643,55 @@ def _impl_test_interactive_timers():
645
643
646
644
timer_repeating .start ()
647
645
# Test updating the interval updates a running timer
648
- timer_repeating .interval = 200
646
+ timer_repeating .interval = 100
649
647
# Start as a repeating timer then change to singleshot via the attribute
650
648
timer_single_shot .start ()
651
649
timer_single_shot .single_shot = True
652
650
653
- fig .canvas .start_event_loop (event_loop_time )
654
- assert 1 < mock_repeating .call_count <= expected_200ms_calls + 1 , \
655
- f"Interval update: Expected between 2 and { expected_200ms_calls + 1 } calls, " \
656
- f"got { mock_repeating .call_count } "
651
+ fig .canvas .start_event_loop (0.5 )
652
+ assert 2 <= mock_repeating .call_count <= 5 , \
653
+ f"Interval update: Expected 2-5 calls, got { mock_repeating .call_count } "
657
654
assert mock_single_shot .call_count == 1 , \
658
655
f"Singleshot: Expected 1 call, got { mock_single_shot .call_count } "
659
656
660
- # 200ms timer triggers and the callback takes 100ms to run
661
- # Test that we don't drift and that we get called on every 200ms
662
- # interval and not every 300ms
663
- mock_repeating .side_effect = lambda : time .sleep (0.1 )
657
+ # 250ms timer triggers and the callback takes 150ms to run
658
+ # Test that we don't drift and that we get called on every 250ms
659
+ # firing and not every 400ms
660
+ timer_repeating .interval = 250
661
+ mock_repeating .side_effect = lambda : time .sleep (0.15 )
662
+ # calling start() again on a repeating timer should remove the old
663
+ # one, so we don't want double the number of calls here either because
664
+ # two timers are potentially running.
665
+ timer_repeating .start ()
664
666
mock_repeating .call_count = 0
665
667
# Make sure we can start the timer after stopping a singleshot timer
666
668
timer_single_shot .stop ()
667
669
timer_single_shot .start ()
668
670
671
+ event_loop_time = 2 # in seconds
672
+ expected_calls = int (event_loop_time / (timer_repeating .interval / 1000 ))
673
+
674
+ t_start = time .perf_counter ()
669
675
fig .canvas .start_event_loop (event_loop_time )
670
- # Not exact timers, so add a little slop. We really want to make sure we are
671
- # getting more than 3 (every 300ms).
672
- assert mock_repeating .call_count >= expected_200ms_calls - 1 , \
673
- f"Slow callback: Expected at least { expected_200ms_calls - 1 } calls, " \
676
+ t_loop = time .perf_counter () - t_start
677
+ # Should be around 2s, but allow for some slop on CI. We want to make sure
678
+ # we aren't getting 2 + (callback time) 0.5s/iteration, which would be 4+ s.
679
+ assert 1.8 < t_loop < 3 , \
680
+ f"Event loop: Expected to run for around 2s, but ran for { t_loop :.2f} s"
681
+ # Not exact timers, so add some slop. (Quite a bit for CI resources)
682
+ assert abs (mock_repeating .call_count - expected_calls ) <= 2 , \
683
+ f"Slow callback: Expected { expected_calls } calls, " \
674
684
f"got { mock_repeating .call_count } "
675
685
assert mock_single_shot .call_count == 2 , \
676
686
f"Singleshot: Expected 2 calls, got { mock_single_shot .call_count } "
677
- plt .close ("all" )
678
687
679
688
680
689
@pytest .mark .parametrize ("env" , _get_testable_interactive_backends ())
681
690
def test_interactive_timers (env ):
682
691
if env ["MPLBACKEND" ] == "wx" :
683
692
pytest .skip ("wx backend is deprecated; tests failed on appveyor" )
693
+ if env ["MPLBACKEND" ].startswith ("gtk3" ) and is_ci_environment ():
694
+ pytest .xfail ("GTK3 backend timer is slow on CI resources" )
684
695
_run_helper (_impl_test_interactive_timers ,
685
696
timeout = _test_timeout , extra_env = env )
686
697
0 commit comments