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=None, 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 # outer redirection to /dev/null required to avoid subprocess.Popen blocking 87 # on the FDs being closed 88 command = '(( %s ) </dev/null 2>&1 | log -t %s &) >/dev/null 2>&1' % (command, background) 89 return self.adb('shell %s' % pipes.quote(command), 90 ignore_status=ignore_status) 91 92 def adb(self, command, ignore_status=False): 93 """Run an ADB command (e.g. `adb sync`). 94 95 Args: 96 command: string containing command to run 97 ignore_status: True iff we should ignore the command's exit code. 98 99 Returns: 100 instance of ShellResult. 101 102 Raises: 103 subprocess.CalledProcessError on command exit != 0. 104 """ 105 command = 'adb %s' % command 106 p = subprocess.Popen(command, shell=True, close_fds=True, 107 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 108 universal_newlines=True) 109 stdout, stderr = p.communicate() 110 if not ignore_status and p.returncode: 111 raise subprocess.CalledProcessError(p.returncode, command) 112 return ShellResult(p.returncode, stdout, stderr) 113 114class NativeServer: 115 def cleanup(self): 116 self.host.run('killall %s' % self.binary, ignore_status=True) 117 def run(self): 118 return self.host.run(self.binary, background=self.binary) 119 120class NativeClient: 121 def cleanup(self): 122 self.host.run('killall %s' % self.binary, ignore_status=True) 123 def run(self): 124 result = self.host.run(self.binary + ' --gtest_color=yes', ignore_status=True) 125 print(result.printable_string()) 126 if result.exit_status: 127 raise ShellResultFail(result) 128 129class CppServer(NativeServer): 130 def __init__(self, host, bitness): 131 self.name = "%s_bit_cpp_server" % pretty_bitness(bitness) 132 self.host = host 133 self.binary = CPP_TEST_SERVICE_FOR_BITNESS % bitness 134 135class CppClient(NativeClient): 136 def __init__(self, host, bitness): 137 self.name = "%s_bit_cpp_client" % pretty_bitness(bitness) 138 self.host = host 139 self.binary = CPP_TEST_CLIENT_FOR_BITNESS % bitness 140 141class CppV1Client(NativeClient): 142 def __init__(self, host, bitness): 143 self.name = "%s_bit_cpp_v1_client" % pretty_bitness(bitness) 144 self.host = host 145 self.binary = CPP_TEST_V1_CLIENT_FOR_BITNESS % bitness 146 147class NdkServer(NativeServer): 148 def __init__(self, host, bitness): 149 self.name = "%s_bit_ndk_server" % pretty_bitness(bitness) 150 self.host = host 151 self.binary = NDK_TEST_SERVICE_FOR_BITNESS % bitness 152 153class NdkClient(NativeClient): 154 def __init__(self, host, bitness): 155 self.name = "%s_bit_ndk_client" % pretty_bitness(bitness) 156 self.host = host 157 self.binary = NDK_TEST_CLIENT_FOR_BITNESS % bitness 158 159class JavaServer: 160 def __init__(self, host, bitness): 161 self.name = "java_server_%s" % pretty_bitness(bitness) 162 self.host = host 163 self.bitness = bitness 164 def cleanup(self): 165 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 166 ignore_status=True) 167 def run(self): 168 return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service.jar ' 169 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 170 ' /data/framework android.aidl.service.TestServiceServer', 171 background=self.name) 172 173class JavaClient: 174 def __init__(self, host, bitness): 175 self.name = "java_client_%s" % pretty_bitness(bitness) 176 self.host = host 177 self.bitness = bitness 178 def cleanup(self): 179 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 180 ignore_status=True) 181 def run(self): 182 result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client.jar ' 183 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 184 ' /data/framework android.aidl.tests.AidlJavaTests') 185 print(result.printable_string()) 186 if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: 187 raise ShellResultFail(result) 188 189class JavaVersionTestClient: 190 def __init__(self, host, bitness, ver): 191 self.name = "java_client_sdk%d_%s" % (ver, pretty_bitness(bitness)) 192 self.host = host 193 self.bitness = bitness 194 self.ver = ver 195 def cleanup(self): 196 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 197 ignore_status=True) 198 def run(self): 199 result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client_sdk%d.jar ' % self.ver 200 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 201 ' /data/framework android.aidl.sdkversion.tests.AidlJavaVersionTests') 202 print(result.printable_string()) 203 if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: 204 raise ShellResultFail(result) 205 206class JavaVersionTestServer: 207 def __init__(self, host, bitness, ver): 208 self.name = "java_server_sdk%s_%s" % (ver, pretty_bitness(bitness)) 209 self.host = host 210 self.bitness = bitness 211 self.ver = ver 212 def cleanup(self): 213 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 214 ignore_status=True) 215 def run(self): 216 return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service_sdk%d.jar ' % self.ver 217 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 218 ' /data/framework android.aidl.sdkversion.service.AidlJavaVersionTestService', 219 background=self.name) 220 221class JavaPermissionClient: 222 def __init__(self, host, bitness): 223 self.name = "java_client_permission_%s" % pretty_bitness(bitness) 224 self.host = host 225 self.bitness = bitness 226 def cleanup(self): 227 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 228 ignore_status=True) 229 def run(self): 230 result = self.host.run('CLASSPATH=/data/framework/aidl_test_java_client_permission.jar ' 231 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 232 ' /data/framework android.aidl.permission.tests.PermissionTests') 233 print(result.printable_string()) 234 if re.search(INSTRUMENTATION_SUCCESS_PATTERN, result.stdout) is None: 235 raise ShellResultFail(result) 236 237class JavaPermissionServer: 238 def __init__(self, host, bitness): 239 self.name = "java_server_permission_%s" % pretty_bitness(bitness) 240 self.host = host 241 self.bitness = bitness 242 def cleanup(self): 243 self.host.run('killall ' + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness), 244 ignore_status=True) 245 def run(self): 246 return self.host.run('CLASSPATH=/data/framework/aidl_test_java_service_permission.jar ' 247 + APP_PROCESS_FOR_PRETTY_BITNESS % pretty_bitness(self.bitness) + 248 ' /data/framework android.aidl.permission.service.PermissionTestService', 249 background=self.name) 250 251def getprop(host, prop): 252 return host.run('getprop "%s"' % prop).stdout.strip() 253 254class RustClient: 255 def __init__(self, host, bitness): 256 self.name = "%s_bit_rust_client" % pretty_bitness(bitness) 257 self.host = host 258 self.binary = RUST_TEST_CLIENT_FOR_BITNESS % bitness 259 def cleanup(self): 260 self.host.run('killall %s' % self.binary, ignore_status=True) 261 def run(self): 262 result = self.host.run(self.binary, ignore_status=True) 263 print(result.printable_string()) 264 if result.exit_status: 265 raise ShellResultFail(result) 266 267class RustServer: 268 def __init__(self, host, bitness): 269 self.name = "%s_bit_rust_server" % pretty_bitness(bitness) 270 self.host = host 271 self.binary = RUST_TEST_SERVICE_FOR_BITNESS % bitness 272 def cleanup(self): 273 self.host.run('killall %s' % self.binary, ignore_status=True) 274 def run(self): 275 return self.host.run(self.binary, background=self.name) 276 277class RustAsyncServer: 278 def __init__(self, host, bitness): 279 self.name = "%s_bit_rust_server_async" % pretty_bitness(bitness) 280 self.host = host 281 self.binary = RUST_TEST_SERVICE_ASYNC_FOR_BITNESS % bitness 282 def cleanup(self): 283 self.host.run('killall %s' % self.binary, ignore_status=True) 284 def run(self): 285 return self.host.run(self.binary, background=self.name) 286 287def supported_bitnesses(host): 288 bitnesses = [] 289 if getprop(host, "ro.product.cpu.abilist32") != "": 290 bitnesses += [BITNESS_32] 291 if getprop(host, "ro.product.cpu.abilist64") != "": 292 bitnesses += [BITNESS_64] 293 return bitnesses 294 295# tests added dynamically below 296class TestAidl(unittest.TestCase): 297 pass 298 299def make_test(client, server): 300 def test(self): 301 try: 302 # Server is unregistered first so that we give more time 303 # for servicemanager to clear the old notification. 304 # Otherwise, it may race that the client gets ahold 305 # of the service. 306 server.cleanup() 307 client.cleanup() 308 sleep(0.2) 309 310 server.run() 311 client.run() 312 finally: 313 client.cleanup() 314 server.cleanup() 315 return test 316 317def add_test(client, server): 318 test_name = 'test_%s_to_%s' % (client.name, server.name) 319 test = make_test(client, server) 320 setattr(TestAidl, test_name, test) 321 322if __name__ == '__main__': 323 host = AdbHost() 324 bitnesses = supported_bitnesses(host) 325 if len(bitnesses) == 0: 326 print("No clients installed") 327 exit(1) 328 329 clients = [] 330 servers = [] 331 332 for bitness in bitnesses: 333 clients += [NdkClient(host, bitness)] 334 servers += [NdkServer(host, bitness)] 335 336 clients += [CppClient(host, bitness)] 337 clients += [CppV1Client(host, bitness)] 338 servers += [CppServer(host, bitness)] 339 340 clients += [JavaClient(host, bitness)] 341 servers += [JavaServer(host, bitness)] 342 343 clients += [RustClient(host, bitness)] 344 servers += [RustServer(host, bitness)] 345 servers += [RustAsyncServer(host, bitness)] 346 347 for client in clients: 348 for server in servers: 349 add_test(client, server) 350 351 # boolean: >= 29 352 # typed: >= 23 353 versions = [1, 29] 354 for c_version, c_bitness, s_version, s_bitness in product(versions, bitnesses, repeat=2): 355 client = JavaVersionTestClient(host, c_bitness, c_version) 356 server = JavaVersionTestServer(host, s_bitness, s_version) 357 add_test(client, server) 358 359 # TODO(b/218914259): Interfaces with permission are only supported for the 360 # Java backend. Once C++ and/or Rust are supported, move the test back into 361 # JavaClient and JavaServer. 362 for bitness in bitnesses: 363 add_test(JavaPermissionClient(host, bitness), JavaPermissionServer(host, bitness)) 364 365 suite = unittest.TestLoader().loadTestsFromTestCase(TestAidl) 366 sys.exit(not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()) 367