Title: [124481] trunk/Tools
Revision
124481
Author
[email protected]
Date
2012-08-02 11:46:24 -0700 (Thu, 02 Aug 2012)

Log Message

[Chromium-Android] Run layout tests on multiple devices in parallel
https://bugs.webkit.org/show_bug.cgi?id=92877

Reviewed by Dirk Pranke.

Moved methods that run command on a particular device from ChromiumAndroidPort to ChromiumAndroidDriver.
The drivers run adb commands with the '-s serial_number' parameter which specifies the device according to the work_number.

* Scripts/webkitpy/layout_tests/port/chromium_android.py:
(ChromiumAndroidPort.__init__):
(ChromiumAndroidPort.default_child_processes): Default to the number of attached devices.
(ChromiumAndroidPort.test_expectations): Removed because it is unused.
(ChromiumAndroidPort.setup_test_run): Most contents moved into ChromiumAndroidDriver._setup_test()
(ChromiumAndroidPort.clean_up_test_run): Now the http server is stopped here.
(ChromiumAndroidPort._get_devices):
(ChromiumAndroidPort._get_device_serial):
(ChromiumAndroidDriver):
(ChromiumAndroidDriver.__init__):
(ChromiumAndroidDriver.__del__):
(ChromiumAndroidDriver._setup_test): Original contents of ChromiumAndroidPort.setup_test_run().
(ChromiumAndroidDriver._push_executable): Moved from ChromiumAndroidPort.
(ChromiumAndroidDriver._push_fonts): Moved from ChromiumAndroidPort.
(ChromiumAndroidDriver._push_test_resources): Moved from ChromiumAndroidPort.
(ChromiumAndroidDriver._synchronize_datetime): Moved from ChromiumAndroidPort.
(ChromiumAndroidDriver._run_adb_command): Moved from ChromiumAndroidPort.
(ChromiumAndroidDriver._teardown_performance): Moved from ChromiumAndroidPort.
(ChromiumAndroidDriver._get_crash_log): Moved from ChromiumAndroidPort.
(ChromiumAndroidDriver.cmd_line):
(ChromiumAndroidDriver._file_exists_on_device):
(ChromiumAndroidDriver._remove_all_pipes):
(ChromiumAndroidDriver._start):
(ChromiumAndroidDriver._start_once):
(ChromiumAndroidDriver.stop):
* Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py:
(MockRunCommand):
(MockRunCommand.__init__):
(MockRunCommand.mock_run_command_fn):
(MockRunCommand.mock_no_device):
(MockRunCommand.mock_one_device):
(MockRunCommand.mock_two_devices):
(MockRunCommand.mock_no_tombstone_dir):
(MockRunCommand.mock_no_tombstone_file):
(MockRunCommand.mock_ten_tombstones):
(MockRunCommand.mock_logcat):
(ChromiumAndroidPortTest):
(ChromiumAndroidPortTest.make_port):
(ChromiumAndroidPortTest.test_driver_cmd_line):
(ChromiumAndroidPortTest.test_get_devices_no_device):
(ChromiumAndroidPortTest.test_get_devices_one_device):
(ChromiumAndroidPortTest.test_get_devices_two_devices):
(ChromiumAndroidPortTest.test_get_device_serial_no_device):
(ChromiumAndroidPortTest.test_get_device_serial_one_device):
(ChromiumAndroidPortTest.test_get_device_serial_two_devices):
(ChromiumAndroidDriverTest):
(ChromiumAndroidDriverTest.setUp):
(ChromiumAndroidDriverTest.test_get_last_stacktrace):
(ChromiumAndroidDriverTest.test_get_crash_log):
(ChromiumAndroidDriverTest.test_cmd_line):
(ChromiumAndroidDriverTwoDriversTest):
(ChromiumAndroidDriverTwoDriversTest.test_two_drivers):
* Scripts/webkitpy/layout_tests/port/driver.py:
(Driver.run_test):
(Driver._get_crash_log): Added to allow subclasses to override.
* Scripts/webkitpy/layout_tests/run_webkit_tests.py:
(parse_args): Removed the --adb-args command-line parameter because now we select device automatically. Added --adb-device to specify devices.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (124480 => 124481)


