From a2cfd4968989fbac4e247c7758ed9cf25e6fc1ef Mon Sep 17 00:00:00 2001 From: Sergey Kraynev Date: Wed, 3 Aug 2016 14:45:26 +0300 Subject: [PATCH] Add test for packages structure Change-Id: I1f9cd14f41edc9f825d86c4867426a9805af2882 --- unittests/base.py | 12 +++ unittests/test_namespaces.py | 71 ------------- unittests/test_packages.py | 186 +++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 unittests/base.py delete mode 100644 unittests/test_namespaces.py create mode 100644 unittests/test_packages.py diff --git a/unittests/base.py b/unittests/base.py new file mode 100644 index 0000000..4e2d90c --- /dev/null +++ b/unittests/base.py @@ -0,0 +1,12 @@ +import os + +import testtools + + +class UnittestBaseCiCd(testtools.TestCase): + + def setUp(self): + super(UnittestBaseCiCd, self).setUp() + # TODO: should be fixed future with some common approach for all tests + root_dir = os.path.dirname(os.path.abspath(__file__)).rsplit('/', 1)[0] + self.apps_dir = os.path.join(root_dir, 'murano-apps') diff --git a/unittests/test_namespaces.py b/unittests/test_namespaces.py deleted file mode 100644 index 2f03f49..0000000 --- a/unittests/test_namespaces.py +++ /dev/null @@ -1,71 +0,0 @@ -import os -import re -import yaml - -import testtools - - -class TestNamespaces(testtools.TestCase): - def get_list_of_classes(self): - # TODO: should be fixed future with some common approach for all tests - root_dir = os.path.dirname(os.path.abspath(__file__)).rsplit('/', 1)[0] - apps_dir = os.path.join(root_dir, 'murano-apps') - class_names = [] - - for path, dirs, files in os.walk(apps_dir): - if path.endswith('Classes'): - names = [os.path.join(path, f) - for f in files if f.endswith('.yaml')] - class_names.extend(names) - return class_names - - def get_namespaces(self, cls_name): - # workaround for PyYAML bug: http://pyyaml.org/ticket/221 - ############################### - class YaqlYamlLoader(yaml.Loader): - pass - - def yaql_constructor(loader, node): - return - - YaqlYamlLoader.add_constructor(u'!yaql', yaql_constructor) - ############################### - - # parse file - parsed_data = '' - with open(cls_name) as f: - parsed_data = yaml.load(f, YaqlYamlLoader) - - n_spaces = parsed_data.get('Namespaces') - if n_spaces is None: - msg = 'File "%s" does not content "Namespaces" section' % cls_name - raise ValueError(msg) - # get only names of namespaces - names = n_spaces.keys() - # remove main namespace from the list - names.remove('=') - return names - - def check_name(self, namespace, cls_name, error_list): - # read file - data = '' - with open(cls_name) as f: - data = f.read() - - regexp_str = '[^a-zA-Z]%s:[a-zA-Z]+' % namespace - regexp = re.compile(regexp_str) - if len(regexp.findall(data)) == 0: - msg = ('Namespace "%s" is not used in the "%s" and should ' - 'be removed from list of Namespaces' % (namespace, cls_name)) - error_list.append(msg) - - def test_namespaces(self): - error_list = [] - for cls_name in self.get_list_of_classes(): - for ns in self.get_namespaces(cls_name): - self.check_name(ns, cls_name, error_list) - - error_string = "\n".join(error_list) - msg = "Test detects follow list of errors: \n%s" % error_string - - self.assertEqual(0, len(error_list), msg) diff --git a/unittests/test_packages.py b/unittests/test_packages.py new file mode 100644 index 0000000..c0486e4 --- /dev/null +++ b/unittests/test_packages.py @@ -0,0 +1,186 @@ +import os +import re +import yaml + +import base + + +class TestNamespaces(base.UnittestBaseCiCd): + + def get_list_of_classes(self): + class_names = [] + + for path, dirs, files in os.walk(self.apps_dir): + if path.endswith('Classes'): + names = [os.path.join(path, f) + for f in files if f.endswith('.yaml')] + class_names.extend(names) + return class_names + + def get_namespaces(self, cls_name): + # workaround for PyYAML bug: http://pyyaml.org/ticket/221 + ############################### + class YaqlYamlLoader(yaml.Loader): + pass + + def yaql_constructor(loader, node): + return + + YaqlYamlLoader.add_constructor(u'!yaql', yaql_constructor) + ############################### + + # parse file + parsed_data = '' + with open(cls_name) as f: + parsed_data = yaml.load(f, YaqlYamlLoader) + + n_spaces = parsed_data.get('Namespaces') + if n_spaces is None: + msg = 'File "%s" does not content "Namespaces" section' % cls_name + raise ValueError(msg) + # get only names of namespaces + names = n_spaces.keys() + # remove main namespace from the list + names.remove('=') + return names + + def check_name(self, namespace, cls_name, error_list): + # read file + data = '' + with open(cls_name) as f: + data = f.read() + + regexp_str = '[^a-zA-Z]%s:[a-zA-Z]+' % namespace + regexp = re.compile(regexp_str) + if len(regexp.findall(data)) == 0: + msg = ('Namespace "%s" is not used in the "%s" and should ' + 'be removed from list of Namespaces' % (namespace, cls_name)) + error_list.append(msg) + + def test_namespaces(self): + error_list = [] + for cls_name in self.get_list_of_classes(): + for ns in self.get_namespaces(cls_name): + self.check_name(ns, cls_name, error_list) + + error_string = "\n".join(error_list) + msg = "Test detects follow list of errors: \n%s" % error_string + + self.assertEqual(0, len(error_list), msg) + + +class TestPackageStructure(base.UnittestBaseCiCd): + ''' + Basic package can be described as example below, + but for test purposes will be used structure similar on output + for os.walk: + + class Content(object): + def __init__(self, dirs=None, files=None): + self.dirs = dirs + self.files = files + + package_structure = Content( + files=[], dirs={'package': + Content(dirs={'Classes': Content(dirs=[], + files=['*.yaml']), + 'Resources': Content(dirs={'scripts': Content()}, + files=['*.template']), + 'UI': Content(dirs=[], + files=['ui.yaml']) + }, + files=['manifest.yaml', + 'logo.png', + 'LICENSE' + ] + ) + } + ) + ''' + package_structure = { + '': { + 'files': [], + 'dirs': ['package']}, + 'package': { + 'files': ['manifest.yaml', 'logo.png', 'LICENSE', + re.compile('\w*.lst')], + 'dirs': ['Classes', 'Resources', 'UI']}, + 'package/Classes': { + 'files': [re.compile('\w*.yaml')], + 'dirs': None}, + 'package/Resources': { + 'files': [re.compile('\w*.template'), re.compile('.*')], + 'dirs': ['scripts']}, + 'package/Resources/scripts': { + # None means, that there are no any requirements for this value + 'files': None, + 'dirs': None}, + 'package/UI': { + 'files': ['ui.yaml'], + 'dirs': []}, + } + + def find_incorrect_items(self, expected, real, errors): + if expected is None: + return + real_set = set(real) + for val in expected: + try: + matches = filter(val.match, real_set) + except AttributeError: + # it's not a pattern and we just need to check, + # that it's in list + if val in real_set: + real_set.remove(val) + else: + # remove matches from real_set + real_set -= set(matches) + + if real_set: + return real_set + + def show_patterns_in_error(self, expected): + output = [] + for val in expected: + try: + output.append(val.pattern) + except AttributeError: + output.append(val) + return output + + def check_package_tree(self, package_name): + errors = [] + base_path = os.path.join(self.apps_dir, package_name) + for path, dirs, files in os.walk(base_path, topdown=True): + # remove with backslash symbol '/' + internal_path = path[len(base_path)+1:] + if internal_path not in self.package_structure: + continue + p_files = self.package_structure[internal_path]['files'] + p_dirs = self.package_structure[internal_path]['dirs'] + wrong_files = self.find_incorrect_items(p_files, files, errors) + if wrong_files: + allowed_vals = self.show_patterns_in_error(p_files) + msg = ('Path: "%s" contains wrong files: %s. Allowed values ' + 'are: %s ' % (path, str(wrong_files), allowed_vals)) + errors.append(msg) + + wrong_dirs = self.find_incorrect_items(p_dirs, dirs, errors) + if wrong_dirs: + allowed_vals = self.show_patterns_in_error(p_dirs) + msg = ('Path: "%s" contains wrong dirs: %s. Allowed values ' + 'are: %s ' % (path, str(wrong_dirs), allowed_vals)) + errors.append(msg) + + return errors + + def test_packages(self): + error_list = [] + packages = os.listdir(self.apps_dir) + for package in packages: + error_list.extend(self.check_package_tree(package)) + + error_string = "\n".join(error_list) + msg = "Test detects follow list of errors: \n%s" % error_string + + self.assertEqual(0, len(error_list), msg)