Home | History | Annotate | Download | only in port
      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