--- trunk/Tools/ChangeLog	2012-08-02 18:41:47 UTC (rev 124480)
+++ trunk/Tools/ChangeLog	2012-08-02 18:46:24 UTC (rev 124481)
@@ -1,3 +1,71 @@
+2012-08-02  Xianzhu Wang  <[email protected]>
+
+        [Chromium-Android] Run layout tests on multiple devices in parallel
+        https://bugs.webkit.org/show_bug.cgi?id=92877
+
+        Reviewed by Dirk Pranke.
+
+        Moved methods that run command on a particular device from ChromiumAndroidPort to ChromiumAndroidDriver.
+        The drivers run adb commands with the '-s serial_number' parameter which specifies the device according to the work_number.
+
+        * Scripts/webkitpy/layout_tests/port/chromium_android.py:
+        (ChromiumAndroidPort.__init__):
+        (ChromiumAndroidPort.default_child_processes): Default to the number of attached devices.
+        (ChromiumAndroidPort.test_expectations): Removed because it is unused. 
+        (ChromiumAndroidPort.setup_test_run): Most contents moved into ChromiumAndroidDriver._setup_test()
+        (ChromiumAndroidPort.clean_up_test_run): Now the http server is stopped here.
+        (ChromiumAndroidPort._get_devices):
+        (ChromiumAndroidPort._get_device_serial):
+        (ChromiumAndroidDriver):
+        (ChromiumAndroidDriver.__init__):
+        (ChromiumAndroidDriver.__del__):
+        (ChromiumAndroidDriver._setup_test): Original contents of ChromiumAndroidPort.setup_test_run().
+        (ChromiumAndroidDriver._push_executable): Moved from ChromiumAndroidPort.
+        (ChromiumAndroidDriver._push_fonts): Moved from ChromiumAndroidPort.
+        (ChromiumAndroidDriver._push_test_resources): Moved from ChromiumAndroidPort.
+        (ChromiumAndroidDriver._synchronize_datetime): Moved from ChromiumAndroidPort.
+        (ChromiumAndroidDriver._run_adb_command): Moved from ChromiumAndroidPort.
+        (ChromiumAndroidDriver._teardown_performance): Moved from ChromiumAndroidPort.
+        (ChromiumAndroidDriver._get_crash_log): Moved from ChromiumAndroidPort.
+        (ChromiumAndroidDriver.cmd_line):
+        (ChromiumAndroidDriver._file_exists_on_device):
+        (ChromiumAndroidDriver._remove_all_pipes):
+        (ChromiumAndroidDriver._start):
+        (ChromiumAndroidDriver._start_once):
+        (ChromiumAndroidDriver.stop):
+        * Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py:
+        (MockRunCommand):
+        (MockRunCommand.__init__):
+        (MockRunCommand.mock_run_command_fn):
+        (MockRunCommand.mock_no_device):
+        (MockRunCommand.mock_one_device):
+        (MockRunCommand.mock_two_devices):
+        (MockRunCommand.mock_no_tombstone_dir):
+        (MockRunCommand.mock_no_tombstone_file):
+        (MockRunCommand.mock_ten_tombstones):
+        (MockRunCommand.mock_logcat):
+        (ChromiumAndroidPortTest):
+        (ChromiumAndroidPortTest.make_port):
+        (ChromiumAndroidPortTest.test_driver_cmd_line):
+        (ChromiumAndroidPortTest.test_get_devices_no_device):
+        (ChromiumAndroidPortTest.test_get_devices_one_device):
+        (ChromiumAndroidPortTest.test_get_devices_two_devices):
+        (ChromiumAndroidPortTest.test_get_device_serial_no_device):
+        (ChromiumAndroidPortTest.test_get_device_serial_one_device):
+        (ChromiumAndroidPortTest.test_get_device_serial_two_devices):
+        (ChromiumAndroidDriverTest):
+        (ChromiumAndroidDriverTest.setUp):
+        (ChromiumAndroidDriverTest.test_get_last_stacktrace):
+        (ChromiumAndroidDriverTest.test_get_crash_log):
+        (ChromiumAndroidDriverTest.test_cmd_line):
+        (ChromiumAndroidDriverTwoDriversTest):
+        (ChromiumAndroidDriverTwoDriversTest.test_two_drivers):
+        * Scripts/webkitpy/layout_tests/port/driver.py:
+        (Driver.run_test):
+        (Driver._get_crash_log): Added to allow subclasses to override.
+        * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+        (parse_args): Removed the --adb-args command-line parameter because now we select device automatically. Added --adb-device to specify devices.
+
 2012-08-02  Dinu Jacob  <[email protected]>
 
         WebKitTestRunner needs layoutTestController.setUserStyleSheetEnabled

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py (124480 => 124481)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py	2012-08-02 18:41:47 UTC (rev 124480)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py	2012-08-02 18:46:24 UTC (rev 124481)
@@ -29,7 +29,7 @@
 
 import logging
 import os
-import shlex
+import re
 import threading
 import time
 
@@ -167,15 +167,13 @@
 
         self._operating_system = 'android'
         self._version = 'icecreamsandwich'
-        self._original_governor = None
-        self._android_base_dir = None
 
         self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
 
-        self._adb_command = ['adb']
-        adb_args = self.get_option('adb_args')
-        if adb_args:
-            self._adb_command += shlex.split(adb_args)
+        if hasattr(self._options, 'adb_device'):
+            self._devices = self._options.adb_device
+        else:
+            self._devices = []
 
     def default_timeout_ms(self):
         # Android platform has less computing power than desktop platforms.
@@ -184,8 +182,7 @@
         return 10 * 1000
 
     def default_child_processes(self):
-        # Because of the nature of apk, we don't support more than one process.
-        return 1
+        return len(self._get_devices())
 
     def baseline_search_path(self):
         return map(self._webkit_baseline_path, self.FALLBACK_PATHS)
