| #!/usr/bin/env python3 |
| # |
| # Copyright (C) 2023 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| import re |
| import subprocess |
| import unittest |
| |
| KNOWN_NON_LOGGING_SERVICES = [ |
| "vendor.ir-default", |
| |
| "SELF_TEST_SERVICE_DOES_NOT_EXIST", |
| ] |
| |
| KNOWN_LOGGING_SERVICES = [ |
| "zygote", |
| |
| # b/210919187 - main log is too busy, gets dropped off |
| # "statsd", |
| # "vendor.audio-hal-aidl", |
| |
| "SELF_TEST_SERVICE_DOES_NOT_EXIST", |
| ] |
| |
| def device_log(log): |
| ret = subprocess.check_output(["adb", "shell", "log", "-t", "logd_integration_test", log]).decode() |
| assert len(ret) == 0, f"Expected no output, but found '{ret}'" |
| |
| def get_service_pid(svc): |
| return int(subprocess.check_output(["adb", "shell", "getprop", "init.svc_debug_pid." + svc])) |
| |
| def get_pid_logs(pid): |
| return subprocess.check_output(["adb", "logcat", "--pid", str(pid), "-d"]).decode() |
| |
| def get_product_name(): |
| return subprocess.check_output(["adb", "shell", "getprop", "ro.product.name"]).decode() |
| |
| def iter_service_pids(test_case, services): |
| a_service_worked = False |
| for service in services: |
| try: |
| yield service, get_service_pid(service) |
| a_service_worked = True |
| except subprocess.CalledProcessError: |
| continue |
| except ValueError: |
| continue |
| test_case.assertTrue(a_service_worked) |
| |
| def get_dropped_logs(test_case, buffer): |
| output = subprocess.check_output(["adb", "logcat", "-b", buffer, "--statistics"]).decode() |
| lines = iter(output.split("\n")) |
| |
| res = [] |
| |
| # Search for these lines, in order. Consider output: |
| # :) adb logcat -b system -S | grep -E "Total|Now" |
| # size/num system Total |
| # Total 883973/6792 883973/6792 |
| # Now 883973/6792 883973/6792 |
| for indication in ["Total", "Now"]: |
| reLineCount = re.compile(f"^{indication}.*\s+[0-9]+/([0-9]+)") |
| while True: |
| line = next(lines) |
| match = reLineCount.match(line) |
| if match: |
| res.append(int(match.group(1))) |
| break |
| |
| total, now = res |
| return total, now, output |
| |
| class LogdIntegrationTest(unittest.TestCase): |
| def subTest(self, subtest_name): |
| """install logger for all subtests""" |
| |
| class SubTestLogger: |
| def __init__(self, testcase, subtest_name): |
| self.subtest_name = subtest_name |
| self.subtest = testcase.subTest(subtest_name) |
| def __enter__(self): |
| device_log(f"Starting subtest {subtest_name}") |
| return self.subtest.__enter__() |
| def __exit__(self, *args): |
| device_log(f"Ending subtest {subtest_name}") |
| return self.subtest.__exit__(*args) |
| |
| return SubTestLogger(super(), subtest_name) |
| |
| def test_no_logs(self): |
| for service, pid in iter_service_pids(self, KNOWN_NON_LOGGING_SERVICES): |
| with self.subTest(service + "_no_logs"): |
| lines = get_pid_logs(pid) |
| self.assertFalse("\n" in lines, f"{service} ({pid}) shouldn't have logs, but found: {lines}") |
| |
| def test_has_logs(self): |
| for service, pid in iter_service_pids(self, KNOWN_LOGGING_SERVICES): |
| with self.subTest(service + "_has_logs"): |
| lines = get_pid_logs(pid) |
| self.assertTrue("\n" in lines, f"{service} ({pid}) should have logs, but found: {lines}") |
| |
| def test_no_dropped_logs(self): |
| dropped_buffer_allowed = { |
| "crash": 0, |
| "kernel": 0, |
| "main": 4000, |
| "system": 0 if get_product_name().startswith("aosp") else 10000, |
| } |
| |
| for buffer, allowed in dropped_buffer_allowed.items(): |
| with self.subTest(buffer + "_buffer_not_dropped"): |
| total, now, output = get_dropped_logs(self, buffer) |
| dropped = total - now |
| |
| self.assertLessEqual(dropped, allowed, |
| f"Buffer {buffer} has {dropped} dropped logs (now {now} out of {total} total logs), but expecting <= {allowed}. {output}") |
| |
| def main(): |
| unittest.main(verbosity=3) |
| |
| if __name__ == "__main__": |
| main() |