1#!/usr/bin/env python3 2 3from itertools import product 4from time import sleep 5 6import pipes 7import re 8import subprocess 9import sys 10import textwrap 11import unittest 12 13BITNESS_32 = ("", "32") 14BITNESS_64 = ("64", "64") 15 16APP_PROCESS_FOR_PRETTY_BITNESS = 'app_process%s' 17CPP_TEST_SERVICE_FOR_BITNESS = ' /data/nativetest%s/aidl_test_service/aidl_test_service%s' 18CPP_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_client/aidl_test_client%s' 19CPP_TEST_V1_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_v1_client/aidl_test_v1_client%s' 20NDK_TEST_SERVICE_FOR_BITNESS = ' /data/nativetest%s/aidl_test_service_ndk/aidl_test_service_ndk%s' 21NDK_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_client_ndk/aidl_test_client_ndk%s' 22RUST_TEST_CLIENT_FOR_BITNESS = ' /data/nativetest%s/aidl_test_rust_client/aidl_test_rust_client%s' 23RUST_TEST_SERVICE_FOR_BITNESS = ' /data/nativetest%s/aidl_test_rust_service/aidl_test_rust_service%s' 24RUST_TEST_SERVICE_ASYNC_FOR_BITNESS = ' /data/nativetest%s/aidl_test_rust_service_async/aidl_test_rust_service_async%s' 25 26# From AidlTestsJava.java 27INSTRUMENTATION_SUCCESS_PATTERN = r'TEST SUCCESS\n$' 28 29class ShellResultFail(Exception): 30 """Raised on test failures.""" 31 def __init__(self, err): 32 stderr = textwrap.indent(err.stderr, " ") 33 stdout = textwrap.indent(err.stdout, " ") 34 35 super().__init__(f"STDOUT:\n{stdout}\nSTDERR:\n{stderr}\nRESULT:{err.exit_status}") 36 37def pretty_bitness(bitness): 38 """Returns a human readable version of bitness, corresponding to BITNESS_* variable""" 39 return bitness[-1] 40 41class ShellResult(object): 42 """Represents the result of running a shell command.""" 43 44 def __init__(self, exit_status, stdout, stderr): 45 """Construct an instance. 46 47 Args: 48 exit_status: integer exit code of shell command 49 stdout: string stdout of shell command 50 stderr: string stderr of shell command 51 """ 52 self.stdout = stdout 53 self.stderr = stderr 54 self.exit_status = exit_status 55 56 def printable_string(self): 57 """Get a string we could print to the logs and understand.""" 58 output = [] 59 output.append('stdout:') 60 for line in self.stdout.splitlines(): 61 output.append(' > %s' % line) 62 output.append('stderr:') 63 for line in self.stderr.splitlines(): 64 output.append(' > %s' % line) 65 return '\n'.join(output) 66 67 68class AdbHost(object): 69 """Represents a device connected via ADB.""" 70 71 def run(self, command, background=False, ignore_status=False): 72 """Run a command on the device via adb shell. 73 74 Args: 75 command: string containing a shell command to run. 76 background: True iff we should run this command in the background. 77 ignore_status: True iff we should ignore the command's exit code. 78 79 Returns: 80 instance of ShellResult. 81 82 Raises: 83 subprocess.CalledProcessError on command exit != 0. 84 """ 85 if background: 86 command = '( %s ) </dev/null >/dev/null 2>&1 &' % command 87 return self.adb('shell %s' % pipes.quote(command), 88 ignore_status=ignore_status) 89 90 def adb(self, command, ignore_status=False): 91 """Run an ADB command (e.g. `adb sync`). 92 93 Args: 94 command: string containing command to run 95 ignore_status: True iff we should ignore the command's exit code. 96 97 Returns: 98 instance of ShellResult. 99 100 Raises: 101 subprocess.CalledProcessError on command exit != 0. 102 """ 103 command = 'adb %s' % command 104 p = subprocess.Popen(command, shell=True, close_fds=True, 105 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 106 universal_newlines=True) 107 stdout, stderr = p.communicate() 108 if not ignore_status and p.returncode: 109 raise subprocess.CalledProcessError(p.returncode, command) 110 return ShellResult(p.returncode, stdout, stderr) 111 112class NativeServer: 113 def cleanup(self): 114 self.host.run('killall %s' % self.binary, ignore_status=True) 115 def run(self): 116 return self.host.run(self.binary, background=True) 117 118class NativeClient: 119 def cleanup(self): 120 self.host.run('killall %s' % self.binary, ignore_status=True) 121 def run(self): 122 result = self.host.run(self.binary + ' --gtest_color=yes', ignore_status=True) 123 print(result.printable_string()) 124 if result.exit_status: 125 raise ShellResultFail(result) 126 127class CppServer(NativeServer): 128 def __init__(self, host, bitness): 129 self.name = "%s_bit_cpp_server" % pretty_bitness(bitness) 130 self.host = host 131 self.binary = CPP_TEST_SERVICE_FOR_BITNESS % bitness 132 133class CppClient(NativeClient): 134 def __init__(self, host, bitness): 135 self.name = "%s_bit_cpp_client" % pretty_bitness(bitness) 136 self.host = host 137 self.binary = CPP_TEST_CLIENT_FOR_BITNESS % bitness 138 139class CppV1Client(NativeClient): 140 def __init__(self, host, bitness): 141 self.name = "%s_bit_cpp_v1_client" % pretty_bitness(bitness) 142 self.host = host 143 self.binary = CPP_TEST_V1_CLIENT_FOR_BITNESS % bitness 144 145class NdkServer(NativeServer): 146 def __init__(self, host, bitness): 147 self.name = "%s_bit_ndk_server" % pretty_bitness(bitness) 148 self.host = host 149 self.binary = NDK_TEST_SERVICE_FOR_BITNESS % bitness 150 151class NdkClient(NativeClient): 152 def __init__(self, host, bitness): 153 self.name = "%s_bit_ndk_client" % pretty_bitness(bitness) 154 self.host = host 155 self.binary = NDK_TEST_CLIENT_FOR_BITNESS % bitness 156 157class JavaServer: 158 def __init__(self, host, bitness): 159 self.name = "java_server_%s" % pretty_bitness(bitness) 160 self.host = host 161 self.bitness = bitness 162 def cleanup(self): 163 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 164 ignore_status=True) 165 def run(self): 166 return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service.jar ' 167 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 168 ' /data/framework android.aidl.service.TestServiceServer', 169 background=True) 170 171class JavaClient: 172 def __init__(self, host, bitness): 173 self.name = "java_client_%s" % pretty_bitness(bitness) 174 self.host = host 175 self.bitness = bitness 176 def cleanup(self): 177 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 178 ignore_status=True) 179 def run(self): 180 result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client.jar ' 181 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 182 ' /data/framework android.aidl.tests.AidlJavaTests') 183 print(result.printable_string()) 184 if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: 185 raise ShellResultFail(result) 186 187class JavaVersionTestClient: 188 def __init__(self, host, bitness, ver): 189 self.name = "java_client_sdk%d_%s" % (ver, pretty_bitness(bitness)) 190 self.host = host 191 self.bitness = bitness 192 self.ver = ver 193 def cleanup(self): 194 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 195 ignore_status=True) 196 def run(self): 197 result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client_sdk%d.jar ' % self.ver 198 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 199 ' /data/framework android.aidl.sdkversion.tests.AidlJavaVersionTests') 200 print(result.printable_string()) 201 if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: 202 raise ShellResultFail(result) 203 204class JavaVersionTestServer: 205 def __init__(self, host, bitness, ver): 206 self.name = "java_server_sdk%s_%s" % (ver, pretty_bitness(bitness)) 207 self.host = host 208 self.bitness = bitness 209 self.ver = ver 210 def cleanup(self): 211 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 212 ignore_status=True) 213 def run(self): 214 return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service_sdk%d.jar ' % self.ver 215 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 216 ' /data/framework android.aidl.sdkversion.service.AidlJavaVersionTestService', 217 background=True) 218 219class JavaPermissionClient: 220 def __init__(self, host, bitness): 221 self.name = "java_client_permission_%s" % pretty_bitness(bitness) 222 self.host = host 223 self.bitness = bitness 224 def cleanup(self): 225 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 226 ignore_status=True) 227 def run(self): 228 result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client_permission.jar ' 229 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 230 ' /data/framework android.aidl.permission.tests.PermissionTests') 231 print(result.printable_string()) 232 if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: 233 raise ShellResultFail(result) 234 235class JavaPermissionServer: 236 def __init__(self, host, bitness): 237 self.name = "java_server_permission_%s" % pretty_bitness(bitness) 238 self.host = host 239 self.bitness = bitness 240 def cleanup(self): 241 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 242 ignore_status=True) 243 def run(self): 244 return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service_permission.jar ' 245 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 246 ' /data/framework android.aidl.permission.service.PermissionTestService', 247 background=True) 248 249def getprop(host, prop): 250 return host.run('getprop "%s"' % prop).stdout.strip() 251 252class RustClient: 253 def __init__(self, host, bitness): 254 self.name = "%s_bit_rust_client" % pretty_bitness(bitness) 255 self.host = host 256 self.binary = RUST_TEST_CLIENT_FOR_BITNESS % bitness 257 def cleanup(self): 258 self.host.run('killall %s' % self.binary, ignore_status=True) 259 def run(self): 260 result = self.host.run(self.binary, ignore_status=True) 261 print(result.printable_string()) 262 if result.exit_status: 263 raise ShellResultFail(result) 264 265class RustServer: 266 def __init__(self, host, bitness): 267 self.name = "%s_bit_rust_server" % pretty_bitness(bitness) 268 self.host = host 269 self.binary = RUST_TEST_SERVICE_FOR_BITNESS % bitness 270 def cleanup(self): 271 self.host.run('killall %s' % self.binary, ignore_status=True) 272 def run(self): 273 return self.host.run(self.binary, background=True) 274 275class RustAsyncServer: 276 def __init__(self, host, bitness): 277 self.name = "%s_bit_rust_server_async" % pretty_bitness(bitness) 278 self.host = host 279 self.binary = RUST_TEST_SERVICE_ASYNC_FOR_BITNESS % bitness 280 def cleanup(self): 281 self.host.run('killall %s' % self.binary, ignore_status=True) 282 def run(self): 283 return self.host.run(self.binary, background=True) 284 285def supported_bitnesses(host): 286 bitnesses = [] 287 if getprop(host, "ro.product.cpu.abilist32") != "": 288 bitnesses += [BITNESS_32] 289 if getprop(host, "ro.product.cpu.abilist64") != "": 290 bitnesses += [BITNESS_64] 291 return bitnesses 292 293# tests added dynamically below 294class TestAidl(unittest.TestCase): 295 pass 296 297def make_test(client, server): 298 def test(self): 299 try: 300 # Server is unregistered first so that we give more time 301 # for servicemanager to clear the old notification. 302 # Otherwise, it may race that the client gets ahold 303 # of the service. 304 server.cleanup() 305 client.cleanup() 306 sleep(0.2) 307 308 server.run() 309 client.run() 310 finally: 311 client.cleanup() 312 server.cleanup() 313 return test 314 315def add_test(client, server): 316 test_name = 'test_%s_to_%s' % (client.name, server.name) 317 test = make_test(client, server) 318 setattr(TestAidl, test_name, test) 319 320if __name__ == '__main__': 321 host = AdbHost() 322 bitnesses = supported_bitnesses(host) 323 if len(bitnesses) == 0: 324 print("No clients installed") 325 exit(1) 326 327 clients = [] 328 servers = [] 329 330 for bitness in bitnesses: 331 clients += [NdkClient(host, bitness)] 332 servers += [NdkServer(host, bitness)] 333 334 clients += [CppClient(host, bitness)] 335 clients += [CppV1Client(host, bitness)] 336 servers += [CppServer(host, bitness)] 337 338 clients += [JavaClient(host, bitness)] 339 servers += [JavaServer(host, bitness)] 340 341 clients += [RustClient(host, bitness)] 342 servers += [RustServer(host, bitness)] 343 servers += [RustAsyncServer(host, bitness)] 344 345 for client in clients: 346 for server in servers: 347 add_test(client, server) 348 349 # boolean: >= 29 350 # typed: >= 23 351 versions = [1, 29] 352 for c_version, c_bitness, s_version, s_bitness in product(versions, bitnesses, repeat=2): 353 client = JavaVersionTestClient(host, c_bitness, c_version) 354 server = JavaVersionTestServer(host, s_bitness, s_version) 355 add_test(client, server) 356 357 # TODO(b/218914259): Interfaces with permission are only supported for the 358 # Java backend. Once C++ and/or Rust are supported, move the test back into 359 # JavaClient and JavaServer. 360 for bitness in bitnesses: 361 add_test(JavaPermissionClient(host, bitness), JavaPermissionServer(host, bitness)) 362 363 suite = unittest.TestLoader().loadTestsFromTestCase(TestAidl) 364 sys.exit(not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()) 365