| #!/usr/bin/env python3 |
| |
| import pipes |
| import re |
| import subprocess |
| import sys |
| import unittest |
| |
| BITNESS_32 = ("", "32") |
| BITNESS_64 = ("64", "64") |
| |
| APP_PROCESS_FOR_PRETTY_BITNESS = 'app_process%s' |
| NATIVE_TEST_SERVICE_FOR_BITNESS = ' /data/nativetest%s/aidl_test_service/aidl_test_service%s' |
| CPP_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_client/aidl_test_client%s' |
| NDK_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_client_ndk/aidl_test_client_ndk%s' |
| RUST_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_rust_client/aidl_test_rust_client%s' |
| RUST_TEST_SERVICE_FOR_BITNESS = ' /data/nativetest%s/aidl_test_rust_service/aidl_test_rust_service%s' |
| |
| # From AidlTestsJava.java |
| INSTRUMENTATION_SUCCESS_PATTERN = r'TEST SUCCESS\n$' |
| |
| class TestFail(Exception): |
| """Raised on test failures.""" |
| pass |
| |
| def pretty_bitness(bitness): |
| """Returns a human readable version of bitness, corresponding to BITNESS_* variable""" |
| return bitness[-1] |
| |
| class ShellResult(object): |
| """Represents the result of running a shell command.""" |
| |
| def __init__(self, exit_status, stdout, stderr): |
| """Construct an instance. |
| |
| Args: |
| exit_status: integer exit code of shell command |
| stdout: string stdout of shell command |
| stderr: string stderr of shell command |
| """ |
| self.stdout = stdout |
| self.stderr = stderr |
| self.exit_status = exit_status |
| |
| def printable_string(self): |
| """Get a string we could print to the logs and understand.""" |
| output = [] |
| output.append('stdout:') |
| for line in self.stdout.splitlines(): |
| output.append(' > %s' % line) |
| output.append('stderr:') |
| for line in self.stderr.splitlines(): |
| output.append(' > %s' % line) |
| return '\n'.join(output) |
| |
| |
| class AdbHost(object): |
| """Represents a device connected via ADB.""" |
| |
| def run(self, command, background=False, ignore_status=False): |
| """Run a command on the device via adb shell. |
| |
| Args: |
| command: string containing a shell command to run. |
| background: True iff we should run this command in the background. |
| ignore_status: True iff we should ignore the command's exit code. |
| |
| Returns: |
| instance of ShellResult. |
| |
| Raises: |
| subprocess.CalledProcessError on command exit != 0. |
| """ |
| if background: |
| command = '( %s ) </dev/null >/dev/null 2>&1 &' % command |
| return self.adb('shell %s' % pipes.quote(command), |
| ignore_status=ignore_status) |
| |
| def adb(self, command, ignore_status=False): |
| """Run an ADB command (e.g. `adb sync`). |
| |
| Args: |
| command: string containing command to run |
| ignore_status: True iff we should ignore the command's exit code. |
| |
| Returns: |
| instance of ShellResult. |
| |
| Raises: |
| subprocess.CalledProcessError on command exit != 0. |
| """ |
| command = 'adb %s' % command |
| p = subprocess.Popen(command, shell=True, close_fds=True, |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| universal_newlines=True) |
| stdout, stderr = p.communicate() |
| if not ignore_status and p.returncode: |
| raise subprocess.CalledProcessError(p.returncode, command) |
| return ShellResult(p.returncode, stdout, stderr) |
| |
| class NativeServer: |
| def __init__(self, host, bitness): |
| self.name = "%s_bit_native_server" % pretty_bitness(bitness) |
| self.host = host |
| self.binary = NATIVE_TEST_SERVICE_FOR_BITNESS % bitness |
| def cleanup(self): |
| self.host.run('killall %s' % self.binary, ignore_status=True) |
| def run(self): |
| return self.host.run(self.binary, background=True) |
| |
| class NativeClient: |
| def cleanup(self): |
| self.host.run('killall %s' % self.binary, ignore_status=True) |
| def run(self): |
| result = self.host.run(self.binary + ' --gtest_color=yes', ignore_status=True) |
| print(result.printable_string()) |
| if result.exit_status: |
| raise TestFail(result.stdout) |
| |
| class CppClient(NativeClient): |
| def __init__(self, host, bitness): |
| self.name = "%s_bit_cpp_client" % pretty_bitness(bitness) |
| self.host = host |
| self.binary = CPP_TEST_CLIENT_FOR_BITNESS % bitness |
| |
| class NdkClient(NativeClient): |
| def __init__(self, host, bitness): |
| self.name = "%s_bit_ndk_client" % pretty_bitness(bitness) |
| self.host = host |
| self.binary = NDK_TEST_CLIENT_FOR_BITNESS % bitness |
| |
| class JavaServer: |
| def __init__(self, host, bitness): |
| self.name = "java_server_%s" % pretty_bitness(bitness) |
| self.host = host |
| self.bitness = bitness |
| def cleanup(self): |
| self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), |
| ignore_status=True) |
| def run(self): |
| return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service.jar ' |
| + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + |
| ' /data/framework android.aidl.service.TestServiceServer', |
| background=True) |
| |
| class JavaClient: |
| def __init__(self, host, bitness): |
| self.name = "java_client_%s" % pretty_bitness(bitness) |
| self.host = host |
| self.bitness = bitness |
| def cleanup(self): |
| self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), |
| ignore_status=True) |
| def run(self): |
| result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client.jar ' |
| + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + |
| ' /data/framework android.aidl.tests.AidlJavaTests') |
| print(result.printable_string()) |
| if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: |
| raise TestFail(result.stdout) |
| |
| def getprop(host, prop): |
| return host.run('getprop "%s"' % prop).stdout.strip() |
| |
| class RustClient: |
| def __init__(self, host, bitness): |
| self.name = "%s_bit_rust_client" % pretty_bitness(bitness) |
| self.host = host |
| self.binary = RUST_TEST_CLIENT_FOR_BITNESS % bitness |
| def cleanup(self): |
| self.host.run('killall %s' % self.binary, ignore_status=True) |
| def run(self): |
| result = self.host.run(self.binary, ignore_status=True) |
| print(result.printable_string()) |
| if result.exit_status: |
| raise TestFail(result.stdout) |
| |
| class RustServer: |
| def __init__(self, host, bitness): |
| self.name = "%s_bit_rust_server" % pretty_bitness(bitness) |
| self.host = host |
| self.binary = RUST_TEST_SERVICE_FOR_BITNESS % bitness |
| def cleanup(self): |
| self.host.run('killall %s' % self.binary, ignore_status=True) |
| def run(self): |
| return self.host.run(self.binary, background=True) |
| |
| def supported_bitnesses(host): |
| bitnesses = [] |
| if getprop(host, "ro.product.cpu.abilist32") != "": |
| bitnesses += [BITNESS_32] |
| if getprop(host, "ro.product.cpu.abilist64") != "": |
| bitnesses += [BITNESS_64] |
| return bitnesses |
| |
| # tests added dynamically below |
| class TestAidl(unittest.TestCase): |
| pass |
| |
| def make_test(client, server): |
| def test(self): |
| try: |
| client.cleanup() |
| server.cleanup() |
| server.run() |
| client.run() |
| finally: |
| client.cleanup() |
| server.cleanup() |
| return test |
| |
| if __name__ == '__main__': |
| host = AdbHost() |
| bitnesses = supported_bitnesses(host) |
| if len(bitnesses) == 0: |
| print("No clients installed") |
| exit(1) |
| |
| clients = [] |
| servers = [] |
| |
| for bitness in bitnesses: |
| clients += [NdkClient(host, bitness)] |
| |
| clients += [CppClient(host, bitness)] |
| servers += [NativeServer(host, bitness)] |
| |
| clients += [JavaClient(host, bitness)] |
| servers += [JavaServer(host, bitness)] |
| |
| clients += [RustClient(host, bitness)] |
| servers += [RustServer(host, bitness)] |
| |
| for client in clients: |
| for server in servers: |
| test_name = 'test_%s_to_%s' % (client.name, server.name) |
| test = make_test(client, server) |
| setattr(TestAidl, test_name, test) |
| |
| suite = unittest.TestLoader().loadTestsFromTestCase(TestAidl) |
| sys.exit(not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()) |