@@ -217,15 +214,6 @@
         android_expectations_file = self.path_from_webkit_base('LayoutTests', 'platform', 'chromium', 'test_expectations_android.txt')
         return super(ChromiumAndroidPort, self).expectations_files() + [android_expectations_file]
 
-    def test_expectations(self):
-        # Automatically apply all expectation rules of chromium-linux to
-        # chromium-android.
-        # FIXME: This is a temporary measure to reduce the manual work when
-        # updating WebKit. This method should be removed when we merge
-        # test_expectations_android.txt into TestExpectations.
-        expectations = super(ChromiumAndroidPort, self).test_expectations()
-        return expectations.replace('LINUX ', 'LINUX ANDROID ')
-
     def start_http_server(self, additional_dirs=None, number_of_servers=0):
         # The http server runs during the whole testing period, so ignore this call.
         pass
@@ -235,33 +223,11 @@
         pass
 
     def setup_test_run(self):
-        self._run_adb_command(['root'])
-        self._setup_performance()
-        # Required by webkit_support::GetWebKitRootDirFilePath().
-        # Other directories will be created automatically by adb push.
-        self._run_adb_command(['shell', 'mkdir', '-p', DEVICE_SOURCE_ROOT_DIR + 'chrome'])
-        # Allow the DumpRenderTree app to fully access the directory.
-        # The native code needs the permission to write temporary files here.
-        self._run_adb_command(['shell', 'chmod', '777', DEVICE_SOURCE_ROOT_DIR])
-
-        self._push_executable()
-        self._push_fonts()
-        self._synchronize_datetime()
-
-        # Delete the disk cache if any to ensure a clean test run.
-        # This is like what's done in ChromiumPort.setup_test_run but on the device.
-        self._run_adb_command(['shell', 'rm', '-r', DRT_APP_CACHE_DIR])
-
         # Start the HTTP server so that the device can access the test cases.
         super(ChromiumAndroidPort, self).start_http_server(additional_dirs={TEST_PATH_PREFIX: self.layout_tests_dir()})
 
-        _log.debug('Starting forwarder')
-        self._run_adb_command(['shell', '%s %s' % (DEVICE_FORWARDER_PATH, FORWARD_PORTS)])
-
     def clean_up_test_run(self):
