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