diff options
Diffstat (limited to 'Tools/Scripts/test-webkitpy')
| -rwxr-xr-x | Tools/Scripts/test-webkitpy | 266 | 
1 files changed, 266 insertions, 0 deletions
diff --git a/Tools/Scripts/test-webkitpy b/Tools/Scripts/test-webkitpy new file mode 100755 index 0000000..7efacb0 --- /dev/null +++ b/Tools/Scripts/test-webkitpy @@ -0,0 +1,266 @@ +#!/usr/bin/env python +# Copyright (c) 2009 Google Inc. All rights reserved. +# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) +# +# 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 logging +import os +import sys + +# Do not import anything from webkitpy prior to cleaning webkitpy of +# orphaned *.pyc files.  This ensures that no orphaned *.pyc files are +# accidentally imported during the course of this script. +# +# Also, do not import or execute any Python code incompatible with +# Python 2.4 until after execution of the init() method below. + + +_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. + +    """ +    # Don't use the Python ternary operator here so that this method will +    # work with Python 2.4. +    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, paths_not_to_log): +    """Delete from a directory all .pyc files that have no .py file. + +    Args: +      dir_to_clean: The path to the directory to clean. +      paths_not_to_log: A list of paths to .pyc files whose deletions should +                        not be logged.  This list should normally include +                        only test .pyc files. + +    """ +    _log.debug("Cleaning orphaned *.pyc files from: %s" % dir_to_clean) + +    # Normalize paths not to log. +    paths_not_to_log = [os.path.abspath(path) for path in paths_not_to_log] + +    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) +                if os.path.abspath(file_path) not in paths_not_to_log: +                    _log.info("Deleting orphan *.pyc file: %s" % file_path) +                os.remove(file_path) + + +# As a substitute for a unit test, this method tests _clean_pyc_files() +# in addition to calling it.  We chose not to use the unittest module +# because _clean_pyc_files() is called only once and is not used elsewhere. +def _clean_packages_with_test(external_package_paths): +    webkitpy_dir = os.path.join(os.path.dirname(__file__), "webkitpy") +    package_paths = [webkitpy_dir] + external_package_paths + +    # The test .pyc file is-- +    # webkitpy/python24/TEMP_test-webkitpy_test_pyc_file.pyc. +    test_path = os.path.join(webkitpy_dir, "python24", +                             "TEMP_test-webkitpy_test_pyc_file.pyc") + +    test_file = open(test_path, "w") +    try: +        test_file.write("Test .pyc file generated by test-webkitpy.") +    finally: +        test_file.close() + +    # Confirm that the test file exists so that when we check that it does +    # not exist, the result is meaningful. +    if not os.path.exists(test_path): +        raise Exception("Test .pyc file not created: %s" % test_path) + +    for path in package_paths: +        _clean_pyc_files(path, [test_path]) + +    if os.path.exists(test_path): +        raise Exception("Test .pyc file not deleted: %s" % test_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/python24/versioning.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.python24.versioning" 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_with_test(external_package_paths) + +    import webkitpy.python24.versioning as versioning + +    versioning.check_version(log=_log) + +    (comparison, current_version, minimum_version) = \ +        versioning.compare_version() + +    if comparison > 0: +        # Then the current version is later than the minimum version. +        message = ("You are testing webkitpy with a Python version (%s) " +                   "higher than the minimum version (%s) it was meant " +                   "to support." % (current_version, minimum_version)) +        _log.warn(message) + + +def _path_from_webkit_root(*components): +    webkit_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +    return os.path.join(webkit_root, *components) + + +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('WebKit2', 'Scripts', 'webkit2'), +        _path_from_webkit_root('Tools', 'QueueStatusServer'), +    ] +    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 + +    external_package_paths = filter(_test_import, external_package_paths) + +    Tester().run_tests(sys.argv, external_package_paths)  | 
