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