-        # Leave the forwarder and tests httpd server there because they are
-        # useful for debugging and do no harm to subsequent tests.
-        self._teardown_performance()
+        super(ChromiumAndroidPort, self).stop_http_server()
 
     def skipped_layout_tests(self, test_list):
         return self._real_tests([
@@ -319,20 +285,67 @@
     def _driver_class(self):
         return ChromiumAndroidDriver
 
-    def _get_crash_log(self, name, pid, stdout, stderr, newer_than):
-        if not stdout:
-            stdout = ''
-        stdout += '********* Logcat:\n' + self._get_logcat()
-        if not stderr:
-            stderr = ''
-        stderr += '********* Tombstone file:\n' + self._get_last_stacktrace()
-        return super(ChromiumAndroidPort, self)._get_crash_log(name, pid, stdout, stderr, newer_than)
-
     # Local private functions.
 
+    def _get_devices(self):
+        if not self._devices:
+            re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
+            result = self._executive.run_command(['adb', 'devices'], error_handler=self._executive.ignore_error)
+            self._devices = re_device.findall(result)
+            if not self._devices:
+                raise AssertionError('No devices attached. Result of "adb devices": %s' % result)
+        return self._devices
+
+    def _get_device_serial(self, worker_number):
+        devices = self._get_devices()
+        if worker_number >= len(devices):
+            raise AssertionError('Worker number exceeds available number of devices')
+        return devices[worker_number]
+
+
+class ChromiumAndroidDriver(driver.Driver):
+    def __init__(self, port, worker_number, pixel_tests, no_timeout=False):
+        super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_tests, no_timeout)
+        self._pixel_tests = pixel_tests
+        self._in_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.in'
+        self._out_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.out'
+        self._err_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.err'
+        self._read_stdout_process = None
+        self._read_stderr_process = None
+        self._forwarder_process = None
+        self._has_setup = False
+        self._original_governor = None
+        self._adb_command = ['adb', '-s', port._get_device_serial(worker_number)]
+
+    def __del__(self):
+        self._teardown_performance()
+        super(ChromiumAndroidDriver, self).__del__()
+
+    def _setup_test(self):
+        if self._has_setup:
+            return
+
+        self._has_setup = True
+        self._run_adb_command(['root'])
+        self._setup_performance()
+        # Required by webkit_support::GetWebKitRootDirFilePath().
+        # Other directories will be created automatically by adb push.
+        self._run_adb_command(['shell', 'mkdir', '-p', DEVICE_SOURCE_ROOT_DIR + 'chrome'])
+        # Allow the DumpRenderTree app to fully access the directory.
+        # The native code needs the permission to write temporary files here.
+        self._run_adb_command(['shell', 'chmod', '777', DEVICE_SOURCE_ROOT_DIR])
+
+        self._push_executable()
+        self._push_fonts()
+        self._synchronize_datetime()
+
+        # Delete the disk cache if any to ensure a clean test run.
+        # This is like what's done in ChromiumPort.setup_test_run but on the device.
+        self._run_adb_command(['shell', 'rm', '-r', DRT_APP_CACHE_DIR])
+
     def _push_executable(self):
-        drt_host_path = self._path_to_driver()
-        forwarder_host_path = self._path_to_forwarder()
+        drt_host_path = self._port._path_to_driver()
+        forwarder_host_path = self._port._path_to_forwarder()
         host_stamp = int(float(max(os.stat(drt_host_path).st_mtime,
                                    os.stat(forwarder_host_path).st_mtime)))
         device_stamp = int(float(self._run_adb_command([
@@ -344,10 +357,10 @@
             install_result = self._run_adb_command(['install', drt_host_path])
             if install_result.find('Success') == -1:
                 raise AssertionError('Failed to install %s onto device: %s' % (drt_host_path, install_result))
-            self._push_to_device(self._build_path('DumpRenderTree.pak'), DEVICE_DRT_DIR + 'DumpRenderTree.pak')
-            self._push_to_device(self._build_path('DumpRenderTree_resources'), DEVICE_DRT_DIR + 'DumpRenderTree_resources')
-            self._push_to_device(self._build_path('android_main_fonts.xml'), DEVICE_DRT_DIR + 'android_main_fonts.xml')
-            self._push_to_device(self._build_path('android_fallback_fonts.xml'), DEVICE_DRT_DIR + 'android_fallback_fonts.xml')
+            self._push_to_device(self._port._build_path('DumpRenderTree.pak'), DEVICE_DRT_DIR + 'DumpRenderTree.pak')
+            self._push_to_device(self._port._build_path('DumpRenderTree_resources'), DEVICE_DRT_DIR + 'DumpRenderTree_resources')
+            self._push_to_device(self._port._build_path('android_main_fonts.xml'), DEVICE_DRT_DIR + 'android_main_fonts.xml')
+            self._push_to_device(self._port._build_path('android_fallback_fonts.xml'), DEVICE_DRT_DIR + 'android_fallback_fonts.xml')
             # Version control of test resources is dependent on executables,
             # because we will always rebuild executables when resources are
             # updated.
@@ -357,7 +370,7 @@
     def _push_fonts(self):
         if not self._check_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION):
             _log.debug('Pushing fonts')
-            path_to_ahem_font = self._build_path('AHEM____.TTF')
+            path_to_ahem_font = self._port._build_path('AHEM____.TTF')
             self._push_to_device(path_to_ahem_font, DEVICE_FONTS_DIR + 'AHEM____.TTF')
             for (host_dir, font_file) in HOST_FONT_FILES:
                 self._push_to_device(host_dir + font_file, DEVICE_FONTS_DIR + font_file)
@@ -367,14 +380,14 @@
     def _push_test_resources(self):
         _log.debug('Pushing test resources')
         for resource in TEST_RESOURCES_TO_PUSH:
-            self._push_to_device(self.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource)
+            self._push_to_device(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource)
 
     def _synchronize_datetime(self):
         # The date/time between host and device may not be synchronized.
         # We need to make them synchronized, otherwise tests might fail.
         try:
             # Get seconds since 1970-01-01 00:00:00 UTC.
-            host_datetime = self._executive.run_command(['date', '-u', '+%s'])
+            host_datetime = self._port._executive.run_command(['date', '-u', '+%s'])
         except:
             # Reset to 1970-01-01 00:00:00 UTC.
             host_datetime = 0
@@ -394,10 +407,10 @@
     def _run_adb_command(self, cmd, ignore_error=False):
         _log.debug('Run adb command: ' + str(cmd))
         if ignore_error:
-            error_handler = self._executive.ignore_error
+            error_handler = self._port._executive.ignore_error
         else:
             error_handler = None
-        result = self._executive.run_command(self._adb_command + cmd, error_handler=error_handler)
+        result = self._port._executive.run_command(self._adb_command + cmd, error_handler=error_handler)
         # Limit the length to avoid too verbose output of commands like 'adb logcat' and 'cat /data/tombstones/tombstone01'
         # whose outputs are normally printed in later logs.
         _log.debug('Run adb result: ' + result[:80])
@@ -453,27 +466,25 @@
             self._run_adb_command(['shell', 'echo', self._original_governor, SCALING_GOVERNOR])
         self._original_governor = None
 
-
-class ChromiumAndroidDriver(driver.Driver):
-    def __init__(self, port, worker_number, pixel_tests, no_timeout=False):
-        super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_tests, no_timeout)
-        self._pixel_tests = pixel_tests
-        self._in_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.in'
-        self._out_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.out'
-        self._err_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.err'
-        self._read_stdout_process = None
-        self._read_stderr_process = None
-
     def _command_wrapper(cls, wrapper_option):
         # Ignore command wrapper which is not applicable on Android.
         return []
 
+    def _get_crash_log(self, stdout, stderr, newer_than):
+        if not stdout:
+            stdout = ''
+        stdout += '********* Logcat:\n' + self._get_logcat()
+        if not stderr:
+            stderr = ''
+        stderr += '********* Tombstone file:\n' + self._get_last_stacktrace()
+        return super(ChromiumAndroidDriver, self)._get_crash_log(stdout, stderr, newer_than)
+
     def cmd_line(self, pixel_tests, per_test_args):
-        return self._port._adb_command + ['shell']
+        return self._adb_command + ['shell']
 
     def _file_exists_on_device(self, full_file_path):
         assert full_file_path.startswith('/')
-        return self._port._run_adb_command(['shell', 'ls', full_file_path]).strip() == full_file_path
+        return self._run_adb_command(['shell', 'ls', full_file_path]).strip() == full_file_path
 
     def _drt_cmd_line(self, pixel_tests, per_test_args):
         return driver.Driver.cmd_line(self, pixel_tests, per_test_args) + [
@@ -496,7 +507,7 @@
                 self._file_exists_on_device(self._err_fifo_path))
 
     def _remove_all_pipes(self):
-        self._port._run_adb_command(['shell', 'rm', self._in_fifo_path, self._out_fifo_path, self._err_fifo_path])
+        self._run_adb_command(['shell', 'rm', self._in_fifo_path, self._out_fifo_path, self._err_fifo_path])
         return (not self._file_exists_on_device(self._in_fifo_path) and
                 not self._file_exists_on_device(self._out_fifo_path) and
                 not self._file_exists_on_device(self._err_fifo_path))
@@ -510,10 +521,12 @@
         super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args)
 
     def _start(self, pixel_tests, per_test_args):
+        self._setup_test()
+
         for retries in range(3):
             if self._start_once(pixel_tests, per_test_args):
                 return
-            _log.error('Failed to start DumpRenderTree application. Retries=%d. Log:%s' % (retries, self._port._get_logcat()))
+            _log.error('Failed to start DumpRenderTree application. Retries=%d. Log:%s' % (retries, self._get_logcat()))
             self.stop()
             time.sleep(2)
         raise AssertionError('Failed to start DumpRenderTree application multiple times. Give up.')
@@ -521,9 +534,14 @@
     def _start_once(self, pixel_tests, per_test_args):
         super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args)
 
-        self._port._run_adb_command(['logcat', '-c'])
-        self._port._run_adb_command(['shell', 'echo'] + self._drt_cmd_line(pixel_tests, per_test_args) + ['>', COMMAND_LINE_FILE])
-        start_result = self._port._run_adb_command(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', DRT_ACTIVITY_FULL_NAME])
+        _log.debug('Starting forwarder')
+        self._forwarder_process = server_process.ServerProcess(
+            self._port, 'Forwarder', self._adb_command + ['shell', '%s -D %s' % (DEVICE_FORWARDER_PATH, FORWARD_PORTS)])
+        self._forwarder_process.start()
+
+        self._run_adb_command(['logcat', '-c'])
+        self._run_adb_command(['shell', 'echo'] + self._drt_cmd_line(pixel_tests, per_test_args) + ['>', COMMAND_LINE_FILE])
+        start_result = self._run_adb_command(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', DRT_ACTIVITY_FULL_NAME])
         if start_result.find('Exception') != -1:
             _log.error('Failed to start DumpRenderTree application. Exception:\n' + start_result)
             return False
@@ -540,13 +558,13 @@
         # Start a process to read from the stdout fifo of the DumpRenderTree app and print to stdout.
         _log.debug('Redirecting stdout to ' + self._out_fifo_path)
         self._read_stdout_process = server_process.ServerProcess(
-            self._port, 'ReadStdout', self._port._adb_command + ['shell', 'cat', self._out_fifo_path], universal_newlines=True)
+            self._port, 'ReadStdout', self._adb_command + ['shell', 'cat', self._out_fifo_path], universal_newlines=True)
         self._read_stdout_process.start()
 
         # Start a process to read from the stderr fifo of the DumpRenderTree app and print to stdout.
         _log.debug('Redirecting stderr to ' + self._err_fifo_path)
         self._read_stderr_process = server_process.ServerProcess(
-            self._port, 'ReadStderr', self._port._adb_command + ['shell', 'cat', self._err_fifo_path], universal_newlines=True)
+            self._port, 'ReadStderr', self._adb_command + ['shell', 'cat', self._err_fifo_path], universal_newlines=True)
         self._read_stderr_process.start()
 
         _log.debug('Redirecting stdin to ' + self._in_fifo_path)
@@ -587,7 +605,7 @@
             return True
 
     def stop(self):
-        self._port._run_adb_command(['shell', 'am', 'force-stop', DRT_APP_PACKAGE])
+        self._run_adb_command(['shell', 'am', 'force-stop', DRT_APP_PACKAGE])
 
         if self._read_stdout_process:
             self._read_stdout_process.kill()
@@ -604,6 +622,10 @@
             self._server_process = None
         super(ChromiumAndroidDriver, self).stop()
 
+        if self._forwarder_process:
+            self._forwarder_process.stop(kill_directly=True)
+            self._forwarder_process = None
+
         if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pipes, DRT_START_STOP_TIMEOUT_SECS):
             raise AssertionError('Failed to remove fifo files. May be locked.')
 

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py (124480 => 124481)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py	2012-08-02 18:41:47 UTC (rev 124480)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py	2012-08-02 18:46:24 UTC (rev 124481)
@@ -41,11 +41,80 @@
 from webkitpy.layout_tests.port import driver_unittest
 
 
