Title: [127849] trunk/Tools
Revision
127849
Author
zandober...@gmail.com
Date
2012-09-07 03:01:13 -0700 (Fri, 07 Sep 2012)

Log Message

[NRWT] XvfbDriver should stop the Xvfb more aggressively
https://bugs.webkit.org/show_bug.cgi?id=95097

Reviewed by Dirk Pranke.

The Xvfb process is now killed when the XvfbDriver is stopping. Simply
terminating the process does not always work as expected, leading to timeouts
in run-webkit-tests on the buildbot. The lock file Xvfb creates is also removed
as it's not removed by the killed Xvfb process.

Also included is a thorough unittest for XvfbDriver. Proper starting of the driver
is tested in different circumstances - whether pixel tests are enabled or not,
with an arbitrary worker number or with an Xorg process already running on the system.
The stopping of the driver is tested by checking that the Xvfb process is truly killed
and then set to None.

* BuildSlaveSupport/kill-old-processes:
Kill any stale Xvfb processes at the start of the build cycle so no Xvfb
is left running, potentially clogging up a display at a certain position.
* Scripts/webkitpy/common/system/executive_mock.py:
(MockExecutive.__init__):
The _running_pids is now a dictionary with process names as keys and pids
as values.
(MockExecutive.check_running_pid):
Check whether the passed-in pid is in the _running_pids dictionary's values.
(MockExecutive):
(MockExecutive.running_pids):
This method returns the list of all the process pids of which the name passes
through the process_name_filter.
* Scripts/webkitpy/layout_tests/port/xvfbdriver.py:
Use the Executive object of the port's host to check for running pids and open
new subprocesses or kill them throughout the class.
(XvfbDriver._start):
Use the _server_process_constructor to make testing the XvfbDriver possible. Also,
start the server process after it's created.
(XvfbDriver.stop):
Now kills the Xvfb process instead of terminating it and waiting for it to close.
* Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py: Added.
(XvfbDriverTest):
(XvfbDriverTest.make_driver):
(XvfbDriverTest.assertDriverStartSuccessful):
(XvfbDriverTest.test_start_no_pixel_tests):
(XvfbDriverTest.test_start_pixel_tests):
(XvfbDriverTest.test_start_arbitrary_worker_number):
(XvfbDriverTest.test_start_existing_xorg_process):
(XvfbDriverTest.test_stop):
(XvfbDriverTest.test_stop.FakeXvfbProcess):

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/BuildSlaveSupport/kill-old-processes (127848 => 127849)


--- trunk/Tools/BuildSlaveSupport/kill-old-processes	2012-09-07 09:59:59 UTC (rev 127848)
+++ trunk/Tools/BuildSlaveSupport/kill-old-processes	2012-09-07 10:01:13 UTC (rev 127849)
@@ -95,6 +95,7 @@
         "svn",
         "webkit_unit_tests",
         "WebKitTestRunner",
+        "Xvfb",
     ]
 
     if sys.platform == 'darwin':

Modified: trunk/Tools/ChangeLog (127848 => 127849)


--- trunk/Tools/ChangeLog	2012-09-07 09:59:59 UTC (rev 127848)
+++ trunk/Tools/ChangeLog	2012-09-07 10:01:13 UTC (rev 127849)
@@ -1,3 +1,53 @@
+2012-09-07  Zan Dobersek  <zandober...@gmail.com>
+
+        [NRWT] XvfbDriver should stop the Xvfb more aggressively
+        https://bugs.webkit.org/show_bug.cgi?id=95097
+
+        Reviewed by Dirk Pranke.
+
+        The Xvfb process is now killed when the XvfbDriver is stopping. Simply
+        terminating the process does not always work as expected, leading to timeouts
+        in run-webkit-tests on the buildbot. The lock file Xvfb creates is also removed
+        as it's not removed by the killed Xvfb process.
+
+        Also included is a thorough unittest for XvfbDriver. Proper starting of the driver
+        is tested in different circumstances - whether pixel tests are enabled or not,
+        with an arbitrary worker number or with an Xorg process already running on the system.
+        The stopping of the driver is tested by checking that the Xvfb process is truly killed
+        and then set to None.
+
+        * BuildSlaveSupport/kill-old-processes:
+        Kill any stale Xvfb processes at the start of the build cycle so no Xvfb
+        is left running, potentially clogging up a display at a certain position.
+        * Scripts/webkitpy/common/system/executive_mock.py:
+        (MockExecutive.__init__):
+        The _running_pids is now a dictionary with process names as keys and pids
+        as values.
+        (MockExecutive.check_running_pid):
+        Check whether the passed-in pid is in the _running_pids dictionary's values.
+        (MockExecutive):
+        (MockExecutive.running_pids):
+        This method returns the list of all the process pids of which the name passes
+        through the process_name_filter.
+        * Scripts/webkitpy/layout_tests/port/xvfbdriver.py:
+        Use the Executive object of the port's host to check for running pids and open
+        new subprocesses or kill them throughout the class.
+        (XvfbDriver._start):
+        Use the _server_process_constructor to make testing the XvfbDriver possible. Also,
+        start the server process after it's created.
+        (XvfbDriver.stop):
+        Now kills the Xvfb process instead of terminating it and waiting for it to close.
+        * Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py: Added.
+        (XvfbDriverTest):
+        (XvfbDriverTest.make_driver):
+        (XvfbDriverTest.assertDriverStartSuccessful):
+        (XvfbDriverTest.test_start_no_pixel_tests):
+        (XvfbDriverTest.test_start_pixel_tests):
+        (XvfbDriverTest.test_start_arbitrary_worker_number):
+        (XvfbDriverTest.test_start_existing_xorg_process):
+        (XvfbDriverTest.test_stop):
+        (XvfbDriverTest.test_stop.FakeXvfbProcess):
+
 2012-09-06  Peter Beverloo  <pe...@chromium.org>
 
         Introduce the Chromium Android Release (Tests) bot

Modified: trunk/Tools/Scripts/webkitpy/common/system/executive_mock.py (127848 => 127849)


--- trunk/Tools/Scripts/webkitpy/common/system/executive_mock.py	2012-09-07 09:59:59 UTC (rev 127848)
+++ trunk/Tools/Scripts/webkitpy/common/system/executive_mock.py	2012-09-07 10:01:13 UTC (rev 127849)
@@ -58,12 +58,21 @@
         self._should_throw = should_throw
         self._should_throw_when_run = should_throw_when_run or set()
         # FIXME: Once executive wraps os.getpid() we can just use a static pid for "this" process.
-        self._running_pids = [os.getpid()]
+        self._running_pids = {'test-webkitpy': os.getpid()}
         self._proc = None
 
     def check_running_pid(self, pid):
-        return pid in self._running_pids
+        return pid in self._running_pids.values()
 
+    def running_pids(self, process_name_filter):
+        running_pids = []
+        for process_name, process_pid in self._running_pids.iteritems():
+            if process_name_filter(process_name):
+                running_pids.append(process_pid)
+
+        log("MOCK running_pids: %s" % running_pids)
+        return running_pids
+
     def run_and_throw_if_fail(self, args, quiet=False, cwd=None, env=None):
         if self._should_log:
             env_string = ""

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver.py (127848 => 127849)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver.py	2012-09-07 09:59:59 UTC (rev 127848)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver.py	2012-09-07 10:01:13 UTC (rev 127849)
@@ -46,7 +46,7 @@
         def x_filter(process_name):
             return process_name.find("Xorg") > -1
 
-        running_displays = len(Executive().running_pids(x_filter))
+        running_displays = len(self._port.host.executive.running_pids(x_filter))
 
         # Use even displays for pixel tests and odd ones otherwise. When pixel tests are disabled,
         # DriverProxy creates two drivers, one for normal and the other for ref tests. Both have
@@ -54,23 +54,24 @@
         display_id = self._worker_number * 2 + running_displays
         if pixel_tests:
             display_id += 1
-        run_xvfb = ["Xvfb", ":%d" % (display_id), "-screen",  "0", "800x600x24", "-nolisten", "tcp"]
+        run_xvfb = ["Xvfb", ":%d" % display_id, "-screen",  "0", "800x600x24", "-nolisten", "tcp"]
         with open(os.devnull, 'w') as devnull:
-            self._xvfb_process = subprocess.Popen(run_xvfb, stderr=devnull)
+            self._xvfb_process = self._port.host.executive.popen(run_xvfb, stderr=devnull)
+        self._lock_file = "/tmp/.X%d-lock" % display_id
+
         server_name = self._port.driver_name()
         environment = self._port.setup_environ_for_server(server_name)
         # We must do this here because the DISPLAY number depends on _worker_number
         environment['DISPLAY'] = ":%d" % (display_id)
         self._crashed_process_name = None
         self._crashed_pid = None
-        self._server_process = ServerProcess(self._port, server_name, self.cmd_line(pixel_tests, per_test_args), environment)
+        self._server_process = self._port._server_process_constructor(self._port, server_name, self.cmd_line(pixel_tests, per_test_args), environment)
+        self._server_process.start()
 
     def stop(self):
         super(XvfbDriver, self).stop()
         if getattr(self, '_xvfb_process', None):
-            try:
-                self._xvfb_process.terminate()
-                self._xvfb_process.wait()
-            except OSError:
-                _log.warn("The driver is already terminated.")
+            self._port.host.executive.kill_process(self._xvfb_process.pid)
             self._xvfb_process = None
+            if self._port.host.filesystem.isfile(self._lock_file):
+                self._port.host.filesystem.remove(self._lock_file)

Added: trunk/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py (0 => 127849)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py	                        (rev 0)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py	2012-09-07 10:01:13 UTC (rev 127849)
@@ -0,0 +1,102 @@
+# Copyright (C) 2012 Zan Dobersek <zandober...@gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#    * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#    * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#    * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.system.deprecated_logging import log
+from webkitpy.common.system.filesystem_mock import MockFileSystem
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.common.system.systemhost_mock import MockSystemHost
+from webkitpy.layout_tests.port import Port
+from webkitpy.layout_tests.port.config_mock import MockConfig
+from webkitpy.layout_tests.port.server_process_mock import MockServerProcess
+from webkitpy.layout_tests.port.xvfbdriver import XvfbDriver
+
+
+class XvfbDriverTest(unittest.TestCase):
+    def make_driver(self, worker_number=0, xorg_running=False):
+        port = Port(host=MockSystemHost(log_executive=True), config=MockConfig())
+        port._server_process_constructor = MockServerProcess
+        if xorg_running:
+            port._executive._running_pids['Xorg'] = 108
+
+        driver = XvfbDriver(port, worker_number=worker_number, pixel_tests=True)
+        return driver
+
+    def cleanup_driver(self, driver):
+        # Setting _xvfb_process member to None is necessary as the Driver object is stopped on deletion,
+        # killing the Xvfb process if present. Thus, this method should only be called from tests that do not
+        # intend to test the behavior of XvfbDriver.stop.
+        driver._xvfb_process = None
+
+    def assertDriverStartSuccessful(self, driver, expected_stderr, expected_display, pixel_tests=False):
+        OutputCapture().assert_outputs(self, driver.start, [pixel_tests, []], expected_stderr=expected_stderr)
+        self.assertTrue(driver._server_process.started)
+        self.assertEqual(driver._server_process.env["DISPLAY"], expected_display)
+
+    def test_start_no_pixel_tests(self):
+        driver = self.make_driver()
+        expected_stderr = "MOCK running_pids: []\nMOCK popen: ['Xvfb', ':0', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n"
+        self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":0")
+        self.cleanup_driver(driver)
+
+    def test_start_pixel_tests(self):
+        driver = self.make_driver()
+        expected_stderr = "MOCK running_pids: []\nMOCK popen: ['Xvfb', ':1', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n"
+        self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":1", pixel_tests=True)
+        self.cleanup_driver(driver)
+
+    def test_start_arbitrary_worker_number(self):
+        driver = self.make_driver(worker_number=17)
+        expected_stderr = "MOCK running_pids: []\nMOCK popen: ['Xvfb', ':35', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n"
+        self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":35", pixel_tests=True)
+        self.cleanup_driver(driver)
+
+    def test_start_existing_xorg_process(self):
+        driver = self.make_driver(xorg_running=True)
+        expected_stderr = "MOCK running_pids: [108]\nMOCK popen: ['Xvfb', ':1', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n"
+        self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":1")
+        self.cleanup_driver(driver)
+
+    def test_stop(self):
+        filesystem = MockFileSystem(files={'/tmp/.X42-lock': '1234\n'})
+        port = Port(host=MockSystemHost(log_executive=True, filesystem=filesystem), config=MockConfig())
+        port._executive.kill_process = lambda x: log("MOCK kill_process pid: " + str(x))
+        driver = XvfbDriver(port, worker_number=0, pixel_tests=True)
+
+        class FakeXvfbProcess(object):
+            pid = 1234
+
+        driver._xvfb_process = FakeXvfbProcess()
+        driver._lock_file = '/tmp/.X42-lock'
+
+        expected_stderr = "MOCK kill_process pid: 1234\n"
+        OutputCapture().assert_outputs(self, driver.stop, [], expected_stderr=expected_stderr)
+
+        self.assertEqual(driver._xvfb_process, None)
+        self.assertFalse(port._filesystem.exists(driver._lock_file))
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to