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