mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
174 lines
7.1 KiB
Python
174 lines
7.1 KiB
Python
# Copyright (C) 2012 Google, Inc.
|
|
# 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:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. 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.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
|
|
|
|
"""this module is responsible for finding python tests."""
|
|
|
|
import logging
|
|
import re
|
|
|
|
|
|
_log = logging.getLogger(__name__)
|
|
|
|
|
|
class _DirectoryTree(object):
|
|
def __init__(self, filesystem, top_directory, starting_subdirectory):
|
|
self.filesystem = filesystem
|
|
self.top_directory = filesystem.realpath(top_directory)
|
|
self.search_directory = self.top_directory
|
|
self.top_package = ''
|
|
if starting_subdirectory:
|
|
self.top_package = starting_subdirectory.replace(filesystem.sep, '.') + '.'
|
|
self.search_directory = filesystem.join(self.top_directory, starting_subdirectory)
|
|
|
|
def find_modules(self, suffixes, sub_directory=None):
|
|
if sub_directory:
|
|
search_directory = self.filesystem.join(self.top_directory, sub_directory)
|
|
else:
|
|
search_directory = self.search_directory
|
|
|
|
def file_filter(filesystem, dirname, basename):
|
|
return any(basename.endswith(suffix) for suffix in suffixes)
|
|
|
|
filenames = self.filesystem.files_under(search_directory, file_filter=file_filter)
|
|
return [self.to_module(filename) for filename in filenames]
|
|
|
|
def to_module(self, path):
|
|
return path.replace(self.top_directory + self.filesystem.sep, '').replace(self.filesystem.sep, '.')[:-3]
|
|
|
|
def subpath(self, path):
|
|
"""Returns the relative path from the top of the tree to the path, or None if the path is not under the top of the tree."""
|
|
realpath = self.filesystem.realpath(self.filesystem.join(self.top_directory, path))
|
|
if realpath.startswith(self.top_directory + self.filesystem.sep):
|
|
return realpath.replace(self.top_directory + self.filesystem.sep, '')
|
|
return None
|
|
|
|
def clean(self):
|
|
"""Delete all .pyc files in the tree that have no matching .py file."""
|
|
_log.debug("Cleaning orphaned *.pyc files from: %s" % self.search_directory)
|
|
filenames = self.filesystem.files_under(self.search_directory)
|
|
for filename in filenames:
|
|
if filename.endswith(".pyc") and filename[:-1] not in filenames:
|
|
_log.info("Deleting orphan *.pyc file: %s" % filename)
|
|
self.filesystem.remove(filename)
|
|
|
|
|
|
class Finder(object):
|
|
def __init__(self, filesystem):
|
|
self.filesystem = filesystem
|
|
self.trees = []
|
|
self._names_to_skip = []
|
|
|
|
def add_tree(self, top_directory, starting_subdirectory=None):
|
|
self.trees.append(_DirectoryTree(self.filesystem, top_directory, starting_subdirectory))
|
|
|
|
def skip(self, names, reason, bugid):
|
|
self._names_to_skip.append(tuple([names, reason, bugid]))
|
|
|
|
def additional_paths(self, paths):
|
|
return [tree.top_directory for tree in self.trees if tree.top_directory not in paths]
|
|
|
|
def clean_trees(self):
|
|
for tree in self.trees:
|
|
tree.clean()
|
|
|
|
def is_module(self, name):
|
|
relpath = name.replace('.', self.filesystem.sep) + '.py'
|
|
return any(self.filesystem.exists(self.filesystem.join(tree.top_directory, relpath)) for tree in self.trees)
|
|
|
|
def is_dotted_name(self, name):
|
|
return re.match(r'[a-zA-Z.][a-zA-Z0-9_.]*', name)
|
|
|
|
def to_module(self, path):
|
|
for tree in self.trees:
|
|
if path.startswith(tree.top_directory):
|
|
return tree.to_module(path)
|
|
return None
|
|
|
|
def find_names(self, args, find_all):
|
|
suffixes = ['_unittest.py', '_integrationtest.py']
|
|
if args:
|
|
names = []
|
|
for arg in args:
|
|
names.extend(self._find_names_for_arg(arg, suffixes))
|
|
return names
|
|
|
|
return self._default_names(suffixes, find_all)
|
|
|
|
def _find_names_for_arg(self, arg, suffixes):
|
|
realpath = self.filesystem.realpath(arg)
|
|
if self.filesystem.exists(realpath):
|
|
names = self._find_in_trees(realpath, suffixes)
|
|
if not names:
|
|
_log.error("%s is not in one of the test trees." % arg)
|
|
return names
|
|
|
|
# See if it's a python package in a tree (or a relative path from the top of a tree).
|
|
names = self._find_in_trees(arg.replace('.', self.filesystem.sep), suffixes)
|
|
if names:
|
|
return names
|
|
|
|
if self.is_dotted_name(arg):
|
|
# The name may not exist, but that's okay; we'll find out later.
|
|
return [arg]
|
|
|
|
_log.error("%s is not a python name or an existing file or directory." % arg)
|
|
return []
|
|
|
|
def _find_in_trees(self, path, suffixes):
|
|
for tree in self.trees:
|
|
relpath = tree.subpath(path)
|
|
if not relpath:
|
|
continue
|
|
if self.filesystem.isfile(path):
|
|
return [tree.to_module(path)]
|
|
else:
|
|
return tree.find_modules(suffixes, path)
|
|
return []
|
|
|
|
def _default_names(self, suffixes, find_all):
|
|
modules = []
|
|
for tree in self.trees:
|
|
modules.extend(tree.find_modules(suffixes))
|
|
modules.sort()
|
|
|
|
for module in modules:
|
|
_log.debug("Found: %s" % module)
|
|
|
|
if not find_all:
|
|
for (names, reason, bugid) in self._names_to_skip:
|
|
self._exclude(modules, names, reason, bugid)
|
|
|
|
return modules
|
|
|
|
def _exclude(self, modules, module_prefixes, reason, bugid):
|
|
_log.info('Skipping tests in the following modules or packages because they %s:' % reason)
|
|
for prefix in module_prefixes:
|
|
_log.info(' %s' % prefix)
|
|
modules_to_exclude = filter(lambda m: m.startswith(prefix), modules)
|
|
for m in modules_to_exclude:
|
|
if len(modules_to_exclude) > 1:
|
|
_log.debug(' %s' % m)
|
|
modules.remove(m)
|
|
_log.info(' (https://bugs.webkit.org/show_bug.cgi?id=%d; use --all to include)' % bugid)
|
|
_log.info('')
|