Skip to content

Commit cdba9cd

Browse files
committed
Fix tests on Mac
1 parent a3233d8 commit cdba9cd

File tree

1 file changed

+47
-18
lines changed

1 file changed

+47
-18
lines changed

src/sage/doctest/util.py

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -762,28 +762,27 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
762762
EXAMPLES::
763763
764764
sage: from sage.doctest.util import ensure_interruptible_after
765-
sage: with ensure_interruptible_after(1) as data: sleep(3r)
765+
sage: with ensure_interruptible_after(1) as data: sleep(3)
766766
767767
``as data`` is optional, but if it is used, it will contain a few useful values::
768768
769-
sage: data # abs tol 0.01
769+
sage: data # abs tol 1
770770
{'alarm_raised': True, 'elapsed': 1.0}
771771
772772
``max_wait_after_interrupt`` can be passed if the function may take longer than usual to be interrupted::
773773
774+
sage: # needs sage.misc.cython
774775
sage: cython(r'''
775-
....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec
776+
....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec, time_t
776777
....: from cysignals.signals cimport sig_check
777-
....: from time import time as walltime
778778
....:
779779
....: cpdef void uninterruptible_sleep(double seconds):
780780
....: cdef timespec start_time, target_time
781-
....: start_walltime = walltime()
782781
....: clock_gettime(CLOCK_REALTIME, &start_time)
783782
....:
784-
....: cdef int floor_seconds = <int>seconds
783+
....: cdef time_t floor_seconds = <time_t>seconds
785784
....: target_time.tv_sec = start_time.tv_sec + floor_seconds
786-
....: target_time.tv_nsec = start_time.tv_nsec + <int>((seconds - floor_seconds) * 1e9)
785+
....: target_time.tv_nsec = start_time.tv_nsec + <long>((seconds - floor_seconds) * 1e9)
787786
....: if target_time.tv_nsec >= 1000000000:
788787
....: target_time.tv_nsec -= 1000000000
789788
....: target_time.tv_sec += 1
@@ -808,23 +807,44 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
808807
809808
TESTS::
810809
811-
sage: # we use 1r instead of 1 to avoid unexplained slowdown
812-
sage: with ensure_interruptible_after(2) as data: sleep(1r)
810+
sage: with ensure_interruptible_after(2) as data: sleep(1)
813811
Traceback (most recent call last):
814812
...
815-
RuntimeError: Function terminates early after 1.00... < 2.0000 seconds
816-
sage: data # abs tol 0.01
813+
RuntimeError: Function terminates early after 1... < 2.0000 seconds
814+
sage: data # abs tol 1
817815
{'alarm_raised': False, 'elapsed': 1.0}
818-
sage: with ensure_interruptible_after(1) as data: raise ValueError
819-
Traceback (most recent call last):
820-
...
821-
ValueError
822-
sage: data # abs tol 0.01
823-
{'alarm_raised': False, 'elapsed': 0.0}
824816
825-
::
817+
The test above requires a large tolerance, because both ``time.sleep`` and
818+
``from posix.unistd cimport usleep`` may have slowdown on the order of 0.1s on Mac,
819+
likely because the system is idle and GitHub CI switches the program out,
820+
and context switch back takes time. So we use busy wait instead::
826821
827822
sage: # needs sage.misc.cython
823+
sage: cython(r'''
824+
....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec, time_t
825+
....: from cysignals.signals cimport sig_check
826+
....:
827+
....: cpdef void interruptible_sleep(double seconds):
828+
....: cdef timespec start_time, target_time
829+
....: clock_gettime(CLOCK_REALTIME, &start_time)
830+
....:
831+
....: cdef time_t floor_seconds = <time_t>seconds
832+
....: target_time.tv_sec = start_time.tv_sec + floor_seconds
833+
....: target_time.tv_nsec = start_time.tv_nsec + <long>((seconds - floor_seconds) * 1e9)
834+
....: if target_time.tv_nsec >= 1000000000:
835+
....: target_time.tv_nsec -= 1000000000
836+
....: target_time.tv_sec += 1
837+
....:
838+
....: while True:
839+
....: sig_check()
840+
....: clock_gettime(CLOCK_REALTIME, &start_time)
841+
....: if start_time.tv_sec > target_time.tv_sec or (start_time.tv_sec == target_time.tv_sec and start_time.tv_nsec >= target_time.tv_nsec):
842+
....: break
843+
....: ''')
844+
sage: with ensure_interruptible_after(2) as data: interruptible_sleep(1)
845+
Traceback (most recent call last):
846+
...
847+
RuntimeError: Function terminates early after 1.00... < 2.0000 seconds
828848
sage: with ensure_interruptible_after(1) as data: uninterruptible_sleep(2)
829849
Traceback (most recent call last):
830850
...
@@ -837,6 +857,15 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
837857
RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2.00... seconds
838858
sage: data # abs tol 0.01
839859
{'alarm_raised': True, 'elapsed': 2.0}
860+
861+
::
862+
863+
sage: with ensure_interruptible_after(1) as data: raise ValueError
864+
Traceback (most recent call last):
865+
...
866+
ValueError
867+
sage: data # abs tol 0.01
868+
{'alarm_raised': False, 'elapsed': 0.0}
840869
"""
841870
seconds = float(seconds)
842871
max_wait_after_interrupt = float(max_wait_after_interrupt)

0 commit comments

Comments
 (0)