1010from contextlib import ExitStack , contextmanager
1111from threading import Thread
1212from test .support import LOOPBACK_TIMEOUT
13- from time import sleep , time
13+ from time import time
1414from unittest .mock import patch
1515
1616
@@ -42,7 +42,8 @@ def logcat_thread():
4242 for line in self .logcat_process .stdout :
4343 self .logcat_queue .put (line .rstrip ("\n " ))
4444 self .logcat_process .stdout .close ()
45- Thread (target = logcat_thread ).start ()
45+ self .logcat_thread = Thread (target = logcat_thread )
46+ self .logcat_thread .start ()
4647
4748 from ctypes import CDLL , c_char_p , c_int
4849 android_log_write = getattr (CDLL ("liblog.so" ), "__android_log_write" )
@@ -78,6 +79,7 @@ def assert_log(self, level, tag, expected, *, skip=False, timeout=0.5):
7879 def tearDown (self ):
7980 self .logcat_process .terminate ()
8081 self .logcat_process .wait (LOOPBACK_TIMEOUT )
82+ self .logcat_thread .join (LOOPBACK_TIMEOUT )
8183
8284 @contextmanager
8385 def unbuffered (self , stream ):
@@ -369,6 +371,8 @@ def write(b, lines=None, *, write_len=None):
369371 ):
370372 stream .write (obj )
371373
374+
375+ class TestAndroidRateLimit (unittest .TestCase ):
372376 def test_rate_limit (self ):
373377 # https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:system/logging/liblog/include/log/log_read.h;l=39
374378 PER_MESSAGE_OVERHEAD = 28
@@ -387,6 +391,19 @@ def test_rate_limit(self):
387391 1024 - PER_MESSAGE_OVERHEAD - len (tag ) - len (message .format (0 ))
388392 ) + "\n "
389393
394+ # To avoid depending on the performance of the test device, we mock the
395+ # passage of time.
396+ mock_now = time ()
397+
398+ def mock_time ():
399+ # Avoid division by zero by simulating a small delay.
400+ mock_sleep (0.0001 )
401+ return mock_now
402+
403+ def mock_sleep (duration ):
404+ nonlocal mock_now
405+ mock_now += duration
406+
390407 # See _android_support.py. The default values of these parameters work
391408 # well across a wide range of devices, but we'll use smaller values to
392409 # ensure a quick and reliable test that doesn't flood the log too much.
@@ -395,21 +412,24 @@ def test_rate_limit(self):
395412 with (
396413 patch ("_android_support.MAX_BYTES_PER_SECOND" , MAX_KB_PER_SECOND * 1024 ),
397414 patch ("_android_support.BUCKET_SIZE" , BUCKET_KB * 1024 ),
415+ patch ("_android_support.sleep" , mock_sleep ),
416+ patch ("_android_support.time" , mock_time ),
398417 ):
399418 # Make sure the token bucket is full.
400- sleep (BUCKET_KB / MAX_KB_PER_SECOND )
419+ stream .write ("Initial message to reset _prev_write_time" )
420+ mock_sleep (BUCKET_KB / MAX_KB_PER_SECOND )
401421 line_num = 0
402422
403423 # Write BUCKET_KB messages, and return the rate at which they were
404424 # accepted in KB per second.
405425 def write_bucketful ():
406426 nonlocal line_num
407- start = time ()
427+ start = mock_time ()
408428 max_line_num = line_num + BUCKET_KB
409429 while line_num < max_line_num :
410430 stream .write (message .format (line_num ))
411431 line_num += 1
412- return BUCKET_KB / (time () - start )
432+ return BUCKET_KB / (mock_time () - start )
413433
414434 # The first bucketful should be written with minimal delay. The
415435 # factor of 2 here is not arbitrary: it verifies that the system can
@@ -427,5 +447,5 @@ def write_bucketful():
427447 )
428448
429449 # Once the token bucket refills, we should go back to full speed.
430- sleep (BUCKET_KB / MAX_KB_PER_SECOND )
450+ mock_sleep (BUCKET_KB / MAX_KB_PER_SECOND )
431451 self .assertGreater (write_bucketful (), MAX_KB_PER_SECOND * 2 )
0 commit comments