1# Copyright (C) 2012 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
7#    * Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9#    * Redistributions in binary form must reproduce the above
10# copyright notice, this list of conditions and the following disclaimer
11# in the documentation and/or other materials provided with the
12# distribution.
13#    * Neither the name of Google Inc. nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29import StringIO
30import optparse
31import sys
32import time
33import unittest
34
35from webkitpy.common.system import executive_mock
36from webkitpy.common.system.executive_mock import MockExecutive2
37from webkitpy.common.system.systemhost_mock import MockSystemHost
38
39from webkitpy.layout_tests.port import android
40from webkitpy.layout_tests.port import port_testcase
41from webkitpy.layout_tests.port import driver
42from webkitpy.layout_tests.port import driver_unittest
43from webkitpy.tool.mocktool import MockOptions
44
45# Type of tombstone test which the mocked Android Debug Bridge should execute.
46VALID_TOMBSTONE_TEST_TYPE = 0
47NO_FILES_TOMBSTONE_TEST_TYPE = 1
48NO_PERMISSION_TOMBSTONE_TEST_TYPE = 2
49INVALID_ENTRY_TOMBSTONE_TEST_TYPE = 3
50INVALID_ENTRIES_TOMBSTONE_TEST_TYPE = 4
51
52# Any "adb" commands will be interpret by this class instead of executing actual
53# commansd on the file system, which we don't want to do.
54class MockAndroidDebugBridge:
55    def __init__(self, device_count):
56        self._device_count = device_count
57        self._last_command = None
58        self._tombstone_output = None
59
60    # Local public methods.
61
62    def run_command(self, args):
63        self._last_command = ' '.join(args)
64        if args[0].startswith('path'):
65            if args[0] == 'path1':
66                return ''
67            if args[0] == 'path2':
68                return 'version 1.1'
69
70            return 'version 1.0'
71
72        if args[0] == 'adb':
73            if len(args) > 1 and args[1] == 'version':
74                return 'version 1.0'
75            if len(args) > 1 and args[1] == 'devices':
76                return self._get_device_output()
77            if len(args) > 3 and args[3] == 'command':
78                return 'mockoutput'
79            if len(args) > 3 and args[3] == 'install':
80                return 'Success'
81            if len(args) > 3 and args[3] in ('push', 'wait-for-device'):
82                return 'mockoutput'
83            if len(args) > 5 and args[5] == 'battery':
84                return 'level: 99'
85            if len(args) > 5 and args[5] == 'force-stop':
86                return 'mockoutput'
87            if len(args) > 5 and args[5] == 'power':
88                return 'mScreenOn=true'
89            if len(args) > 5 and args[4] == 'cat' and args[5].find('tombstone') != -1:
90                return 'tombstone content'
91            if len(args) > 6 and args[4] == 'ls' and args[6].find('tombstone') != -1:
92                assert self._tombstone_output, 'Tombstone output needs to have been set by the test.'
93                return self._tombstone_output
94
95        return ''
96
97    def last_command(self):
98        return self._last_command
99
100    def set_tombstone_output(self, output):
101        self._tombstone_output = output
102
103    # Local private methods.
104
105    def _get_device_output(self):
106        serials = ['123456789ABCDEF0', '123456789ABCDEF1', '123456789ABCDEF2',
107                   '123456789ABCDEF3', '123456789ABCDEF4', '123456789ABCDEF5']
108        output = 'List of devices attached\n'
109        for serial in serials[:self._device_count]:
110            output += '%s\tdevice\n' % serial
111        return output
112
113
114class AndroidCommandsTest(unittest.TestCase):
115    def setUp(self):
116        android.AndroidCommands._adb_command_path = None
117        android.AndroidCommands._adb_command_path_options = ['adb']
118
119    def make_executive(self, device_count):
120        self._mock_executive = MockAndroidDebugBridge(device_count)
121        return MockExecutive2(run_command_fn=self._mock_executive.run_command)
122
123    def make_android_commands(self, device_count, serial):
124        return android.AndroidCommands(self.make_executive(device_count), serial, debug_logging=False)
125
126    # The used adb command should include the device's serial number, and get_serial() should reflect this.
127    def test_adb_command_and_get_serial(self):
128        android_commands = self.make_android_commands(1, '123456789ABCDEF0')
129        self.assertEquals(['adb', '-s', '123456789ABCDEF0'], android_commands.adb_command())
130        self.assertEquals('123456789ABCDEF0', android_commands.get_serial())
131
132    # Running an adb command should return the command's output.
133    def test_run_command(self):
134        android_commands = self.make_android_commands(1, '123456789ABCDEF0')
135
136        output = android_commands.run(['command'])
137        self.assertEquals('adb -s 123456789ABCDEF0 command', self._mock_executive.last_command())
138        self.assertEquals('mockoutput', output)
139
140    # Test that the convenience methods create the expected commands.
141    def test_convenience_methods(self):
142        android_commands = self.make_android_commands(1, '123456789ABCDEF0')
143
144        android_commands.file_exists('/some_directory')
145        self.assertEquals('adb -s 123456789ABCDEF0 shell ls -d /some_directory', self._mock_executive.last_command())
146
147        android_commands.push('foo', 'bar')
148        self.assertEquals('adb -s 123456789ABCDEF0 push foo bar', self._mock_executive.last_command())
149
150        android_commands.pull('bar', 'foo')
151        self.assertEquals('adb -s 123456789ABCDEF0 pull bar foo', self._mock_executive.last_command())
152
153
154class AndroidPortTest(port_testcase.PortTestCase):
155    port_name = 'android'
156    port_maker = android.AndroidPort
157
158    def make_port(self, **kwargs):
159        port = super(AndroidPortTest, self).make_port(**kwargs)
160        port._mock_adb = MockAndroidDebugBridge(kwargs.get('device_count', 1))
161        port._executive = MockExecutive2(run_command_fn=port._mock_adb.run_command)
162        return port
163
164    def test_check_build(self):
165        host = MockSystemHost()
166        host.filesystem.exists = lambda p: True
167        port = self.make_port(host=host, options=MockOptions(child_processes=1))
168        port.check_build(needs_http=True, printer=port_testcase.FakePrinter())
169
170    def test_check_sys_deps(self):
171        # FIXME: Do something useful here, but testing the full logic would be hard.
172        pass
173
174    def make_wdiff_available(self, port):
175        port._wdiff_available = True
176        port._host_port._wdiff_available = True
177
178    # Test that content_shell currently is the only supported driver.
179    def test_non_content_shell_driver(self):
180        self.assertRaises(self.make_port, options=optparse.Values({'driver_name': 'foobar'}))
181
182    # Test that the number of child processes to create depends on the devices.
183    def test_default_child_processes(self):
184        port_default = self.make_port(device_count=5)
185        port_fixed_device = self.make_port(device_count=5, options=optparse.Values({'adb_device': '123456789ABCDEF9'}))
186
187        self.assertEquals(5, port_default.default_child_processes())
188        self.assertEquals(1, port_fixed_device.default_child_processes())
189
190    # Test that an HTTP server indeed is required by Android (as we serve all tests over them)
191    def test_requires_http_server(self):
192        self.assertTrue(self.make_port(device_count=1).requires_http_server())
193
194    # Tests the default timeouts for Android, which are different than the rest of Chromium.
195    def test_default_timeout_ms(self):
196        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Release'})).default_timeout_ms(), 10000)
197        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Debug'})).default_timeout_ms(), 10000)
198
199
200class ChromiumAndroidDriverTest(unittest.TestCase):
201    def setUp(self):
202        self._mock_adb = MockAndroidDebugBridge(1)
203        self._mock_executive = MockExecutive2(run_command_fn=self._mock_adb.run_command)
204
205        android_commands = android.AndroidCommands(self._mock_executive, '123456789ABCDEF0', debug_logging=False)
206        self._port = android.AndroidPort(MockSystemHost(executive=self._mock_executive), 'android')
207        self._driver = android.ChromiumAndroidDriver(self._port, worker_number=0,
208            pixel_tests=True, driver_details=android.ContentShellDriverDetails(), android_devices=self._port._devices)
209
210    # The cmd_line() method in the Android port is used for starting a shell, not the test runner.
211    def test_cmd_line(self):
212        self.assertEquals(['adb', '-s', '123456789ABCDEF0', 'shell'], self._driver.cmd_line(False, []))
213
214    # Test that the Chromium Android port can interpret Android's shell output.
215    def test_read_prompt(self):
216        self._driver._server_process = driver_unittest.MockServerProcess(lines=['root@android:/ # '])
217        self.assertIsNone(self._driver._read_prompt(time.time() + 1))
218        self._driver._server_process = driver_unittest.MockServerProcess(lines=['$ '])
219        self.assertIsNone(self._driver._read_prompt(time.time() + 1))
220
221
222class ChromiumAndroidDriverTwoDriversTest(unittest.TestCase):
223    # Test two drivers getting the right serial numbers, and that we disregard per-test arguments.
224    def test_two_drivers(self):
225        mock_adb = MockAndroidDebugBridge(2)
226        mock_executive = MockExecutive2(run_command_fn=mock_adb.run_command)
227
228        port = android.AndroidPort(MockSystemHost(executive=mock_executive), 'android')
229        driver0 = android.ChromiumAndroidDriver(port, worker_number=0, pixel_tests=True,
230            driver_details=android.ContentShellDriverDetails(), android_devices=port._devices)
231        driver1 = android.ChromiumAndroidDriver(port, worker_number=1, pixel_tests=True,
232            driver_details=android.ContentShellDriverDetails(), android_devices=port._devices)
233
234        self.assertEqual(['adb', '-s', '123456789ABCDEF0', 'shell'], driver0.cmd_line(True, []))
235        self.assertEqual(['adb', '-s', '123456789ABCDEF1', 'shell'], driver1.cmd_line(True, ['anything']))
236
237
238class ChromiumAndroidTwoPortsTest(unittest.TestCase):
239    # Test that the driver's command line indeed goes through to the driver.
240    def test_options_with_two_ports(self):
241        mock_adb = MockAndroidDebugBridge(2)
242        mock_executive = MockExecutive2(run_command_fn=mock_adb.run_command)
243
244        port0 = android.AndroidPort(MockSystemHost(executive=mock_executive),
245            'android', options=MockOptions(additional_drt_flag=['--foo=bar']))
246        port1 = android.AndroidPort(MockSystemHost(executive=mock_executive),
247            'android', options=MockOptions(driver_name='content_shell'))
248
249        self.assertEqual(1, port0.driver_cmd_line().count('--foo=bar'))
250        self.assertEqual(0, port1.driver_cmd_line().count('--create-stdin-fifo'))
251
252
253class ChromiumAndroidDriverTombstoneTest(unittest.TestCase):
254    EXPECTED_STACKTRACE = '-rw------- 1000 1000 3604 2013-11-19 16:16 tombstone_10\ntombstone content'
255
256    def setUp(self):
257        self._mock_adb = MockAndroidDebugBridge(1)
258        self._mock_executive = MockExecutive2(run_command_fn=self._mock_adb.run_command)
259
260        self._port = android.AndroidPort(MockSystemHost(executive=self._mock_executive), 'android')
261        self._driver = android.ChromiumAndroidDriver(self._port, worker_number=0,
262            pixel_tests=True, driver_details=android.ContentShellDriverDetails(), android_devices=self._port._devices)
263
264        self._errors = []
265        self._driver._log_error = lambda msg: self._errors.append(msg)
266
267        self._warnings = []
268        self._driver._log_warning = lambda msg: self._warnings.append(msg)
269
270    # Tests that we return an empty string and log an error when no tombstones could be found.
271    def test_no_tombstones_found(self):
272        self._mock_adb.set_tombstone_output('/data/tombstones/tombstone_*: No such file or directory')
273        stacktrace = self._driver._get_last_stacktrace()
274
275        self.assertEqual(1, len(self._errors))
276        self.assertEqual('The driver crashed, but no tombstone found!', self._errors[0])
277        self.assertEqual('', stacktrace)
278
279    # Tests that an empty string will be returned if we cannot read the tombstone files.
280    def test_insufficient_tombstone_permission(self):
281        self._mock_adb.set_tombstone_output('/data/tombstones/tombstone_*: Permission denied')
282        stacktrace = self._driver._get_last_stacktrace()
283
284        self.assertEqual(1, len(self._errors))
285        self.assertEqual('The driver crashed, but we could not read the tombstones!', self._errors[0])
286        self.assertEqual('', stacktrace)
287
288    # Tests that invalid "ls" output will throw a warning when listing the tombstone files.
289    def test_invalid_tombstone_list_entry_format(self):
290        self._mock_adb.set_tombstone_output('-rw------- 1000 1000 3604 2013-11-19 16:15 tombstone_00\n' +
291                                            '-- invalid entry --\n' +
292                                            '-rw------- 1000 1000 3604 2013-11-19 16:16 tombstone_10')
293        stacktrace = self._driver._get_last_stacktrace()
294
295        self.assertEqual(1, len(self._warnings))
296        self.assertEqual(ChromiumAndroidDriverTombstoneTest.EXPECTED_STACKTRACE, stacktrace)
297
298    # Tests the case in which we can't find any valid tombstone entries at all. The tombstone
299    # output used for the mock misses the permission part.
300    def test_invalid_tombstone_list(self):
301        self._mock_adb.set_tombstone_output('1000 1000 3604 2013-11-19 16:15 tombstone_00\n' +
302                                            '1000 1000 3604 2013-11-19 16:15 tombstone_01\n' +
303                                            '1000 1000 3604 2013-11-19 16:15 tombstone_02')
304        stacktrace = self._driver._get_last_stacktrace()
305
306        self.assertEqual(3, len(self._warnings))
307        self.assertEqual(1, len(self._errors))
308        self.assertEqual('The driver crashed, but we could not find any valid tombstone!', self._errors[0])
309        self.assertEqual('', stacktrace)
310
311    # Tests that valid tombstone listings will return the contents of the most recent file.
312    def test_read_valid_tombstone_file(self):
313        self._mock_adb.set_tombstone_output('-rw------- 1000 1000 3604 2013-11-19 16:15 tombstone_00\n' +
314                                            '-rw------- 1000 1000 3604 2013-11-19 16:16 tombstone_10\n' +
315                                            '-rw------- 1000 1000 3604 2013-11-19 16:15 tombstone_02')
316        stacktrace = self._driver._get_last_stacktrace()
317
318        self.assertEqual(0, len(self._warnings))
319        self.assertEqual(0, len(self._errors))
320        self.assertEqual(ChromiumAndroidDriverTombstoneTest.EXPECTED_STACKTRACE, stacktrace)
321