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