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