| OLD | NEW |
| (Empty) |
| 1 # Copyright (C) 2012 Google, Inc. | |
| 2 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) | |
| 3 # | |
| 4 # Redistribution and use in source and binary forms, with or without | |
| 5 # modification, are permitted provided that the following conditions | |
| 6 # are met: | |
| 7 # 1. Redistributions of source code must retain the above copyright | |
| 8 # notice, this list of conditions and the following disclaimer. | |
| 9 # 2. Redistributions in binary form must reproduce the above copyright | |
| 10 # notice, this list of conditions and the following disclaimer in the | |
| 11 # documentation and/or other materials provided with the distribution. | |
| 12 # | |
| 13 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND | |
| 14 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| 15 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| 16 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR | |
| 17 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 18 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| 19 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| 20 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 21 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 22 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 23 | |
| 24 """this module is responsible for finding python tests.""" | |
| 25 | |
| 26 import logging | |
| 27 import re | |
| 28 | |
| 29 | |
| 30 _log = logging.getLogger(__name__) | |
| 31 | |
| 32 | |
| 33 class _DirectoryTree(object): | |
| 34 def __init__(self, filesystem, top_directory, starting_subdirectory): | |
| 35 self.filesystem = filesystem | |
| 36 self.top_directory = filesystem.realpath(top_directory) | |
| 37 self.search_directory = self.top_directory | |
| 38 self.top_package = '' | |
| 39 if starting_subdirectory: | |
| 40 self.top_package = starting_subdirectory.replace(filesystem.sep, '.'
) + '.' | |
| 41 self.search_directory = filesystem.join(self.top_directory, starting
_subdirectory) | |
| 42 | |
| 43 def find_modules(self, suffixes, sub_directory=None): | |
| 44 if sub_directory: | |
| 45 search_directory = self.filesystem.join(self.top_directory, sub_dire
ctory) | |
| 46 else: | |
| 47 search_directory = self.search_directory | |
| 48 | |
| 49 def file_filter(filesystem, dirname, basename): | |
| 50 return any(basename.endswith(suffix) for suffix in suffixes) | |
| 51 | |
| 52 filenames = self.filesystem.files_under(search_directory, file_filter=fi
le_filter) | |
| 53 return [self.to_module(filename) for filename in filenames] | |
| 54 | |
| 55 def to_module(self, path): | |
| 56 return path.replace(self.top_directory + self.filesystem.sep, '').replac
e(self.filesystem.sep, '.')[:-3] | |
| 57 | |
| 58 def subpath(self, path): | |
| 59 """Returns the relative path from the top of the tree to the path, or No
ne if the path is not under the top of the tree.""" | |
| 60 realpath = self.filesystem.realpath(self.filesystem.join(self.top_direct
ory, path)) | |
| 61 if realpath.startswith(self.top_directory + self.filesystem.sep): | |
| 62 return realpath.replace(self.top_directory + self.filesystem.sep, ''
) | |
| 63 return None | |
| 64 | |
| 65 def clean(self): | |
| 66 """Delete all .pyc files in the tree that have no matching .py file.""" | |
| 67 _log.debug("Cleaning orphaned *.pyc files from: %s" % self.search_direct
ory) | |
| 68 filenames = self.filesystem.files_under(self.search_directory) | |
| 69 for filename in filenames: | |
| 70 if filename.endswith(".pyc") and filename[:-1] not in filenames: | |
| 71 _log.info("Deleting orphan *.pyc file: %s" % filename) | |
| 72 self.filesystem.remove(filename) | |
| 73 | |
| 74 | |
| 75 class Finder(object): | |
| 76 def __init__(self, filesystem): | |
| 77 self.filesystem = filesystem | |
| 78 self.trees = [] | |
| 79 self._names_to_skip = [] | |
| 80 | |
| 81 def add_tree(self, top_directory, starting_subdirectory=None): | |
| 82 self.trees.append(_DirectoryTree(self.filesystem, top_directory, startin
g_subdirectory)) | |
| 83 | |
| 84 def skip(self, names, reason, bugid): | |
| 85 self._names_to_skip.append(tuple([names, reason, bugid])) | |
| 86 | |
| 87 def additional_paths(self, paths): | |
| 88 return [tree.top_directory for tree in self.trees if tree.top_directory
not in paths] | |
| 89 | |
| 90 def clean_trees(self): | |
| 91 for tree in self.trees: | |
| 92 tree.clean() | |
| 93 | |
| 94 def is_module(self, name): | |
| 95 relpath = name.replace('.', self.filesystem.sep) + '.py' | |
| 96 return any(self.filesystem.exists(self.filesystem.join(tree.top_director
y, relpath)) for tree in self.trees) | |
| 97 | |
| 98 def is_dotted_name(self, name): | |
| 99 return re.match(r'[a-zA-Z.][a-zA-Z0-9_.]*', name) | |
| 100 | |
| 101 def to_module(self, path): | |
| 102 for tree in self.trees: | |
| 103 if path.startswith(tree.top_directory): | |
| 104 return tree.to_module(path) | |
| 105 return None | |
| 106 | |
| 107 def find_names(self, args, find_all): | |
| 108 suffixes = ['_unittest.py', '_integrationtest.py'] | |
| 109 if args: | |
| 110 names = [] | |
| 111 for arg in args: | |
| 112 names.extend(self._find_names_for_arg(arg, suffixes)) | |
| 113 return names | |
| 114 | |
| 115 return self._default_names(suffixes, find_all) | |
| 116 | |
| 117 def _find_names_for_arg(self, arg, suffixes): | |
| 118 realpath = self.filesystem.realpath(arg) | |
| 119 if self.filesystem.exists(realpath): | |
| 120 names = self._find_in_trees(realpath, suffixes) | |
| 121 if not names: | |
| 122 _log.error("%s is not in one of the test trees." % arg) | |
| 123 return names | |
| 124 | |
| 125 # See if it's a python package in a tree (or a relative path from the to
p of a tree). | |
| 126 names = self._find_in_trees(arg.replace('.', self.filesystem.sep), suffi
xes) | |
| 127 if names: | |
| 128 return names | |
| 129 | |
| 130 if self.is_dotted_name(arg): | |
| 131 # The name may not exist, but that's okay; we'll find out later. | |
| 132 return [arg] | |
| 133 | |
| 134 _log.error("%s is not a python name or an existing file or directory." %
arg) | |
| 135 return [] | |
| 136 | |
| 137 def _find_in_trees(self, path, suffixes): | |
| 138 for tree in self.trees: | |
| 139 relpath = tree.subpath(path) | |
| 140 if not relpath: | |
| 141 continue | |
| 142 if self.filesystem.isfile(path): | |
| 143 return [tree.to_module(path)] | |
| 144 else: | |
| 145 return tree.find_modules(suffixes, path) | |
| 146 return [] | |
| 147 | |
| 148 def _default_names(self, suffixes, find_all): | |
| 149 modules = [] | |
| 150 for tree in self.trees: | |
| 151 modules.extend(tree.find_modules(suffixes)) | |
| 152 modules.sort() | |
| 153 | |
| 154 for module in modules: | |
| 155 _log.debug("Found: %s" % module) | |
| 156 | |
| 157 if not find_all: | |
| 158 for (names, reason, bugid) in self._names_to_skip: | |
| 159 self._exclude(modules, names, reason, bugid) | |
| 160 | |
| 161 return modules | |
| 162 | |
| 163 def _exclude(self, modules, module_prefixes, reason, bugid): | |
| 164 _log.info('Skipping tests in the following modules or packages because t
hey %s:' % reason) | |
| 165 for prefix in module_prefixes: | |
| 166 _log.info(' %s' % prefix) | |
| 167 modules_to_exclude = filter(lambda m: m.startswith(prefix), modules) | |
| 168 for m in modules_to_exclude: | |
| 169 if len(modules_to_exclude) > 1: | |
| 170 _log.debug(' %s' % m) | |
| 171 modules.remove(m) | |
| 172 _log.info(' (https://bugs.webkit.org/show_bug.cgi?id=%d; use --all to
include)' % bugid) | |
| 173 _log.info('') | |
| OLD | NEW |