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 29 import StringIO 30 import optparse 31 import sys 32 import time 33 import unittest 34 35 from webkitpy.common.system import executive_mock 36 from webkitpy.common.system.executive_mock import MockExecutive2 37 from webkitpy.common.system.systemhost_mock import MockSystemHost 38 39 from webkitpy.layout_tests.port import android 40 from webkitpy.layout_tests.port import port_testcase 41 from webkitpy.layout_tests.port import driver 42 from webkitpy.layout_tests.port import driver_unittest 43 from webkitpy.tool.mocktool import MockOptions 44 45 # Type of tombstone test which the mocked Android Debug Bridge should execute. 46 VALID_TOMBSTONE_TEST_TYPE = 0 47 NO_FILES_TOMBSTONE_TEST_TYPE = 1 48 NO_PERMISSION_TOMBSTONE_TEST_TYPE = 2 49 INVALID_ENTRY_TOMBSTONE_TEST_TYPE = 3 50 INVALID_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. 54 class 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 114 class 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 154 class 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 200 class 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 222 class 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 238 class 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 253 class 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