+class MockRunCommand(object):
+    def __init__(self):
+        self._mock_logcat = ''
+        self._mock_devices_output = ''
+        self._mock_devices = []
+        self._mock_ls_tombstones = ''
+
+    def mock_run_command_fn(self, args):
+        if args[0] != 'adb':
+            return ''
+        if args[1] == 'devices':
+            return self._mock_devices_output
+
+        assert len(args) > 3
+        assert args[1] == '-s'
+        assert args[2] in self._mock_devices
+        if args[3] == 'shell':
+            if args[4:] == ['ls', '-n', '/data/tombstones']:
+                return self._mock_ls_tombstones
+            elif args[4] == 'cat':
+                return args[5] + '\nmock_contents\n'
+        elif args[3] == 'logcat':
+            return self._mock_logcat
+        return ''
+
+    def mock_no_device(self):
+        self._mock_devices = []
+        self._mock_devices_output = 'List of devices attached'
+
+    def mock_one_device(self):
+        self._mock_devices = ['123456789ABCDEF0']
+        self._mock_devices_output = ('List of devices attached\n'
+                                     '%s\tdevice\n' % self._mock_devices[0])
+
+    def mock_two_devices(self):
+        self._mock_devices = ['123456789ABCDEF0', '23456789ABCDEF01']
+        self._mock_devices_output = ('* daemon not running. starting it now on port 5037 *'
+                                     '* daemon started successfully *'
+                                     'List of devices attached\n'
+                                     '%s\tdevice\n'
+                                     '%s\tdevice\n' % (self._mock_devices[0], self._mock_devices[1]))
+
+    def mock_no_tombstone_dir(self):
+        self._mock_ls_tombstones = '/data/tombstones: No such file or directory'
+
+    def mock_no_tombstone_file(self):
+        self._mock_ls_tombstones = ''
+
+    def mock_ten_tombstones(self):
+        self._mock_ls_tombstones = ('-rw------- 1000     1000       218643 2012-04-26 18:15 tombstone_00\n'
+                                    '-rw------- 1000     1000       241695 2012-04-26 18:15 tombstone_01\n'
+                                    '-rw------- 1000     1000       219472 2012-04-26 18:16 tombstone_02\n'
+                                    '-rw------- 1000     1000        45316 2012-04-27 16:33 tombstone_03\n'
+                                    '-rw------- 1000     1000        82022 2012-04-23 16:57 tombstone_04\n'
+                                    '-rw------- 1000     1000        82015 2012-04-23 16:57 tombstone_05\n'
+                                    '-rw------- 1000     1000        81974 2012-04-23 16:58 tombstone_06\n'
+                                    '-rw------- 1000     1000       237409 2012-04-26 17:41 tombstone_07\n'
+                                    '-rw------- 1000     1000       276089 2012-04-26 18:15 tombstone_08\n'
+                                    '-rw------- 1000     1000       219618 2012-04-26 18:15 tombstone_09\n')
+
+    def mock_logcat(self, content):
+        self._mock_logcat = content
+
+
 class ChromiumAndroidPortTest(chromium_port_testcase.ChromiumPortTestCase):
     port_name = 'chromium-android'
     port_maker = chromium_android.ChromiumAndroidPort
