Support directories in sanity tests.

This commit is contained in:
Matt Clay 2019-07-30 16:03:27 -07:00
parent 57e4312a36
commit f61b044bf0
4 changed files with 39 additions and 25 deletions

View file

@ -25,6 +25,7 @@ from lib.util import (
get_available_python_versions, get_available_python_versions,
find_python, find_python,
is_subdir, is_subdir,
paths_to_dirs,
) )
from lib.util_common import ( from lib.util_common import (
@ -164,10 +165,13 @@ def command_sanity(args):
if test.all_targets: if test.all_targets:
usable_targets = targets.targets usable_targets = targets.targets
elif test.no_targets: elif test.no_targets:
usable_targets = [] usable_targets = tuple()
else: else:
usable_targets = targets.include usable_targets = targets.include
if test.include_directories:
usable_targets += tuple(TestTarget(path, None, None, '') for path in paths_to_dirs([target.path for target in usable_targets]))
usable_targets = sorted(test.filter_targets(list(usable_targets))) usable_targets = sorted(test.filter_targets(list(usable_targets)))
usable_targets = settings.filter_skipped_targets(usable_targets) usable_targets = settings.filter_skipped_targets(usable_targets)
sanity_targets = SanityTargets(targets.targets, tuple(usable_targets)) sanity_targets = SanityTargets(targets.targets, tuple(usable_targets))
@ -258,6 +262,7 @@ class SanityIgnoreParser:
tests_by_name = {} # type: t.Dict[str, SanityTest] tests_by_name = {} # type: t.Dict[str, SanityTest]
versioned_test_names = set() # type: t.Set[str] versioned_test_names = set() # type: t.Set[str]
unversioned_test_names = {} # type: t.Dict[str, str] unversioned_test_names = {} # type: t.Dict[str, str]
directories = paths_to_dirs(list(paths))
display.info('Read %d sanity test ignore line(s) for %s from: %s' % (len(lines), ansible_label, self.relative_path), verbosity=1) display.info('Read %d sanity test ignore line(s) for %s from: %s' % (len(lines), ansible_label, self.relative_path), verbosity=1)
@ -282,9 +287,14 @@ class SanityIgnoreParser:
self.parse_errors.append((line_no, 1, "Line cannot start with a space")) self.parse_errors.append((line_no, 1, "Line cannot start with a space"))
continue continue
if path not in paths: if path.endswith(os.path.sep):
self.file_not_found_errors.append((line_no, path)) if path not in directories:
continue self.file_not_found_errors.append((line_no, path))
continue
else:
if path not in paths:
self.file_not_found_errors.append((line_no, path))
continue
if not codes: if not codes:
self.parse_errors.append((line_no, len(path), "Error code required after path")) self.parse_errors.append((line_no, len(path), "Error code required after path"))
@ -324,6 +334,10 @@ class SanityIgnoreParser:
continue continue
if path.endswith(os.path.sep) and not test.include_directories:
self.parse_errors.append((line_no, 1, "Sanity test '%s' does not support directory paths" % test_name))
continue
if commands and error_codes: if commands and error_codes:
self.parse_errors.append((line_no, len(path) + len(test_name) + 2, "Error code cannot contain both '!' and ':' characters")) self.parse_errors.append((line_no, len(path) + len(test_name) + 2, "Error code cannot contain both '!' and ':' characters"))
continue continue
@ -564,6 +578,11 @@ class SanityTest(ABC):
"""True if the test does not use test targets. Mutually exclusive with all_targets.""" """True if the test does not use test targets. Mutually exclusive with all_targets."""
return False return False
@property
def include_directories(self): # type: () -> bool
"""True if the test targets should include directories."""
return False
@property @property
def supported_python_versions(self): # type: () -> t.Optional[t.Tuple[str, ...]] def supported_python_versions(self): # type: () -> t.Optional[t.Tuple[str, ...]]
"""A tuple of supported Python versions or None if the test does not depend on specific Python versions.""" """A tuple of supported Python versions or None if the test does not depend on specific Python versions."""
@ -605,6 +624,7 @@ class SanityCodeSmellTest(SanityTest):
self.__all_targets = self.config.get('all_targets') # type: bool self.__all_targets = self.config.get('all_targets') # type: bool
self.__no_targets = self.config.get('no_targets') # type: bool self.__no_targets = self.config.get('no_targets') # type: bool
self.__include_directories = self.config.get('include_directories') # type: bool
else: else:
self.output = None self.output = None
self.extensions = [] self.extensions = []
@ -615,6 +635,7 @@ class SanityCodeSmellTest(SanityTest):
self.__all_targets = False self.__all_targets = False
self.__no_targets = True self.__no_targets = True
self.__include_directories = False
if self.no_targets: if self.no_targets:
mutually_exclusive = ( mutually_exclusive = (
@ -624,6 +645,7 @@ class SanityCodeSmellTest(SanityTest):
'text', 'text',
'ignore_self', 'ignore_self',
'all_targets', 'all_targets',
'include_directories',
) )
problems = sorted(name for name in mutually_exclusive if getattr(self, name)) problems = sorted(name for name in mutually_exclusive if getattr(self, name))
@ -641,6 +663,11 @@ class SanityCodeSmellTest(SanityTest):
"""True if the test does not use test targets. Mutually exclusive with all_targets.""" """True if the test does not use test targets. Mutually exclusive with all_targets."""
return self.__no_targets return self.__no_targets
@property
def include_directories(self): # type: () -> bool
"""True if the test targets should include directories."""
return self.__include_directories
def filter_targets(self, targets): # type: (t.List[TestTarget]) -> t.List[TestTarget] def filter_targets(self, targets): # type: (t.List[TestTarget]) -> t.List[TestTarget]
"""Return the given list of test targets, filtered to include only those relevant for the test.""" """Return the given list of test targets, filtered to include only those relevant for the test."""
if self.no_targets: if self.no_targets:

View file

@ -2,6 +2,8 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import os
from lib.sanity import ( from lib.sanity import (
SanityFailure, SanityFailure,
SanityIgnoreParser, SanityIgnoreParser,
@ -56,7 +58,7 @@ class IgnoresTest(SanityVersionNeutral):
# file not found errors # file not found errors
messages.extend(SanityMessage( messages.extend(SanityMessage(
message="File '%s' does not exist" % path, message="%s '%s' does not exist" % ("Directory" if path.endswith(os.path.sep) else "File", path),
path=sanity_ignore.relative_path, path=sanity_ignore.relative_path,
line=line, line=line,
column=1, column=1,

View file

@ -1,4 +1,4 @@
{ {
"no_targets": true, "include_directories": true,
"output": "path-message" "output": "path-message"
} }

View file

@ -7,8 +7,8 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import os import os
import re
import struct import struct
import sys
from ansible.module_utils.basic import to_bytes from ansible.module_utils.basic import to_bytes
@ -57,7 +57,7 @@ ILLEGAL_END_CHARS = [
def check_path(path, is_dir=False): def check_path(path, is_dir=False):
type_name = 'directory' if is_dir else 'file' type_name = 'directory' if is_dir else 'file'
file_name = os.path.basename(path) file_name = os.path.basename(path.rstrip(os.path.sep))
name = os.path.splitext(file_name)[0] name = os.path.splitext(file_name)[0]
if name.upper() in ILLEGAL_NAMES: if name.upper() in ILLEGAL_NAMES:
@ -74,23 +74,8 @@ def check_path(path, is_dir=False):
def main(): def main():
pattern = re.compile("^test/integration/targets/.*/backup") for path in sys.argv[1:] or sys.stdin.read().splitlines():
check_path(path, is_dir=path.endswith(os.path.sep))
for root, dirs, files in os.walk('.'):
if root == '.':
root = ''
elif root.startswith('./'):
root = root[2:]
# ignore test/integration/targets/*/backup
if pattern.match(root):
continue
for dir_name in dirs:
check_path(os.path.join(root, dir_name), is_dir=True)
for file_name in files:
check_path(os.path.join(root, file_name), is_dir=False)
if __name__ == '__main__': if __name__ == '__main__':