Changeset 104768 in webkit


Ignore:
Timestamp:
Jan 11, 2012 4:58:52 PM (12 years ago)
Author:
dpranke@chromium.org
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):

Location:
trunk/Tools
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r104764 r104768  
     12012-01-10  Dirk Pranke  <dpranke@chromium.org>
     2
     3        test-webkitpy: push more logic into webkitpy.test.main, clean up code
     4        https://bugs.webkit.org/show_bug.cgi?id=76021
     5
     6        Reviewed by Eric Seidel.
     7
     8        I plan to add more functionality to test-webkitpy, but it is
     9        difficult to hack on now; this patch is the first of two that
     10        brings test-webkitpy more inline with current coding style by
     11        pushing more logic into the webkitpy.test.main.Tester class (so
     12        that it will be testable itself).
     13       
     14        There should be no functional changes in this patch.
     15
     16        * Scripts/test-webkitpy:
     17        (_path_from_webkit_root):
     18        * Scripts/webkitpy/test/main.py:
     19        (Tester):
     20        (Tester.init):
     21        (Tester.configure_logging):
     22        (Tester.configure_logging.filter):
     23        (Tester.clean_packages):
     24        (Tester.run_tests):
     25
    1262012-01-11  Wei Jia  <wjia@chromium.org>
    227
  • trunk/Tools/Scripts/test-webkitpy

    r104495 r104768  
    3333import sys
    3434
    35 # We strictly limit what is imported from webkitpy until we get a chance
    36 # to delete any stray *.pyc files.
    37 import webkitpy.common.version_check
     35# NOTE: We intentionally limit imports from webkitpy here to minimize the
     36# chances of breaking test-webkitpy itself.
     37from webkitpy.common import version_check
     38from webkitpy.test import main
     39
    3840
    3941_log = logging.getLogger("test-webkitpy")
    4042
    4143
    42 # Verbose logging is useful for debugging test-webkitpy code that runs
    43 # before the actual unit tests -- things like autoinstall downloading and
    44 # unit-test auto-detection logic.  This is different from verbose logging
    45 # of the unit tests themselves (i.e. the unittest module's --verbose flag).
    46 def configure_logging(is_verbose_logging):
    47     """Configure the root logger.
     44if __name__ == "__main__":
     45    webkit_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
    4846
    49     Configure the root logger not to log any messages from webkitpy --
    50     except for messages from the autoinstall module.  Also set the
    51     logging level as described below.
     47    tester = main.Tester()
     48    tester.init(sys.argv[1:])
    5249
    53     Args:
    54       is_verbose_logging: A boolean value of whether logging should be
    55                           verbose.  If this parameter is true, the logging
    56                           level for the handler on the root logger is set to
    57                           logging.DEBUG.  Otherwise, it is set to logging.INFO.
     50    # FIXME: We should probably test each package separately to avoid naming conflicts.
     51    dirs = [
     52        os.path.join(webkit_root, 'Tools', 'Scripts', 'webkitpy'),
     53        os.path.join(webkit_root, 'Source', 'WebKit2', 'Scripts', 'webkit2'),
     54    ]
    5855
    59     """
    60     if is_verbose_logging:
    61         logging_level = logging.DEBUG
    62     else:
    63         logging_level = logging.INFO
     56    # FIXME: Make this work on windows as well?
     57    appengine_sdk_path = '/usr/local/google_appengine'
     58    if os.path.exists(appengine_sdk_path) and not appengine_sdk_path in sys.path:
     59        sys.path.append(appengine_sdk_path)
     60    try:
     61        import google.appengine
     62        dirs.append(os.path.join(webkit_root, 'Tools', 'QueueStatusServer'))
     63    except ImportError:
     64        _log.info('Skipping QueueStatusServer tests; the Google AppEngine Python SDK is not installed.')
    6465
    65     handler = logging.StreamHandler(sys.stderr)
    66     # We constrain the level on the handler rather than on the root
    67     # logger itself.  This is probably better because the handler is
    68     # configured and known only to this module, whereas the root logger
    69     # is an object shared (and potentially modified) by many modules.
    70     # Modifying the handler, then, is less intrusive and less likely to
    71     # interfere with modifications made by other modules (e.g. in unit
    72     # tests).
    73     handler.setLevel(logging_level)
    74     formatter = logging.Formatter("%(name)s: %(levelname)-8s %(message)s")
    75     handler.setFormatter(formatter)
    76 
    77     logger = logging.getLogger()
    78     logger.addHandler(handler)
    79     logger.setLevel(logging.NOTSET)
    80 
    81     # Filter out most webkitpy messages.
    82     #
    83     # Messages can be selectively re-enabled for this script by updating
    84     # this method accordingly.
    85     def filter(record):
    86         """Filter out autoinstall and non-third-party webkitpy messages."""
    87         # FIXME: Figure out a way not to use strings here, for example by
    88         #        using syntax like webkitpy.test.__name__.  We want to be
    89         #        sure not to import any non-Python 2.4 code, though, until
    90         #        after the version-checking code has executed.
    91         if (record.name.startswith("webkitpy.common.system.autoinstall") or
    92             record.name.startswith("webkitpy.test")):
    93             return True
    94         if record.name.startswith("webkitpy"):
    95             return False
    96         return True
    97 
    98     testing_filter = logging.Filter()
    99     testing_filter.filter = filter
    100 
    101     # Display a message so developers are not mystified as to why
    102     # logging does not work in the unit tests.
    103     _log.info("Suppressing most webkitpy logging while running unit tests.")
    104     handler.addFilter(testing_filter)
    105 
    106 
    107 def _clean_pyc_files(dir_to_clean):
    108     """Delete from under a directory all .pyc files that have no .py file."""
    109     _log.debug("Cleaning orphaned *.pyc files from: %s" % dir_to_clean)
    110     for dir_path, dir_names, file_names in os.walk(dir_to_clean):
    111         for file_name in file_names:
    112             if file_name.endswith(".pyc") and file_name[:-1] not in file_names:
    113                 file_path = os.path.join(dir_path, file_name)
    114                 _log.info("Deleting orphan *.pyc file: %s" % file_path)
    115                 os.remove(file_path)
    116 
    117 
    118 def _clean_packages(external_package_paths):
    119     webkitpy_dir = os.path.join(os.path.dirname(__file__), "webkitpy")
    120     package_paths = [webkitpy_dir] + external_package_paths
    121     for path in package_paths:
    122         _clean_pyc_files(path)
    123 
    124 
    125 def init(command_args, external_package_paths):
    126     """Execute code prior to importing from webkitpy.unittests.
    127 
    128     Args:
    129         command_args: The list of command-line arguments -- usually
    130                       sys.argv[1:].
    131 
    132     """
    133     verbose_logging_flag = "--verbose-logging"
    134     is_verbose_logging = verbose_logging_flag in command_args
    135     if is_verbose_logging:
    136         # Remove the flag so it doesn't cause unittest.main() to error out.
    137         #
    138         # FIXME: Get documentation for the --verbose-logging flag to show
    139         #        up in the usage instructions, which are currently generated
    140         #        by unittest.main().  It's possible that this will require
    141         #        re-implementing the option parser for unittest.main()
    142         #        since there may not be an easy way to modify its existing
    143         #        option parser.
    144         sys.argv.remove(verbose_logging_flag)
    145 
    146     configure_logging(is_verbose_logging)
    147     _log.debug("Verbose WebKit logging enabled.")
    148 
    149     # We clean orphaned *.pyc files from the packages prior to importing from
    150     # them to make sure that no import statements falsely succeed.
    151     # This helps to check that import statements have been updated correctly
    152     # after any file moves.  Otherwise, incorrect import statements can
    153     # be masked.
    154     #
    155     # For example, if webkitpy/common/host.py were moved to a
    156     # different location without changing any import statements, and if
    157     # the corresponding .pyc file were left behind without deleting it,
    158     # then "import webkitpy.common.host" would continue to succeed
    159     # even though it would fail for someone checking out a fresh copy
    160     # of the source tree.  This is because of a Python feature:
    161     #
    162     # "It is possible to have a file called spam.pyc (or spam.pyo when -O
    163     # is used) without a file spam.py for the same module. This can be used
    164     # to distribute a library of Python code in a form that is moderately
    165     # hard to reverse engineer."
    166     #
    167     # ( http://docs.python.org/tutorial/modules.html#compiled-python-files )
    168     #
    169     # Deleting the orphaned .pyc file prior to importing, however, would
    170     # cause an ImportError to occur on import as desired.
    171     _clean_packages(external_package_paths)
    172 
    173 
    174 def _path_from_webkit_root(*components):
    175     webkit_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
    176     return os.path.join(webkit_root, *components)
    177 
    178 
    179 def _test_import(module_path):
    180     try:
    181         sys.path.append(os.path.dirname(module_path))
    182         module_name = os.path.basename(module_path)
    183         __import__(module_name)
    184         return True
    185     except Exception, e:
    186         message = "Skipping tests in %s due to failure (%s)." % (module_path, e)
    187         if module_name.endswith("QueueStatusServer"):
    188             message += "  This module is optional.  The failure is likely due to a missing Google AppEngine install.  (http://code.google.com/appengine/downloads.html)"
    189         _log.warn(message)
    190         return False
    191 
    192 if __name__ == "__main__":
    193     # FIXME: We should probably test each package separately to avoid naming conflicts.
    194     external_package_paths = [
    195         _path_from_webkit_root('Source', 'WebKit2', 'Scripts', 'webkit2'),
    196         _path_from_webkit_root('Tools', 'QueueStatusServer'),
    197     ]
    198     init(sys.argv[1:], external_package_paths)
    199 
    200     # We import the unit test code after init() to ensure that any
    201     # Python version warnings are displayed in case an error occurs
    202     # while interpreting webkitpy.unittests.  This also allows
    203     # logging to be configured prior to importing -- for example to
    204     # enable the display of autoinstall logging.log messages while
    205     # running the unit tests.
    206     from webkitpy.test.main import Tester
    207 
    208     external_package_paths = filter(_test_import, external_package_paths)
    209 
    210     Tester().run_tests(sys.argv, external_package_paths)
     66    tester.clean_packages(dirs)
     67    tester.run_tests(sys.argv, dirs)
  • trunk/Tools/Scripts/webkitpy/test/main.py

    r104740 r104768  
    2121# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2222
    23 """Contains the entry method for test-webkitpy."""
     23"""unit testing code for webkitpy."""
    2424
    2525import logging
     
    2828import unittest
    2929
    30 import webkitpy
    31 
     30# NOTE: We intentionally do not depend on anything else in webkitpy here to avoid breaking test-webkitpy.
    3231
    3332_log = logging.getLogger(__name__)
     
    3534
    3635class Tester(object):
    37 
    38     """Discovers and runs webkitpy unit tests."""
    39 
    40     def _find_test_files(self, webkitpy_dir, suffix):
    41         """Return a list of paths to all unit-test files."""
    42         unittest_paths = []  # Return value.
    43 
    44         for dir_path, dir_names, file_names in os.walk(webkitpy_dir):
     36    """webkitpy unit tests driver (finds and runs tests)."""
     37
     38    def init(self, command_args):
     39        """Execute code prior to importing from webkitpy.unittests.
     40
     41        Args:
     42            command_args: The list of command-line arguments -- usually
     43                        sys.argv[1:].
     44
     45        """
     46        verbose_logging_flag = "--verbose-logging"
     47        is_verbose_logging = verbose_logging_flag in command_args
     48        if is_verbose_logging:
     49            # Remove the flag so it doesn't cause unittest.main() to error out.
     50            #
     51            # FIXME: Get documentation for the --verbose-logging flag to show
     52            #        up in the usage instructions, which are currently generated
     53            #        by unittest.main().  It's possible that this will require
     54            #        re-implementing the option parser for unittest.main()
     55            #        since there may not be an easy way to modify its existing
     56            #        option parser.
     57            sys.argv.remove(verbose_logging_flag)
     58
     59        if is_verbose_logging:
     60            self.configure_logging(logging.DEBUG)
     61            _log.debug("Verbose WebKit logging enabled.")
     62        else:
     63            self.configure_logging(logging.INFO)
     64
     65    # Verbose logging is useful for debugging test-webkitpy code that runs
     66    # before the actual unit tests -- things like autoinstall downloading and
     67    # unit-test auto-detection logic.  This is different from verbose logging
     68    # of the unit tests themselves (i.e. the unittest module's --verbose flag).
     69    def configure_logging(self, log_level):
     70        """Configure the root logger.
     71
     72        Configure the root logger not to log any messages from webkitpy --
     73        except for messages from the autoinstall module.  Also set the
     74        logging level as described below.
     75        """
     76        handler = logging.StreamHandler(sys.stderr)
     77        # We constrain the level on the handler rather than on the root
     78        # logger itself.  This is probably better because the handler is
     79        # configured and known only to this module, whereas the root logger
     80        # is an object shared (and potentially modified) by many modules.
     81        # Modifying the handler, then, is less intrusive and less likely to
     82        # interfere with modifications made by other modules (e.g. in unit
     83        # tests).
     84        handler.setLevel(log_level)
     85        formatter = logging.Formatter("%(message)s")
     86        handler.setFormatter(formatter)
     87
     88        logger = logging.getLogger()
     89        logger.addHandler(handler)
     90        logger.setLevel(logging.NOTSET)
     91
     92        # Filter out most webkitpy messages.
     93        #
     94        # Messages can be selectively re-enabled for this script by updating
     95        # this method accordingly.
     96        def filter(record):
     97            """Filter out autoinstall and non-third-party webkitpy messages."""
     98            # FIXME: Figure out a way not to use strings here, for example by
     99            #        using syntax like webkitpy.test.__name__.  We want to be
     100            #        sure not to import any non-Python 2.4 code, though, until
     101            #        after the version-checking code has executed.
     102            if (record.name.startswith("webkitpy.common.system.autoinstall") or
     103                record.name.startswith("webkitpy.test")):
     104                return True
     105            if record.name.startswith("webkitpy"):
     106                return False
     107            return True
     108
     109        testing_filter = logging.Filter()
     110        testing_filter.filter = filter
     111
     112        # Display a message so developers are not mystified as to why
     113        # logging does not work in the unit tests.
     114        _log.info("Suppressing most webkitpy logging while running unit tests.")
     115        handler.addFilter(testing_filter)
     116
     117    def clean_packages(self, dirs):
     118        """Delete all .pyc files under dirs that have no .py file."""
     119        # We clean orphaned *.pyc files from the packages prior to importing from
     120        # them to make sure that no import statements falsely succeed.
     121        # This helps to check that import statements have been updated correctly
     122        # after any file moves.  Otherwise, incorrect import statements can
     123        # be masked.
     124        #
     125        # For example, if webkitpy/common/host.py were moved to a
     126        # different location without changing any import statements, and if
     127        # the corresponding .pyc file were left behind without deleting it,
     128        # then "import webkitpy.common.host" would continue to succeed
     129        # even though it would fail for someone checking out a fresh copy
     130        # of the source tree.  This is because of a Python feature:
     131        #
     132        # "It is possible to have a file called spam.pyc (or spam.pyo when -O
     133        # is used) without a file spam.py for the same module. This can be used
     134        # to distribute a library of Python code in a form that is moderately
     135        # hard to reverse engineer."
     136        #
     137        # ( http://docs.python.org/tutorial/modules.html#compiled-python-files )
     138        #
     139        # Deleting the orphaned .pyc file prior to importing, however, would
     140        # cause an ImportError to occur on import as desired.
     141        for dir_to_clean in dirs:
     142            _log.debug("Cleaning orphaned *.pyc files from: %s" % dir_to_clean)
     143            for dir_path, dir_names, file_names in os.walk(dir_to_clean):
     144                for file_name in file_names:
     145                    if file_name.endswith(".pyc") and file_name[:-1] not in file_names:
     146                        file_path = os.path.join(dir_path, file_name)
     147                        _log.info("Deleting orphan *.pyc file: %s" % file_path)
     148                        os.remove(file_path)
     149
     150    def _find_under(self, dir_to_search, suffix):
     151        """Return a list of paths to all files under dir_to_search ending in suffix."""
     152        paths = []
     153        for dir_path, dir_names, file_names in os.walk(dir_to_search):
    45154            for file_name in file_names:
    46                 if not file_name.endswith(suffix):
    47                     continue
    48                 unittest_path = os.path.join(dir_path, file_name)
    49                 unittest_paths.append(unittest_path)
    50 
    51         return unittest_paths
     155                if file_name.endswith(suffix):
     156                    paths.append(os.path.join(dir_path, file_name))
     157        return paths
    52158
    53159    def _modules_from_paths(self, package_root, paths):
     
    94200        ]
    95201
    96     def run_tests(self, sys_argv, external_package_paths=None):
    97         """Run the unit tests in all *_unittest.py modules in webkitpy.
    98 
    99         This method excludes "webkitpy.common.checkout.scm.scm_unittest" unless
    100         the --all option is the second element of sys_argv.
    101 
    102         Args:
    103           sys_argv: A reference to sys.argv.
    104 
    105         """
    106         if external_package_paths is None:
    107             external_package_paths = []
    108         else:
    109             # FIXME: We should consider moving webkitpy off of using "webkitpy." to prefix
    110             # all includes.  If we did that, then this would use path instead of dirname(path).
    111             # QueueStatusServer.__init__ has a sys.path import hack due to this code.
    112             sys.path.extend(set(os.path.dirname(path) for path in external_package_paths))
    113 
    114         if '--xml' in sys.argv:
    115             sys.argv.remove('--xml')
     202    def run_tests(self, argv, dirs):
     203        """Run all the tests found under dirs."""
     204        # FIXME: We should consider moving webkitpy off of using "webkitpy." to prefix
     205        # all includes.  If we did that, then this would use path instead of dirname(path).
     206        # QueueStatusServer.__init__ has a sys.path import hack due to this code.
     207        sys.path.extend(set(os.path.dirname(path) for path in dirs))
     208
     209        if '--xml' in argv:
     210            argv.remove('--xml')
    116211            from webkitpy.thirdparty.autoinstalled.xmlrunner import XMLTestRunner
    117212            test_runner = XMLTestRunner(output='test-webkitpy-xml-reports')
     
    119214            test_runner = unittest.TextTestRunner
    120215
    121         if len(sys_argv) > 1 and not sys_argv[-1].startswith("-"):
     216        if len(argv) > 1 and not argv[-1].startswith("-"):
    122217            # Then explicit modules or test names were provided, which
    123218            # the unittest module is equipped to handle.
    124             unittest.main(argv=sys_argv, module=None, testRunner=test_runner)
     219            unittest.main(argv=argv, module=None, testRunner=test_runner)
    125220            # No need to return since unitttest.main() exits.
    126221
    127222        # Otherwise, auto-detect all unit tests.
    128223
    129         # FIXME: This should be combined with the external_package_paths code above.
    130         webkitpy_dir = os.path.dirname(webkitpy.__file__)
    131 
    132224        skip_integration_tests = False
    133         if len(sys_argv) > 1 and sys.argv[1] == "--skip-integrationtests":
    134             sys.argv.remove("--skip-integrationtests")
     225        if len(argv) > 1 and argv[1] == "--skip-integrationtests":
     226            argv.remove("--skip-integrationtests")
    135227            skip_integration_tests = True
    136228
    137229        modules = []
    138         for path in [webkitpy_dir] + external_package_paths:
    139             modules.extend(self._modules_from_paths(path, self._find_test_files(path, "_unittest.py")))
     230        for dir_to_search in dirs:
     231            modules.extend(self._modules_from_paths(dir_to_search, self._find_under(dir_to_search, "_unittest.py")))
    140232            if not skip_integration_tests:
    141                 modules.extend(self._modules_from_paths(path, self._find_test_files(path, "_integrationtest.py")))
     233                modules.extend(self._modules_from_paths(dir_to_search, self._find_under(dir_to_search, "_integrationtest.py")))
    142234        modules.sort()
    143235
    144         # This is a sanity check to ensure that the unit-test discovery
    145         # methods are working.
     236        # This is a sanity check to ensure that the unit-test discovery methods are working.
    146237        if len(modules) < 1:
    147238            raise Exception("No unit-test modules found.")
     
    152243        # FIXME: This is a hack, but I'm tired of commenting out the test.
    153244        #        See https://bugs.webkit.org/show_bug.cgi?id=31818
    154         if len(sys_argv) > 1 and sys.argv[1] == "--all":
    155             sys.argv.remove("--all")
     245        if len(argv) > 1 and argv[1] == "--all":
     246            argv.remove("--all")
    156247        else:
    157248            excluded_module = "webkitpy.common.checkout.scm.scm_unittest"
     
    167258            __import__(module)
    168259
    169         sys_argv.extend(modules)
     260        argv.extend(modules)
    170261
    171262        # We pass None for the module because we do not want the unittest
     
    174265        # this module.)  See the loadTestsFromName() method of the
    175266        # unittest.TestLoader class for more details on this parameter.
    176         unittest.main(argv=sys_argv, module=None, testRunner=test_runner)
     267        unittest.main(argv=argv, module=None, testRunner=test_runner)
Note: See TracChangeset for help on using the changeset viewer.