Home | History | Annotate | Download | only in test
      1 # Copyright (C) 2010 Chris Jerdonek (cjerdonek (at] webkit.org)
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions
      5 # are met:
      6 # 1.  Redistributions of source code must retain the above copyright
      7 #     notice, this list of conditions and the following disclaimer.
      8 # 2.  Redistributions in binary form must reproduce the above copyright
      9 #     notice, this list of conditions and the following disclaimer in the
     10 #     documentation and/or other materials provided with the distribution.
     11 #
     12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
     13 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
     16 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     17 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     18 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     19 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     20 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     22 
     23 """Contains the entry method for test-webkitpy."""
     24 
     25 import logging
     26 import os
     27 import sys
     28 import unittest
     29 
     30 import webkitpy
     31 
     32 
     33 _log = logging.getLogger(__name__)
     34 
     35 
     36 class Tester(object):
     37 
     38     """Discovers and runs webkitpy unit tests."""
     39 
     40     def _find_unittest_files(self, webkitpy_dir):
     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):
     45             for file_name in file_names:
     46                 if not file_name.endswith("_unittest.py"):
     47                     continue
     48                 unittest_path = os.path.join(dir_path, file_name)
     49                 unittest_paths.append(unittest_path)
     50 
     51         return unittest_paths
     52 
     53     def _modules_from_paths(self, package_root, paths):
     54         """Return a list of fully-qualified module names given paths."""
     55         package_path = os.path.abspath(package_root)
     56         root_package_name = os.path.split(package_path)[1]  # Equals "webkitpy".
     57 
     58         prefix_length = len(package_path)
     59 
     60         modules = []
     61         for path in paths:
     62             path = os.path.abspath(path)
     63             # This gives us, for example: /common/config/ports_unittest.py
     64             rel_path = path[prefix_length:]
     65             # This gives us, for example: /common/config/ports_unittest
     66             rel_path = os.path.splitext(rel_path)[0]
     67 
     68             parts = []
     69             while True:
     70                 (rel_path, tail) = os.path.split(rel_path)
     71                 if not tail:
     72                     break
     73                 parts.insert(0, tail)
     74             # We now have, for example: common.config.ports_unittest
     75             # FIXME: This is all a hack around the fact that we always prefix webkitpy includes with "webkitpy."
     76             parts.insert(0, root_package_name)  # Put "webkitpy" at the beginning.
     77             module = ".".join(parts)
     78             modules.append(module)
     79 
     80         return modules
     81 
     82     def _win32_blacklist(self, module_path):
     83         # FIXME: Remove this once https://bugs.webkit.org/show_bug.cgi?id=54526 is resolved.
     84         if any([module_path.startswith(package) for package in [
     85             'webkitpy.tool',
     86             'webkitpy.common.checkout',
     87             'webkitpy.common.config',
     88             ]]):
     89             return False
     90 
     91         return module_path not in [
     92             # FIXME: This file also requires common.checkout to work
     93             'webkitpy.layout_tests.deduplicate_tests_unittest',
     94         ]
     95 
     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_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 len(sys_argv) > 1 and not sys_argv[-1].startswith("-"):
    115             # Then explicit modules or test names were provided, which
    116             # the unittest module is equipped to handle.
    117             unittest.main(argv=sys_argv, module=None)
    118             # No need to return since unitttest.main() exits.
    119 
    120         # Otherwise, auto-detect all unit tests.
    121 
    122         # FIXME: This should be combined with the external_package_paths code above.
    123         webkitpy_dir = os.path.dirname(webkitpy.__file__)
    124 
    125         modules = []
    126         for path in [webkitpy_dir] + external_package_paths:
    127             modules.extend(self._modules_from_paths(path, self._find_unittest_files(path)))
    128         modules.sort()
    129 
    130         # This is a sanity check to ensure that the unit-test discovery
    131         # methods are working.
    132         if len(modules) < 1:
    133             raise Exception("No unit-test modules found.")
    134 
    135         for module in modules:
    136             _log.debug("Found: %s" % module)
    137 
    138         # FIXME: This is a hack, but I'm tired of commenting out the test.
    139         #        See https://bugs.webkit.org/show_bug.cgi?id=31818
    140         if len(sys_argv) > 1 and sys.argv[1] == "--all":
    141             sys.argv.remove("--all")
    142         else:
    143             excluded_module = "webkitpy.common.checkout.scm_unittest"
    144             _log.info("Excluding: %s (use --all to include)" % excluded_module)
    145             modules.remove(excluded_module)
    146 
    147         if sys.platform == 'win32':
    148             modules = filter(self._win32_blacklist, modules)
    149 
    150         sys_argv.extend(modules)
    151 
    152         # We pass None for the module because we do not want the unittest
    153         # module to resolve module names relative to a given module.
    154         # (This would require importing all of the unittest modules from
    155         # this module.)  See the loadTestsFromName() method of the
    156         # unittest.TestLoader class for more details on this parameter.
    157         unittest.main(argv=sys_argv, module=None)
    158