10
10
from contextlib import ExitStack , contextmanager
11
11
from threading import Thread
12
12
from test .support import LOOPBACK_TIMEOUT
13
- from time import sleep , time
13
+ from time import time
14
14
from unittest .mock import patch
15
15
16
16
@@ -42,7 +42,8 @@ def logcat_thread():
42
42
for line in self .logcat_process .stdout :
43
43
self .logcat_queue .put (line .rstrip ("\n " ))
44
44
self .logcat_process .stdout .close ()
45
- Thread (target = logcat_thread ).start ()
45
+ self .logcat_thread = Thread (target = logcat_thread )
46
+ self .logcat_thread .start ()
46
47
47
48
from ctypes import CDLL , c_char_p , c_int
48
49
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):
78
79
def tearDown (self ):
79
80
self .logcat_process .terminate ()
80
81
self .logcat_process .wait (LOOPBACK_TIMEOUT )
82
+ self .logcat_thread .join (LOOPBACK_TIMEOUT )
81
83
82
84
@contextmanager
83
85
def unbuffered (self , stream ):
@@ -369,6 +371,8 @@ def write(b, lines=None, *, write_len=None):
369
371
):
370
372
stream .write (obj )
371
373
374
+
375
+ class TestAndroidRateLimit (unittest .TestCase ):
372
376
def test_rate_limit (self ):
373
377
# https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:system/logging/liblog/include/log/log_read.h;l=39
374
378
PER_MESSAGE_OVERHEAD = 28
@@ -387,6 +391,19 @@ def test_rate_limit(self):
387
391
1024 - PER_MESSAGE_OVERHEAD - len (tag ) - len (message .format (0 ))
388
392
) + "\n "
389
393
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
+
390
407
# See _android_support.py. The default values of these parameters work
391
408
# well across a wide range of devices, but we'll use smaller values to
392
409
# ensure a quick and reliable test that doesn't flood the log too much.
@@ -395,21 +412,24 @@ def test_rate_limit(self):
395
412
with (
396
413
patch ("_android_support.MAX_BYTES_PER_SECOND" , MAX_KB_PER_SECOND * 1024 ),
397
414
patch ("_android_support.BUCKET_SIZE" , BUCKET_KB * 1024 ),
415
+ patch ("_android_support.sleep" , mock_sleep ),
416
+ patch ("_android_support.time" , mock_time ),
398
417
):
399
418
# 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 )
401
421
line_num = 0
402
422
403
423
# Write BUCKET_KB messages, and return the rate at which they were
404
424
# accepted in KB per second.
405
425
def write_bucketful ():
406
426
nonlocal line_num
407
- start = time ()
427
+ start = mock_time ()
408
428
max_line_num = line_num + BUCKET_KB
409
429
while line_num < max_line_num :
410
430
stream .write (message .format (line_num ))
411
431
line_num += 1
412
- return BUCKET_KB / (time () - start )
432
+ return BUCKET_KB / (mock_time () - start )
413
433
414
434
# The first bucketful should be written with minimal delay. The
415
435
# factor of 2 here is not arbitrary: it verifies that the system can
@@ -427,5 +447,5 @@ def write_bucketful():
427
447
)
428
448
429
449
# 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 )
431
451
self .assertGreater (write_bucketful (), MAX_KB_PER_SECOND * 2 )
0 commit comments