• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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