mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
gh-116622: Mock the passage of time in Android logcat rate limit tests (#124015)
Mock the passage of time in Android logcat rate limit tests Co-authored-by: Russell Keith-Magee <russell@keith-magee.com>
This commit is contained in:
parent
584cdf8d41
commit
f554883425
2 changed files with 30 additions and 7 deletions
|
@ -165,7 +165,10 @@ class Logcat:
|
||||||
now = time()
|
now = time()
|
||||||
self._bucket_level += (
|
self._bucket_level += (
|
||||||
(now - self._prev_write_time) * MAX_BYTES_PER_SECOND)
|
(now - self._prev_write_time) * MAX_BYTES_PER_SECOND)
|
||||||
self._bucket_level = min(self._bucket_level, BUCKET_SIZE)
|
|
||||||
|
# If the bucket level is still below zero, the clock must have gone
|
||||||
|
# backwards, so reset it to zero and continue.
|
||||||
|
self._bucket_level = max(0, min(self._bucket_level, BUCKET_SIZE))
|
||||||
self._prev_write_time = now
|
self._prev_write_time = now
|
||||||
|
|
||||||
self._bucket_level -= PER_MESSAGE_OVERHEAD + len(tag) + len(message)
|
self._bucket_level -= PER_MESSAGE_OVERHEAD + len(tag) + len(message)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from array import array
|
||||||
from contextlib import ExitStack, contextmanager
|
from contextlib import ExitStack, contextmanager
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from test.support import LOOPBACK_TIMEOUT
|
from test.support import LOOPBACK_TIMEOUT
|
||||||
from time import sleep, time
|
from time import time
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +42,8 @@ class TestAndroidOutput(unittest.TestCase):
|
||||||
for line in self.logcat_process.stdout:
|
for line in self.logcat_process.stdout:
|
||||||
self.logcat_queue.put(line.rstrip("\n"))
|
self.logcat_queue.put(line.rstrip("\n"))
|
||||||
self.logcat_process.stdout.close()
|
self.logcat_process.stdout.close()
|
||||||
Thread(target=logcat_thread).start()
|
self.logcat_thread = Thread(target=logcat_thread)
|
||||||
|
self.logcat_thread.start()
|
||||||
|
|
||||||
from ctypes import CDLL, c_char_p, c_int
|
from ctypes import CDLL, c_char_p, c_int
|
||||||
android_log_write = getattr(CDLL("liblog.so"), "__android_log_write")
|
android_log_write = getattr(CDLL("liblog.so"), "__android_log_write")
|
||||||
|
@ -78,6 +79,7 @@ class TestAndroidOutput(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.logcat_process.terminate()
|
self.logcat_process.terminate()
|
||||||
self.logcat_process.wait(LOOPBACK_TIMEOUT)
|
self.logcat_process.wait(LOOPBACK_TIMEOUT)
|
||||||
|
self.logcat_thread.join(LOOPBACK_TIMEOUT)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def unbuffered(self, stream):
|
def unbuffered(self, stream):
|
||||||
|
@ -369,6 +371,8 @@ class TestAndroidOutput(unittest.TestCase):
|
||||||
):
|
):
|
||||||
stream.write(obj)
|
stream.write(obj)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAndroidRateLimit(unittest.TestCase):
|
||||||
def test_rate_limit(self):
|
def test_rate_limit(self):
|
||||||
# https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:system/logging/liblog/include/log/log_read.h;l=39
|
# https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:system/logging/liblog/include/log/log_read.h;l=39
|
||||||
PER_MESSAGE_OVERHEAD = 28
|
PER_MESSAGE_OVERHEAD = 28
|
||||||
|
@ -387,6 +391,19 @@ class TestAndroidOutput(unittest.TestCase):
|
||||||
1024 - PER_MESSAGE_OVERHEAD - len(tag) - len(message.format(0))
|
1024 - PER_MESSAGE_OVERHEAD - len(tag) - len(message.format(0))
|
||||||
) + "\n"
|
) + "\n"
|
||||||
|
|
||||||
|
# To avoid depending on the performance of the test device, we mock the
|
||||||
|
# passage of time.
|
||||||
|
mock_now = time()
|
||||||
|
|
||||||
|
def mock_time():
|
||||||
|
# Avoid division by zero by simulating a small delay.
|
||||||
|
mock_sleep(0.0001)
|
||||||
|
return mock_now
|
||||||
|
|
||||||
|
def mock_sleep(duration):
|
||||||
|
nonlocal mock_now
|
||||||
|
mock_now += duration
|
||||||
|
|
||||||
# See _android_support.py. The default values of these parameters work
|
# See _android_support.py. The default values of these parameters work
|
||||||
# well across a wide range of devices, but we'll use smaller values to
|
# well across a wide range of devices, but we'll use smaller values to
|
||||||
# ensure a quick and reliable test that doesn't flood the log too much.
|
# ensure a quick and reliable test that doesn't flood the log too much.
|
||||||
|
@ -395,21 +412,24 @@ class TestAndroidOutput(unittest.TestCase):
|
||||||
with (
|
with (
|
||||||
patch("_android_support.MAX_BYTES_PER_SECOND", MAX_KB_PER_SECOND * 1024),
|
patch("_android_support.MAX_BYTES_PER_SECOND", MAX_KB_PER_SECOND * 1024),
|
||||||
patch("_android_support.BUCKET_SIZE", BUCKET_KB * 1024),
|
patch("_android_support.BUCKET_SIZE", BUCKET_KB * 1024),
|
||||||
|
patch("_android_support.sleep", mock_sleep),
|
||||||
|
patch("_android_support.time", mock_time),
|
||||||
):
|
):
|
||||||
# Make sure the token bucket is full.
|
# Make sure the token bucket is full.
|
||||||
sleep(BUCKET_KB / MAX_KB_PER_SECOND)
|
stream.write("Initial message to reset _prev_write_time")
|
||||||
|
mock_sleep(BUCKET_KB / MAX_KB_PER_SECOND)
|
||||||
line_num = 0
|
line_num = 0
|
||||||
|
|
||||||
# Write BUCKET_KB messages, and return the rate at which they were
|
# Write BUCKET_KB messages, and return the rate at which they were
|
||||||
# accepted in KB per second.
|
# accepted in KB per second.
|
||||||
def write_bucketful():
|
def write_bucketful():
|
||||||
nonlocal line_num
|
nonlocal line_num
|
||||||
start = time()
|
start = mock_time()
|
||||||
max_line_num = line_num + BUCKET_KB
|
max_line_num = line_num + BUCKET_KB
|
||||||
while line_num < max_line_num:
|
while line_num < max_line_num:
|
||||||
stream.write(message.format(line_num))
|
stream.write(message.format(line_num))
|
||||||
line_num += 1
|
line_num += 1
|
||||||
return BUCKET_KB / (time() - start)
|
return BUCKET_KB / (mock_time() - start)
|
||||||
|
|
||||||
# The first bucketful should be written with minimal delay. The
|
# The first bucketful should be written with minimal delay. The
|
||||||
# factor of 2 here is not arbitrary: it verifies that the system can
|
# factor of 2 here is not arbitrary: it verifies that the system can
|
||||||
|
@ -427,5 +447,5 @@ class TestAndroidOutput(unittest.TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Once the token bucket refills, we should go back to full speed.
|
# Once the token bucket refills, we should go back to full speed.
|
||||||
sleep(BUCKET_KB / MAX_KB_PER_SECOND)
|
mock_sleep(BUCKET_KB / MAX_KB_PER_SECOND)
|
||||||
self.assertGreater(write_bucketful(), MAX_KB_PER_SECOND * 2)
|
self.assertGreater(write_bucketful(), MAX_KB_PER_SECOND * 2)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue