Title: [104768] trunk/Tools
Revision
104768
Author
[email protected]
Date
2012-01-11 16:58:52 -0800 (Wed, 11 Jan 2012)

Log Message

test-webkitpy: push more logic into webkitpy.test.main, clean up code
https://bugs.webkit.org/show_bug.cgi?id=76021

Reviewed by Eric Seidel.

I plan to add more functionality to test-webkitpy, but it is
difficult to hack on now; this patch is the first of two that
brings test-webkitpy more inline with current coding style by
pushing more logic into the webkitpy.test.main.Tester class (so
that it will be testable itself).

There should be no functional changes in this patch.

* Scripts/test-webkitpy:
(_path_from_webkit_root):
* Scripts/webkitpy/test/main.py:
(Tester):
(Tester.init):
(Tester.configure_logging):
(Tester.configure_logging.filter):
(Tester.clean_packages):
(Tester.run_tests):

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (104767 => 104768)


--- trunk/Tools/ChangeLog	2012-01-12 00:56:24 UTC (rev 104767)
+++ trunk/Tools/ChangeLog	2012-01-12 00:58:52 UTC (rev 104768)
@@ -1,3 +1,28 @@
+2012-01-10  Dirk Pranke  <[email protected]>
+
+        test-webkitpy: push more logic into webkitpy.test.main, clean up code
+        https://bugs.webkit.org/show_bug.cgi?id=76021
+
+        Reviewed by Eric Seidel.
+
+        I plan to add more functionality to test-webkitpy, but it is
+        difficult to hack on now; this patch is the first of two that
+        brings test-webkitpy more inline with current coding style by
+        pushing more logic into the webkitpy.test.main.Tester class (so
+        that it will be testable itself).
+        
+        There should be no functional changes in this patch.
+
+        * Scripts/test-webkitpy:
+        (_path_from_webkit_root):
+        * Scripts/webkitpy/test/main.py:
+        (Tester):
+        (Tester.init):
+        (Tester.configure_logging):
+        (Tester.configure_logging.filter):
+        (Tester.clean_packages):
+        (Tester.run_tests):
+
 2012-01-11  Wei Jia  <[email protected]>
 
         implement layout tests for <video> with media stream

Modified: trunk/Tools/Scripts/test-webkitpy (104767 => 104768)


--- trunk/Tools/Scripts/test-webkitpy	2012-01-12 00:56:24 UTC (rev 104767)
+++ trunk/Tools/Scripts/test-webkitpy	2012-01-12 00:58:52 UTC (rev 104768)
@@ -32,179 +32,36 @@
 import os
 import sys
 
-# We strictly limit what is imported from webkitpy until we get a chance
-# to delete any stray *.pyc files.
-import webkitpy.common.version_check
+# NOTE: We intentionally limit imports from webkitpy here to minimize the
+# chances of breaking test-webkitpy itself.
+from webkitpy.common import version_check
+from webkitpy.test import main
 
+
 _log = logging.getLogger("test-webkitpy")
 
 
-# Verbose logging is useful for debugging test-webkitpy code that runs
-# before the actual unit tests -- things like autoinstall downloading and
-# unit-test auto-detection logic.  This is different from verbose logging
-# of the unit tests themselves (i.e. the unittest module's --verbose flag).
-def configure_logging(is_verbose_logging):
-    """Configure the root logger.
-
-    Configure the root logger not to log any messages from webkitpy --
-    except for messages from the autoinstall module.  Also set the
-    logging level as described below.
-
-    Args:
-      is_verbose_logging: A boolean value of whether logging should be
-                          verbose.  If this parameter is true, the logging
-                          level for the handler on the root logger is set to
-                          logging.DEBUG.  Otherwise, it is set to logging.INFO.
-
-    """
-    if is_verbose_logging:
-        logging_level = logging.DEBUG
-    else:
-        logging_level = logging.INFO
-
-    handler = logging.StreamHandler(sys.stderr)
-    # We constrain the level on the handler rather than on the root
-    # logger itself.  This is probably better because the handler is
-    # configured and known only to this module, whereas the root logger
-    # is an object shared (and potentially modified) by many modules.
-    # Modifying the handler, then, is less intrusive and less likely to
-    # interfere with modifications made by other modules (e.g. in unit
-    # tests).
-    handler.setLevel(logging_level)
-    formatter = logging.Formatter("%(name)s: %(levelname)-8s %(message)s")
-    handler.setFormatter(formatter)
-
-    logger = logging.getLogger()
-    logger.addHandler(handler)
-    logger.setLevel(logging.NOTSET)
-
-    # Filter out most webkitpy messages.
-    #
-    # Messages can be selectively re-enabled for this script by updating
-    # this method accordingly.
-    def filter(record):
-        """Filter out autoinstall and non-third-party webkitpy messages."""
-        # FIXME: Figure out a way not to use strings here, for example by
-        #        using syntax like webkitpy.test.__name__.  We want to be
-        #        sure not to import any non-Python 2.4 code, though, until
-        #        after the version-checking code has executed.
-        if (record.name.startswith("webkitpy.common.system.autoinstall") or
-            record.name.startswith("webkitpy.test")):
-            return True
-        if record.name.startswith("webkitpy"):
-            return False
-        return True
-
-    testing_filter = logging.Filter()
-    testing_filter.filter = filter
-
-    # Display a message so developers are not mystified as to why
-    # logging does not work in the unit tests.
-    _log.info("Suppressing most webkitpy logging while running unit tests.")
-    handler.addFilter(testing_filter)
-
-
-def _clean_pyc_files(dir_to_clean):
-    """Delete from under a directory all .pyc files that have no .py file."""
-    _log.debug("Cleaning orphaned *.pyc files from: %s" % dir_to_clean)
-    for dir_path, dir_names, file_names in os.walk(dir_to_clean):
-        for file_name in file_names:
-            if file_name.endswith(".pyc") and file_name[:-1] not in file_names:
-                file_path = os.path.join(dir_path, file_name)
-                _log.info("Deleting orphan *.pyc file: %s" % file_path)
-                os.remove(file_path)
-
-
-def _clean_packages(external_package_paths):
-    webkitpy_dir = os.path.join(os.path.dirname(__file__), "webkitpy")
-    package_paths = [webkitpy_dir] + external_package_paths
-    for path in package_paths:
-        _clean_pyc_files(path)
-
-
-def init(command_args, external_package_paths):
-    """Execute code prior to importing from webkitpy.unittests.
-
-    Args:
-        command_args: The list of command-line arguments -- usually
-                      sys.argv[1:].
-
-    """
-    verbose_logging_flag = "--verbose-logging"
-    is_verbose_logging = verbose_logging_flag in command_args
-    if is_verbose_logging:
-        # Remove the flag so it doesn't cause unittest.main() to error out.
-        #
-        # FIXME: Get documentation for the --verbose-logging flag to show
-        #        up in the usage instructions, which are currently generated
-        #        by unittest.main().  It's possible that this will require
-        #        re-implementing the option parser for unittest.main()
-        #        since there may not be an easy way to modify its existing
-        #        option parser.
-        sys.argv.remove(verbose_logging_flag)
-
-    configure_logging(is_verbose_logging)
-    _log.debug("Verbose WebKit logging enabled.")
-
-    # We clean orphaned *.pyc files from the packages prior to importing from
-    # them to make sure that no import statements falsely succeed.
-    # This helps to check that import statements have been updated correctly
-    # after any file moves.  Otherwise, incorrect import statements can
-    # be masked.
-    #
-    # For example, if webkitpy/common/host.py were moved to a
-    # different location without changing any import statements, and if
-    # the corresponding .pyc file were left behind without deleting it,
-    # then "import webkitpy.common.host" would continue to succeed
-    # even though it would fail for someone checking out a fresh copy
-    # of the source tree.  This is because of a Python feature:
-    #
-    # "It is possible to have a file called spam.pyc (or spam.pyo when -O
-    # is used) without a file spam.py for the same module. This can be used
-    # to distribute a library of Python code in a form that is moderately
-    # hard to reverse engineer."
-    #
-    # ( http://docs.python.org/tutorial/modules.html#compiled-python-files )
-    #
-    # Deleting the orphaned .pyc file prior to importing, however, would
-    # cause an ImportError to occur on import as desired.
-    _clean_packages(external_package_paths)
-
-
-def _path_from_webkit_root(*components):
+if __name__ == "__main__":
     webkit_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
-    return os.path.join(webkit_root, *components)
 
+    tester = main.Tester()
+    tester.init(sys.argv[1:])
 
-def _test_import(module_path):
-    try:
-        sys.path.append(os.path.dirname(module_path))
-        module_name = os.path.basename(module_path)
-        __import__(module_name)
-        return True
-    except Exception, e:
-        message = "Skipping tests in %s due to failure (%s)." % (module_path, e)
-        if module_name.endswith("QueueStatusServer"):
-            message += "  This module is optional.  The failure is likely due to a missing Google AppEngine install.  (http://code.google.com/appengine/downloads.html)"
-        _log.warn(message)
-        return False
-
-if __name__ == "__main__":
     # FIXME: We should probably test each package separately to avoid naming conflicts.
-    external_package_paths = [
-        _path_from_webkit_root('Source', 'WebKit2', 'Scripts', 'webkit2'),
-        _path_from_webkit_root('Tools', 'QueueStatusServer'),
+    dirs = [
+        os.path.join(webkit_root, 'Tools', 'Scripts', 'webkitpy'),
+        os.path.join(webkit_root, 'Source', 'WebKit2', 'Scripts', 'webkit2'),
     ]
-    init(sys.argv[1:], external_package_paths)
 
-    # We import the unit test code after init() to ensure that any
-    # Python version warnings are displayed in case an error occurs
-    # while interpreting webkitpy.unittests.  This also allows
-    # logging to be configured prior to importing -- for example to
-    # enable the display of autoinstall logging.log messages while
-    # running the unit tests.
-    from webkitpy.test.main import Tester
+    # FIXME: Make this work on windows as well?
+    appengine_sdk_path = '/usr/local/google_appengine'
+    if os.path.exists(appengine_sdk_path) and not appengine_sdk_path in sys.path:
+        sys.path.append(appengine_sdk_path)
+    try:
+        import google.appengine
+        dirs.append(os.path.join(webkit_root, 'Tools', 'QueueStatusServer'))
+    except ImportError:
+        _log.info('Skipping QueueStatusServer tests; the Google AppEngine Python SDK is not installed.')
 
-    external_package_paths = filter(_test_import, external_package_paths)
-
-    Tester().run_tests(sys.argv, external_package_paths)
+    tester.clean_packages(dirs)
+    tester.run_tests(sys.argv, dirs)

Modified: trunk/Tools/Scripts/webkitpy/test/main.py (104767 => 104768)


--- trunk/Tools/Scripts/webkitpy/test/main.py	2012-01-12 00:56:24 UTC (rev 104767)
+++ trunk/Tools/Scripts/webkitpy/test/main.py	2012-01-12 00:58:52 UTC (rev 104768)
@@ -20,36 +20,142 @@
 # 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.
 
-"""Contains the entry method for test-webkitpy."""
+"""unit testing code for webkitpy."""
 
 import logging
 import os
 import sys
 import unittest
 
-import webkitpy
+# NOTE: We intentionally do not depend on anything else in webkitpy here to avoid breaking test-webkitpy.
 
-
 _log = logging.getLogger(__name__)
 
 
 class Tester(object):
+    """webkitpy unit tests driver (finds and runs tests)."""
 
-    """Discovers and runs webkitpy unit tests."""
+    def init(self, command_args):
+        """Execute code prior to importing from webkitpy.unittests.
 
-    def _find_test_files(self, webkitpy_dir, suffix):
-        """Return a list of paths to all unit-test files."""
-        unittest_paths = []  # Return value.
+        Args:
+            command_args: The list of command-line arguments -- usually
+                        sys.argv[1:].
 
-        for dir_path, dir_names, file_names in os.walk(webkitpy_dir):
+        """
+        verbose_logging_flag = "--verbose-logging"
+        is_verbose_logging = verbose_logging_flag in command_args
+        if is_verbose_logging:
+            # Remove the flag so it doesn't cause unittest.main() to error out.
+            #
+            # FIXME: Get documentation for the --verbose-logging flag to show
+            #        up in the usage instructions, which are currently generated
+            #        by unittest.main().  It's possible that this will require
+            #        re-implementing the option parser for unittest.main()
+            #        since there may not be an easy way to modify its existing
+            #        option parser.
+            sys.argv.remove(verbose_logging_flag)
+
+        if is_verbose_logging:
+            self.configure_logging(logging.DEBUG)
+            _log.debug("Verbose WebKit logging enabled.")
+        else:
+            self.configure_logging(logging.INFO)
+
+    # Verbose logging is useful for debugging test-webkitpy code that runs
+    # before the actual unit tests -- things like autoinstall downloading and
+    # unit-test auto-detection logic.  This is different from verbose logging
+    # of the unit tests themselves (i.e. the unittest module's --verbose flag).
+    def configure_logging(self, log_level):
+        """Configure the root logger.
+
+        Configure the root logger not to log any messages from webkitpy --
+        except for messages from the autoinstall module.  Also set the
+        logging level as described below.
+        """
+        handler = logging.StreamHandler(sys.stderr)
+        # We constrain the level on the handler rather than on the root
+        # logger itself.  This is probably better because the handler is
+        # configured and known only to this module, whereas the root logger
+        # is an object shared (and potentially modified) by many modules.
+        # Modifying the handler, then, is less intrusive and less likely to
+        # interfere with modifications made by other modules (e.g. in unit
+        # tests).
+        handler.setLevel(log_level)
+        formatter = logging.Formatter("%(message)s")
+        handler.setFormatter(formatter)
+
+        logger = logging.getLogger()
+        logger.addHandler(handler)
+        logger.setLevel(logging.NOTSET)
+
+        # Filter out most webkitpy messages.
+        #
+        # Messages can be selectively re-enabled for this script by updating
+        # this method accordingly.
+        def filter(record):
+            """Filter out autoinstall and non-third-party webkitpy messages."""
+            # FIXME: Figure out a way not to use strings here, for example by
+            #        using syntax like webkitpy.test.__name__.  We want to be
+            #        sure not to import any non-Python 2.4 code, though, until
+            #        after the version-checking code has executed.
+            if (record.name.startswith("webkitpy.common.system.autoinstall") or
+                record.name.startswith("webkitpy.test")):
+                return True
+            if record.name.startswith("webkitpy"):
+                return False
+            return True
+
+        testing_filter = logging.Filter()
+        testing_filter.filter = filter
+
+        # Display a message so developers are not mystified as to why
+        # logging does not work in the unit tests.
+        _log.info("Suppressing most webkitpy logging while running unit tests.")
+        handler.addFilter(testing_filter)
+
+    def clean_packages(self, dirs):
+        """Delete all .pyc files under dirs that have no .py file."""
+        # We clean orphaned *.pyc files from the packages prior to importing from
+        # them to make sure that no import statements falsely succeed.
+        # This helps to check that import statements have been updated correctly
+        # after any file moves.  Otherwise, incorrect import statements can
+        # be masked.
+        #
+        # For example, if webkitpy/common/host.py were moved to a
+        # different location without changing any import statements, and if
+        # the corresponding .pyc file were left behind without deleting it,
+        # then "import webkitpy.common.host" would continue to succeed
+        # even though it would fail for someone checking out a fresh copy
+        # of the source tree.  This is because of a Python feature:
+        #
+        # "It is possible to have a file called spam.pyc (or spam.pyo when -O
+        # is used) without a file spam.py for the same module. This can be used
+        # to distribute a library of Python code in a form that is moderately
+        # hard to reverse engineer."
+        #
+        # ( http://docs.python.org/tutorial/modules.html#compiled-python-files )
+        #
+        # Deleting the orphaned .pyc file prior to importing, however, would
+        # cause an ImportError to occur on import as desired.
+        for dir_to_clean in dirs:
+            _log.debug("Cleaning orphaned *.pyc files from: %s" % dir_to_clean)
+            for dir_path, dir_names, file_names in os.walk(dir_to_clean):
+                for file_name in file_names:
+                    if file_name.endswith(".pyc") and file_name[:-1] not in file_names:
+                        file_path = os.path.join(dir_path, file_name)
+                        _log.info("Deleting orphan *.pyc file: %s" % file_path)
+                        os.remove(file_path)
+
+    def _find_under(self, dir_to_search, suffix):
+        """Return a list of paths to all files under dir_to_search ending in suffix."""
+        paths = []
+        for dir_path, dir_names, file_names in os.walk(dir_to_search):
             for file_name in file_names:
-                if not file_name.endswith(suffix):
-                    continue
-                unittest_path = os.path.join(dir_path, file_name)
-                unittest_paths.append(unittest_path)
+                if file_name.endswith(suffix):
+                    paths.append(os.path.join(dir_path, file_name))
+        return paths
 
-        return unittest_paths
-
     def _modules_from_paths(self, package_root, paths):
         """Return a list of fully-qualified module names given paths."""
         package_path = os.path.abspath(package_root)
@@ -93,56 +199,41 @@
             'webkitpy.to_be_moved.deduplicate_tests_unittest',
         ]
 