-    mock_logcat = ''
 
+    def make_port(self, **kwargs):
+        port = super(ChromiumAndroidPortTest, self).make_port(**kwargs)
+        self.mock_run_command = MockRunCommand()
+        port._executive = MockExecutive2(run_command_fn=self.mock_run_command.mock_run_command_fn)
+        return port
+
     def test_attributes(self):
         port = self.make_port()
         self.assertEquals(port.baseline_path(), port._webkit_baseline_path('chromium-android'))
@@ -59,51 +128,73 @@
         # using a custom expectations file.
         pass
 
-    @staticmethod
-    def mock_run_command_fn(args):
-        if args[1] == 'shell':
-            if args[2:] == ['ls', '-n', '/data/tombstones']:
-                # For 'adb shell ls -n /data/tombstones'
-                return ('-rw------- 1000     1000       218643 2012-04-26 18:15 tombstone_00\n'
-                        '-rw------- 1000     1000       241695 2012-04-26 18:15 tombstone_01\n'
-                        '-rw------- 1000     1000       219472 2012-04-26 18:16 tombstone_02\n'
-                        '-rw------- 1000     1000        45316 2012-04-27 16:33 tombstone_03\n'
-                        '-rw------- 1000     1000        82022 2012-04-23 16:57 tombstone_04\n'
-                        '-rw------- 1000     1000        82015 2012-04-23 16:57 tombstone_05\n'
-                        '-rw------- 1000     1000        81974 2012-04-23 16:58 tombstone_06\n'
-                        '-rw------- 1000     1000       237409 2012-04-26 17:41 tombstone_07\n'
-                        '-rw------- 1000     1000       276089 2012-04-26 18:15 tombstone_08\n'
-                        '-rw------- 1000     1000       219618 2012-04-26 18:15 tombstone_09\n')
-            elif args[2] == 'cat':
-                return args[3] + '\nmock_contents\n'
-        elif args[1] == 'logcat':
-            return ChromiumAndroidPortTest.mock_logcat
-        else:
-            return ''
+    def test_driver_cmd_line(self):
+        # Overriding PortTestCase.test_cmd_line(). Use ChromiumAndroidDriverTest.test_cmd_line() instead.
+        return
 
