diff options
Diffstat (limited to 'src/software/test/base.py')
-rw-r--r-- | src/software/test/base.py | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/src/software/test/base.py b/src/software/test/base.py new file mode 100644 index 0000000..4e56f46 --- /dev/null +++ b/src/software/test/base.py @@ -0,0 +1,209 @@ +# -*- encoding: utf-8 -*- +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Michal Minar <miminar@redhat.com> +# +""" +Common utilities and base class for all software tests. +""" + +import itertools +import os +import pywbem +import tempfile +import unittest +from subprocess import check_output + +import rpmcache + +def mark_dangerous(method): + """ + Decorator for methods of unittest.TestCase subclasses, that + skips dangerous tests, if environment variable does not allow them. + """ + if os.environ.get('LMI_RUN_DANGEROUS', '0') == '1': + return method + else: + return unittest.skip("This test is marked as dangerous.")(method) + +def mark_tedious(method): + """ + Decorator for methods of unittest.TestCase subclasses, that + skips tedious tests. Those running for very long time and usually + need a lot of memory. Environment variable "LMI_RUN_TEDIOUS" can + allow them + """ + if os.environ.get('LMI_RUN_TEDIOUS', '0') == '1': + return method + else: + return unittest.skip("This test is marked as tedious.")(method) + +def get_pkg_files(pkg): + """ + Tries to make a the heterogenous and smallest set of test files from + package. + @param pkg must be installed package + @return list of few files installed by pkg + """ + cmd = ['rpm', '-ql', pkg.name] + output = check_output(cmd) + configs = set() + docs = set() + dirs = set() + files = set() + symlinks = set() + for fpath in output.splitlines(): #pylint: disable=E1103 + if ( len(dirs) == 0 + and not os.path.islink(fpath) + and os.path.isdir(fpath)): + dirs.add(fpath) + elif len(symlinks) == 0 and os.path.islink(fpath): + symlinks.add(fpath) + elif not os.path.islink(fpath) and os.path.isfile(fpath): + if len(configs) == 0 and rpmcache.has_pkg_config_file(pkg, fpath): + configs.add(fpath) + elif len(docs) == 0 and rpmcache.has_pkg_doc_file(pkg, fpath): + docs.add(fpath) + elif len(files) == 0: + files.add(fpath) + out = list(configs) + list(docs) + list(dirs) + list(symlinks) + if len(files) > 0 and len(docs) == 0 and len(symlinks) == 0: + out += list(files) + return out + +class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904 + """ + Base class for all LMI Software test classes. + """ + + CLASS_NAME = "Define in subclass" + + # will be filled when first needed + # it's a dictionary with items (pkg_name, [file_path1, ...]) + PKGDB_FILES = None + + @classmethod + def get_pkgdb_files(cls): + """ + @return dictionary { pkg_name: ["file_path1, ...] } + """ + if cls.PKGDB_FILES is not None: + return cls.PKGDB_FILES + SoftwareBaseTestCase.PKGDB_FILES = res = dict( + (pkg.name, get_pkg_files(pkg)) for pkg in itertools.chain( + cls.safe_pkgs, cls.dangerous_pkgs)) + return res + + @classmethod + def needs_pkgdb_files(cls): + """subclass may override this, if it needs PKGDB_FILES database""" + return False + + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + self.longMessage = True + + def setUp(self): + unittest.TestCase.setUp(self) + self.objpath = pywbem.CIMInstanceName( + namespace="root/cimv2", classname=self.CLASS_NAME) + + def install_pkg(self, pkg, newer=True, repolist=None): + """ + Use this method instead of function in rpmcache in tests. + """ + if repolist is None: + repolist = self.test_repos + return rpmcache.install_pkg(pkg, newer, repolist) + + def ensure_pkg_installed(self, pkg, newer=True, repolist=None): + """ + Use this method instead of function in rpmcache in tests. + """ + if repolist is None: + repolist = self.test_repos + return rpmcache.ensure_pkg_installed(pkg, newer, repolist) + + def assertIsSubclass(self, cls, base_cls): #pylint: disable=C0103 + """ + Checks, whether cls is subclass of base_cls from CIM perspective. + @param cls name of subclass + @param base_cls name of base class + """ + if not isinstance(cls, basestring): + raise TypeError("cls must be a string") + if not isinstance(base_cls, basestring): + raise TypeError("base_cls must be a string") + return self.assertTrue(pywbem.is_subclass(self.conn, + "root/cimv2", base_cls, cls)) + + def assertEqual(self, fst, snd, *args, **kwargs): + """ + Modify assertEqual for instance names comparing only important + properties. + """ + if ( isinstance(fst, pywbem.CIMInstanceName) + and isinstance(snd, pywbem.CIMInstanceName) + and fst.classname == snd.classname + and fst.namespace == snd.namespace + and fst.keybindings == snd.keybindings): + return True + unittest.TestCase.assertEqual(self, fst, snd, *args, **kwargs) + + @classmethod + def setUpClass(cls): + cls.url = os.environ.get('LMI_CIMOM_URL', 'http://localhost:5988') + cls.user = os.environ.get('LMI_CIMOM_USERNAME', '') + cls.password = os.environ.get('LMI_CIMOM_PASSWORD', '') + cls.broker = os.environ.get('LMI_CIMOM_BROKER', None) + cls.conn = pywbem.WBEMConnection(cls.url, (cls.user, cls.password)) + cls.run_dangerous = ( + os.environ.get('LMI_RUN_DANGEROUS', '0') == '1') + cls.test_repos = os.environ.get( + 'LMI_SOFTWARE_TEST_REPOS', '').split(',') + use_cache = os.environ.get('LMI_SOFTWARE_USE_CACHE', '0') == '1' + cls.cache_dir = None + if use_cache: + cls.cache_dir = os.environ.get('LMI_SOFTWARE_CACHE_DIR', None) + if cls.cache_dir is None: + cls.cache_dir = tempfile.mkdtemp(suffix="software_database") + if cls.cache_dir: + cls.prev_dir = os.getcwd() + if not os.path.exists(cls.cache_dir): + os.makedirs(cls.cache_dir) + # rpm packages are expected to be in CWD + os.chdir(cls.cache_dir) + cls.safe_pkgs, cls.dangerous_pkgs = rpmcache.get_pkg_database( + use_cache=use_cache, + dangerous=cls.run_dangerous, + repolist=cls.test_repos, + cache_dir=cls.cache_dir if use_cache else None) + for pkg in cls.dangerous_pkgs: + if not rpmcache.is_pkg_installed(pkg.name): + rpmcache.install_pkg(pkg, repolist=cls.test_repos) + if cls.needs_pkgdb_files(): + cls.pkgdb_files = cls.get_pkgdb_files() + + @classmethod + def tearDownClass(cls): + if cls.run_dangerous: + for pkg in cls.dangerous_pkgs: + if rpmcache.is_pkg_installed(pkg.name): + rpmcache.remove_pkg(pkg.name) + if hasattr(cls, "prev_dir"): + os.chdir(cls.prev_dir) + |