-    def run_tests(self, sys_argv, external_package_paths=None):
-        """Run the unit tests in all *_unittest.py modules in webkitpy.
+    def run_tests(self, argv, dirs):
+        """Run all the tests found under dirs."""
+        # FIXME: We should consider moving webkitpy off of using "webkitpy." to prefix
+        # all includes.  If we did that, then this would use path instead of dirname(path).
+        # QueueStatusServer.__init__ has a sys.path import hack due to this code.
+        sys.path.extend(set(os.path.dirname(path) for path in dirs))
 
-        This method excludes "webkitpy.common.checkout.scm.scm_unittest" unless
-        the --all option is the second element of sys_argv.
-
-        Args:
-          sys_argv: A reference to sys.argv.
-
-        """
-        if external_package_paths is None:
-            external_package_paths = []
-        else:
-            # FIXME: We should consider moving webkitpy off of using "webkitpy." to prefix
-            # all includes.  If we did that, then this would use path instead of dirname(path).
-            # QueueStatusServer.__init__ has a sys.path import hack due to this code.
-            sys.path.extend(set(os.path.dirname(path) for path in external_package_paths))
-
-        if '--xml' in sys.argv:
-            sys.argv.remove('--xml')
+        if '--xml' in argv:
+            argv.remove('--xml')
             from webkitpy.thirdparty.autoinstalled.xmlrunner import XMLTestRunner
             test_runner = XMLTestRunner(output='test-webkitpy-xml-reports')
         else:
             test_runner = unittest.TextTestRunner
 