-    def test_get_last_stacktrace(self):
+    def test_get_devices_no_device(self):
         port = self.make_port()
+        self.mock_run_command.mock_no_device()
+        self.assertRaises(AssertionError, port._get_devices)
 
-        def mock_run_command_no_dir(args):
-            return '/data/tombstones: No such file or directory'
-        port._executive = MockExecutive2(run_command_fn=mock_run_command_no_dir)
-        self.assertEquals(port._get_last_stacktrace(), '')
+    def test_get_devices_one_device(self):
+        port = self.make_port()
+        self.mock_run_command.mock_one_device()
+        self.assertEquals(self.mock_run_command._mock_devices, port._get_devices())
+        self.assertEquals(1, port.default_child_processes())
 
-        def mock_run_command_no_file(args):
-            return ''
-        port._executive = MockExecutive2(run_command_fn=mock_run_command_no_file)
-        self.assertEquals(port._get_last_stacktrace(), '')
+    def test_get_devices_two_devices(self):
+        port = self.make_port()
+        self.mock_run_command.mock_two_devices()
+        self.assertEquals(self.mock_run_command._mock_devices, port._get_devices())
+        self.assertEquals(2, port.default_child_processes())
 
-        port._executive = MockExecutive2(run_command_fn=ChromiumAndroidPortTest.mock_run_command_fn)
-        self.assertEquals(port._get_last_stacktrace(),
+    def test_get_device_serial_no_device(self):
+        port = self.make_port()
+        self.mock_run_command.mock_no_device()
+        self.assertRaises(AssertionError, port._get_device_serial, 0)
+
+    def test_get_device_serial_one_device(self):
+        port = self.make_port()
+        self.mock_run_command.mock_one_device()
+        self.assertEquals(self.mock_run_command._mock_devices[0], port._get_device_serial(0))
+        self.assertRaises(AssertionError, port._get_device_serial, 1)
+
+    def test_get_device_serial_two_devices(self):
+        port = self.make_port()
+        self.mock_run_command.mock_two_devices()
+        self.assertEquals(self.mock_run_command._mock_devices[0], port._get_device_serial(0))
+        self.assertEquals(self.mock_run_command._mock_devices[1], port._get_device_serial(1))
+        self.assertRaises(AssertionError, port._get_device_serial, 2)
+
+
+class ChromiumAndroidDriverTest(unittest.TestCase):
+    def setUp(self):
+        self.mock_run_command = MockRunCommand()
+        self.mock_run_command.mock_one_device()
+        self.port = chromium_android.ChromiumAndroidPort(
+                MockSystemHost(executive=MockExecutive2(run_command_fn=self.mock_run_command.mock_run_command_fn)),
+                'chromium-android')
+        self.driver = chromium_android.ChromiumAndroidDriver(self.port, worker_number=0, pixel_tests=True)
+
+    def test_get_last_stacktrace(self):
+        self.mock_run_command.mock_no_tombstone_dir()
+        self.assertEquals(self.driver._get_last_stacktrace(), '')
+
+        self.mock_run_command.mock_no_tombstone_file()
+        self.assertEquals(self.driver._get_last_stacktrace(), '')
+
+        self.mock_run_command.mock_ten_tombstones()
+        self.assertEquals(self.driver._get_last_stacktrace(),
                           '-rw------- 1000 1000 45316 2012-04-27 16:33 tombstone_03\n'
                           '/data/tombstones/tombstone_03\nmock_contents\n')
 
     def test_get_crash_log(self):
-        port = self.make_port()
-        port._executive = MockExecutive2(run_command_fn=ChromiumAndroidPortTest.mock_run_command_fn)
-        ChromiumAndroidPortTest.mock_logcat = 'logcat contents\n'
-        self.assertEquals(port._get_crash_log('foo', 1234, 'out bar\nout baz\n', 'err bar\nerr baz\n', newer_than=None),
+        self.mock_run_command.mock_logcat('logcat contents\n')
+        self.mock_run_command.mock_ten_tombstones()
+        self.driver._crashed_process_name = 'foo'
+        self.driver._crashed_pid = 1234
+        self.assertEquals(self.driver._get_crash_log('out bar\nout baz\n', 'err bar\nerr baz\n', newer_than=None),
             ('err bar\n'
              'err baz\n'
              '********* Tombstone file:\n'
@@ -121,7 +212,10 @@
              u'STDERR: -rw------- 1000 1000 45316 2012-04-27 16:33 tombstone_03\n'
              u'STDERR: /data/tombstones/tombstone_03\n'
              u'STDERR: mock_contents\n'))
-        self.assertEquals(port._get_crash_log(None, None, None, None, newer_than=None),
+
+        self.driver._crashed_process_name = None
+        self.driver._crashed_pid = None
+        self.assertEquals(self.driver._get_crash_log(None, None, newer_than=None),
             ('********* Tombstone file:\n'
              '-rw------- 1000 1000 45316 2012-04-27 16:33 tombstone_03\n'
              '/data/tombstones/tombstone_03\n'
@@ -134,19 +228,9 @@
              u'STDERR: /data/tombstones/tombstone_03\n'
              u'STDERR: mock_contents\n'))
 
-    def test_driver_cmd_line(self):
-        # Overriding PortTestCase.test_cmd_line(). Use ChromiumAndroidDriverTest.test_cmd_line() instead.
-        return
-
-
-class ChromiumAndroidDriverTest(unittest.TestCase):
-    def setUp(self):
-        mock_port = chromium_android.ChromiumAndroidPort(MockSystemHost(), 'chromium-android')
-        self.driver = chromium_android.ChromiumAndroidDriver(mock_port, worker_number=0, pixel_tests=True)
-
     def test_cmd_line(self):
         cmd_line = self.driver.cmd_line(True, ['anything'])
-        self.assertEquals(['adb', 'shell'], cmd_line)
+        self.assertEquals(['adb', '-s', self.mock_run_command._mock_devices[0], 'shell'], cmd_line)
 
     def test_drt_cmd_line(self):
         cmd_line = self.driver._drt_cmd_line(True, ['--a'])
@@ -171,5 +255,22 @@
         self.assertEquals(self.driver._command_from_driver_input(driver_input), expected_command)
 
 
+class ChromiumAndroidDriverTwoDriversTest(unittest.TestCase):
+    def test_two_drivers(self):
+        mock_run_command = MockRunCommand()
+        mock_run_command.mock_two_devices()
+        port = chromium_android.ChromiumAndroidPort(
+                MockSystemHost(executive=MockExecutive2(run_command_fn=mock_run_command.mock_run_command_fn)),
+                'chromium-android')
+        driver0 = chromium_android.ChromiumAndroidDriver(port, worker_number=0, pixel_tests=True)
+        driver1 = chromium_android.ChromiumAndroidDriver(port, worker_number=1, pixel_tests=True)
+
+        cmd_line0 = driver0.cmd_line(True, ['anything'])
+        self.assertEquals(['adb', '-s', mock_run_command._mock_devices[0], 'shell'], cmd_line0)
+
+        cmd_line1 = driver1.cmd_line(True, ['anything'])
+        self.assertEquals(['adb', '-s', mock_run_command._mock_devices[1], 'shell'], cmd_line1)
+
+
 if __name__ == '__main__':
     unittest.main()

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/driver.py (124480 => 124481)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/driver.py	2012-08-02 18:41:47 UTC (rev 124480)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/driver.py	2012-08-02 18:46:24 UTC (rev 124481)
@@ -166,8 +166,7 @@
 
         crash_log = None
         if self.has_crashed():
-            self.error_from_test, crash_log = self._port._get_crash_log(self._crashed_process_name,
-                self._crashed_pid, text, self.error_from_test, newer_than=start_time)
+            self.error_from_test, crash_log = self._get_crash_log(text, self.error_from_test, newer_than=start_time)
 
             # If we don't find a crash log use a placeholder error message instead.
             if not crash_log:
@@ -188,6 +187,9 @@
             crashed_process_name=self._crashed_process_name,
             crashed_pid=self._crashed_pid, crash_log=crash_log)
 
+    def _get_crash_log(self, stdout, stderr, newer_than):
+        return self._port._get_crash_log(self._crashed_process_name, self._crashed_pid, stdout, stderr, newer_than)
+
     # FIXME: Seems this could just be inlined into callers.
     @classmethod
     def _command_wrapper(cls, wrapper_option):

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py (124480 => 124481)


--- trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py	2012-08-02 18:41:47 UTC (rev 124480)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py	2012-08-02 18:46:24 UTC (rev 124481)
@@ -246,8 +246,9 @@
         optparse.make_option("--per-tile-painting",
             action=""
             help="Use per-tile painting of composited pages"),
-        optparse.make_option("--adb-args", type="string",
-            help="Arguments parsed to Android adb, to select device, etc."),
+        optparse.make_option("--adb-device",
+            action="" default=[],
+            help="Run Android layout tests on these devices."),
     ]))
 
     option_group_definitions.append(("EFL-specific Options", [
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to