1#!/usr/bin/env python3 2 3import pipes 4import re 5import subprocess 6import sys 7import unittest 8 9BITNESS_32 = ("", "32") 10BITNESS_64 = ("64", "64") 11 12APP_PROCESS_FOR_PRETTY_BITNESS = 'app_process%s' 13NATIVE_TEST_SERVICE_FOR_BITNESS = ' /data/nativetest%s/aidl_test_service/aidl_test_service%s' 14CPP_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_client/aidl_test_client%s' 15NDK_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_client_ndk/aidl_test_client_ndk%s' 16RUST_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_rust_client/aidl_test_rust_client%s' 17RUST_TEST_SERVICE_FOR_BITNESS = ' /data/nativetest%s/aidl_test_rust_service/aidl_test_rust_service%s' 18 19# From AidlTestsJava.java 20INSTRUMENTATION_SUCCESS_PATTERN = r'TEST SUCCESS\n$' 21 22class TestFail(Exception): 23 """Raised on test failures.""" 24 pass 25 26def pretty_bitness(bitness): 27 """Returns a human readable version of bitness, corresponding to BITNESS_* variable""" 28 return bitness[-1] 29 30class ShellResult(object): 31 """Represents the result of running a shell command.""" 32 33 def __init__(self, exit_status, stdout, stderr): 34 """Construct an instance. 35 36 Args: 37 exit_status: integer exit code of shell command 38 stdout: string stdout of shell command 39 stderr: string stderr of shell command 40 """ 41 self.stdout = stdout 42 self.stderr = stderr 43 self.exit_status = exit_status 44 45 def printable_string(self): 46 """Get a string we could print to the logs and understand.""" 47 output = [] 48 output.append('stdout:') 49 for line in self.stdout.splitlines(): 50 output.append(' > %s' % line) 51 output.append('stderr:') 52 for line in self.stderr.splitlines(): 53 output.append(' > %s' % line) 54 return '\n'.join(output) 55 56 57class AdbHost(object): 58 """Represents a device connected via ADB.""" 59 60 def run(self, command, background=False, ignore_status=False): 61 """Run a command on the device via adb shell. 62 63 Args: 64 command: string containing a shell command to run. 65 background: True iff we should run this command in the background. 66 ignore_status: True iff we should ignore the command's exit code. 67 68 Returns: 69 instance of ShellResult. 70 71 Raises: 72 subprocess.CalledProcessError on command exit != 0. 73 """ 74 if background: 75 command = '( %s ) </dev/null >/dev/null 2>&1 &' % command 76 return self.adb('shell %s' % pipes.quote(command), 77 ignore_status=ignore_status) 78 79 def adb(self, command, ignore_status=False): 80 """Run an ADB command (e.g. `adb sync`). 81 82 Args: 83 command: string containing command to run 84 ignore_status: True iff we should ignore the command's exit code. 85 86 Returns: 87 instance of ShellResult. 88 89 Raises: 90 subprocess.CalledProcessError on command exit != 0. 91 """ 92 command = 'adb %s' % command 93 p = subprocess.Popen(command, shell=True, close_fds=True, 94 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 95 universal_newlines=True) 96 stdout, stderr = p.communicate() 97 if not ignore_status and p.returncode: 98 raise subprocess.CalledProcessError(p.returncode, command) 99 return ShellResult(p.returncode, stdout, stderr) 100 101class NativeServer: 102 def __init__(self, host, bitness): 103 self.name = "%s_bit_native_server" % pretty_bitness(bitness) 104 self.host = host 105 self.binary = NATIVE_TEST_SERVICE_FOR_BITNESS % bitness 106 def cleanup(self): 107 self.host.run('killall %s' % self.binary, ignore_status=True) 108 def run(self): 109 return self.host.run(self.binary, background=True) 110 111class NativeClient: 112 def cleanup(self): 113 self.host.run('killall %s' % self.binary, ignore_status=True) 114 def run(self): 115 result = self.host.run(self.binary + ' --gtest_color=yes', ignore_status=True) 116 print(result.printable_string()) 117 if result.exit_status: 118 raise TestFail(result.stdout) 119 120class CppClient(NativeClient): 121 def __init__(self, host, bitness): 122 self.name = "%s_bit_cpp_client" % pretty_bitness(bitness) 123 self.host = host 124 self.binary = CPP_TEST_CLIENT_FOR_BITNESS % bitness 125 126class NdkClient(NativeClient): 127 def __init__(self, host, bitness): 128 self.name = "%s_bit_ndk_client" % pretty_bitness(bitness) 129 self.host = host 130 self.binary = NDK_TEST_CLIENT_FOR_BITNESS % bitness 131 132class JavaServer: 133 def __init__(self, host, bitness): 134 self.name = "java_server_%s" % pretty_bitness(bitness) 135 self.host = host 136 self.bitness = bitness 137 def cleanup(self): 138 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 139 ignore_status=True) 140 def run(self): 141 return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service.jar ' 142 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 143 ' /data/framework android.aidl.service.TestServiceServer', 144 background=True) 145 146class JavaClient: 147 def __init__(self, host, bitness): 148 self.name = "java_client_%s" % pretty_bitness(bitness) 149 self.host = host 150 self.bitness = bitness 151 def cleanup(self): 152 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 153 ignore_status=True) 154 def run(self): 155 result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client.jar ' 156 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 157 ' /data/framework android.aidl.tests.AidlJavaTests') 158 print(result.printable_string()) 159 if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: 160 raise TestFail(result.stdout) 161 162def getprop(host, prop): 163 return host.run('getprop "%s"' % prop).stdout.strip() 164 165class RustClient: 166 def __init__(self, host, bitness): 167 self.name = "%s_bit_rust_client" % pretty_bitness(bitness) 168 self.host = host 169 self.binary = RUST_TEST_CLIENT_FOR_BITNESS % bitness 170 def cleanup(self): 171 self.host.run('killall %s' % self.binary, ignore_status=True) 172 def run(self): 173 result = self.host.run(self.binary, ignore_status=True) 174 print(result.printable_string()) 175 if result.exit_status: 176 raise TestFail(result.stdout) 177 178class RustServer: 179 def __init__(self, host, bitness): 180 self.name = "%s_bit_rust_server" % pretty_bitness(bitness) 181 self.host = host 182 self.binary = RUST_TEST_SERVICE_FOR_BITNESS % bitness 183 def cleanup(self): 184 self.host.run('killall %s' % self.binary, ignore_status=True) 185 def run(self): 186 return self.host.run(self.binary, background=True) 187 188def supported_bitnesses(host): 189 bitnesses = [] 190 if getprop(host, "ro.product.cpu.abilist32") != "": 191 bitnesses += [BITNESS_32] 192 if getprop(host, "ro.product.cpu.abilist64") != "": 193 bitnesses += [BITNESS_64] 194 return bitnesses 195 196# tests added dynamically below 197class TestAidl(unittest.TestCase): 198 pass 199 200def make_test(client, server): 201 def test(self): 202 try: 203 client.cleanup() 204 server.cleanup() 205 server.run() 206 client.run() 207 finally: 208 client.cleanup() 209 server.cleanup() 210 return test 211 212if __name__ == '__main__': 213 host = AdbHost() 214 bitnesses = supported_bitnesses(host) 215 if len(bitnesses) == 0: 216 print("No clients installed") 217 exit(1) 218 219 clients = [] 220 servers = [] 221 222 for bitness in bitnesses: 223 clients += [NdkClient(host, bitness)] 224 225 clients += [CppClient(host, bitness)] 226 servers += [NativeServer(host, bitness)] 227 228 clients += [JavaClient(host, bitness)] 229 servers += [JavaServer(host, bitness)] 230 231 clients += [RustClient(host, bitness)] 232 servers += [RustServer(host, bitness)] 233 234 for client in clients: 235 for server in servers: 236 test_name = 'test_%s_to_%s' % (client.name, server.name) 237 test = make_test(client, server) 238 setattr(TestAidl, test_name, test) 239 240 suite = unittest.TestLoader().loadTestsFromTestCase(TestAidl) 241 sys.exit(not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()) 242