-        if len(sys_argv) > 1 and not sys_argv[-1].startswith("-"):
+        if len(argv) > 1 and not argv[-1].startswith("-"):
             # Then explicit modules or test names were provided, which
             # the unittest module is equipped to handle.
-            unittest.main(argv=sys_argv, module=None, testRunner=test_runner)
+            unittest.main(argv=argv, module=None, testRunner=test_runner)
             # No need to return since unitttest.main() exits.
 
         # Otherwise, auto-detect all unit tests.
 
-        # FIXME: This should be combined with the external_package_paths code above.
-        webkitpy_dir = os.path.dirname(webkitpy.__file__)
-
         skip_integration_tests = False
-        if len(sys_argv) > 1 and sys.argv[1] == "--skip-integrationtests":
-            sys.argv.remove("--skip-integrationtests")
+        if len(argv) > 1 and argv[1] == "--skip-integrationtests":
+            argv.remove("--skip-integrationtests")
             skip_integration_tests = True
 
         modules = []
-        for path in [webkitpy_dir] + external_package_paths:
-            modules.extend(self._modules_from_paths(path, self._find_test_files(path, "_unittest.py")))
+        for dir_to_search in dirs:
+            modules.extend(self._modules_from_paths(dir_to_search, self._find_under(dir_to_search, "_unittest.py")))
             if not skip_integration_tests:
-                modules.extend(self._modules_from_paths(path, self._find_test_files(path, "_integrationtest.py")))
+                modules.extend(self._modules_from_paths(dir_to_search, self._find_under(dir_to_search, "_integrationtest.py")))
         modules.sort()
 
-        # This is a sanity check to ensure that the unit-test discovery
-        # methods are working.
+        # This is a sanity check to ensure that the unit-test discovery methods are working.
         if len(modules) < 1:
             raise Exception("No unit-test modules found.")
 
@@ -151,8 +242,8 @@
 
         # FIXME: This is a hack, but I'm tired of commenting out the test.
         #        See https://bugs.webkit.org/show_bug.cgi?id=31818
-        if len(sys_argv) > 1 and sys.argv[1] == "--all":
-            sys.argv.remove("--all")
+        if len(argv) > 1 and argv[1] == "--all":
+            argv.remove("--all")
         else:
             excluded_module = "webkitpy.common.checkout.scm.scm_unittest"
             _log.info("Excluding: %s (use --all to include)" % excluded_module)
@@ -166,11 +257,11 @@
         for module in modules:
             __import__(module)
 
-        sys_argv.extend(modules)
+        argv.extend(modules)
 
         # We pass None for the module because we do not want the unittest
         # module to resolve module names relative to a given module.
         # (This would require importing all of the unittest modules from
         # this module.)  See the loadTestsFromName() method of the
         # unittest.TestLoader class for more details on this parameter.
-        unittest.main(argv=sys_argv, module=None, testRunner=test_runner)
+        unittest.main(argv=argv, module=None, testRunner=test_runner)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to