aboutsummaryrefslogtreecommitdiffstats
path: root/functest/ci/run_tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'functest/ci/run_tests.py')
-rw-r--r--functest/ci/run_tests.py121
1 files changed, 67 insertions, 54 deletions
diff --git a/functest/ci/run_tests.py b/functest/ci/run_tests.py
index b8da3be6..166dbfc9 100644
--- a/functest/ci/run_tests.py
+++ b/functest/ci/run_tests.py
@@ -7,19 +7,24 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
+""" The entry of running tests:
+1) Parses functest/ci/testcases.yaml to check which testcase(s) to be run
+2) Execute the common operations on every testcase (run, push results to db...)
+3) Return the right status code
+"""
+
import argparse
import enum
import importlib
import logging
import logging.config
import os
-import pkg_resources
import re
import sys
import textwrap
+import pkg_resources
import prettytable
-import six
import functest.ci.tier_builder as tb
import functest.core.testcase as testcase
@@ -28,26 +33,32 @@ import functest.utils.openstack_utils as os_utils
from functest.utils.constants import CONST
# __name__ cannot be used here
-logger = logging.getLogger('functest.ci.run_tests')
+LOGGER = logging.getLogger('functest.ci.run_tests')
CONFIG_FUNCTEST_PATH = pkg_resources.resource_filename(
'functest', 'ci/config_functest.yaml')
class Result(enum.Enum):
+ """The overall result in enumerated type"""
+ # pylint: disable=too-few-public-methods
EX_OK = os.EX_OK
EX_ERROR = -1
class BlockingTestFailed(Exception):
+ """Exception when the blocking test fails"""
pass
class TestNotEnabled(Exception):
+ """Exception when the test is not enabled"""
pass
class RunTestsParser(object):
+ """Parser to run tests"""
+ # pylint: disable=too-few-public-methods
def __init__(self):
self.parser = argparse.ArgumentParser()
@@ -63,11 +74,19 @@ class RunTestsParser(object):
"database (default=false).",
action="store_true")
- def parse_args(self, argv=[]):
+ def parse_args(self, argv=None):
+ """Parse arguments.
+
+ It can call sys.exit if arguments are incorrect.
+
+ Returns:
+ the arguments from cmdline
+ """
return vars(self.parser.parse_args(argv))
class Runner(object):
+ """Runner class"""
def __init__(self):
self.executed_test_cases = {}
@@ -81,42 +100,34 @@ class Runner(object):
@staticmethod
def source_rc_file():
+ """Set the environmental vars from openstack.creds"""
+
rc_file = CONST.__getattribute__('openstack_creds')
if not os.path.isfile(rc_file):
raise Exception("RC file %s does not exist..." % rc_file)
- logger.debug("Sourcing the OpenStack RC file...")
+ LOGGER.debug("Sourcing the OpenStack RC file...")
os_utils.source_credentials(rc_file)
- for key, value in six.iteritems(os.environ):
- if re.search("OS_", key):
- if key == 'OS_AUTH_URL':
- CONST.__setattr__('OS_AUTH_URL', value)
- elif key == 'OS_USERNAME':
- CONST.__setattr__('OS_USERNAME', value)
- elif key == 'OS_TENANT_NAME':
- CONST.__setattr__('OS_TENANT_NAME', value)
- elif key == 'OS_PASSWORD':
- CONST.__setattr__('OS_PASSWORD', value)
- elif key == "OS_PROJECT_DOMAIN_NAME":
- CONST.__setattr__('OS_PROJECT_DOMAIN_NAME', value)
@staticmethod
def get_run_dict(testname):
+ """Obtain the the 'run' block of the testcase from testcases.yaml"""
try:
- dict = ft_utils.get_dict_by_test(testname)
- if not dict:
- logger.error("Cannot get {}'s config options".format(testname))
- elif 'run' in dict:
- return dict['run']
+ dic_testcase = ft_utils.get_dict_by_test(testname)
+ if not dic_testcase:
+ LOGGER.error("Cannot get %s's config options", testname)
+ elif 'run' in dic_testcase:
+ return dic_testcase['run']
return None
- except Exception:
- logger.exception("Cannot get {}'s config options".format(testname))
+ except Exception: # pylint: disable=broad-except
+ LOGGER.exception("Cannot get %s's config options", testname)
return None
def run_test(self, test):
+ """Run one test case"""
if not test.is_enabled():
raise TestNotEnabled(
"The test case {} is not enabled".format(test.get_name()))
- logger.info("Running test case '%s'...", test.get_name())
+ LOGGER.info("Running test case '%s'...", test.get_name())
result = testcase.TestCase.EX_RUN_ERROR
run_dict = self.get_run_dict(test.get_name())
if run_dict:
@@ -140,33 +151,32 @@ class Runner(object):
result = test_case.is_successful()
else:
result = testcase.TestCase.EX_OK
- logger.info("Test result:\n\n%s\n", test_case)
+ LOGGER.info("Test result:\n\n%s\n", test_case)
if self.clean_flag:
test_case.clean()
except ImportError:
- logger.exception("Cannot import module {}".format(
- run_dict['module']))
+ LOGGER.exception("Cannot import module %s", run_dict['module'])
except AttributeError:
- logger.exception("Cannot get class {}".format(
- run_dict['class']))
+ LOGGER.exception("Cannot get class %s", run_dict['class'])
else:
raise Exception("Cannot import the class for the test case.")
return result
def run_tier(self, tier):
+ """Run one tier"""
tier_name = tier.get_name()
tests = tier.get_tests()
- if tests is None or len(tests) == 0:
- logger.info("There are no supported test cases in this tier "
+ if not tests:
+ LOGGER.info("There are no supported test cases in this tier "
"for the given scenario")
self.overall_result = Result.EX_ERROR
else:
- logger.info("Running tier '%s'" % tier_name)
+ LOGGER.info("Running tier '%s'", tier_name)
for test in tests:
self.run_test(test)
test_case = self.executed_test_cases[test.get_name()]
if test_case.is_successful() != testcase.TestCase.EX_OK:
- logger.error("The test case '%s' failed.", test.get_name())
+ LOGGER.error("The test case '%s' failed.", test.get_name())
if test.get_project() == "functest":
self.overall_result = Result.EX_ERROR
if test.is_blocking():
@@ -176,13 +186,14 @@ class Runner(object):
return self.overall_result
def run_all(self):
+ """Run all available testcases"""
tiers_to_run = []
msg = prettytable.PrettyTable(
header_style='upper', padding_width=5,
field_names=['tiers', 'order', 'CI Loop', 'description',
'testcases'])
for tier in self._tiers.get_tiers():
- if (len(tier.get_tests()) != 0 and
+ if (tier.get_tests() and
re.search(CONST.__getattribute__('CI_LOOP'),
tier.get_ci_loop()) is not None):
tiers_to_run.append(tier)
@@ -191,11 +202,12 @@ class Runner(object):
textwrap.fill(tier.description, width=40),
textwrap.fill(' '.join([str(x.get_name(
)) for x in tier.get_tests()]), width=40)])
- logger.info("TESTS TO BE EXECUTED:\n\n%s\n", msg)
+ LOGGER.info("TESTS TO BE EXECUTED:\n\n%s\n", msg)
for tier in tiers_to_run:
self.run_tier(tier)
def main(self, **kwargs):
+ """Entry point of class Runner"""
if 'noclean' in kwargs:
self.clean_flag = not kwargs['noclean']
if 'report' in kwargs:
@@ -203,59 +215,59 @@ class Runner(object):
try:
if 'test' in kwargs:
self.source_rc_file()
- logger.debug("Test args: %s", kwargs['test'])
+ LOGGER.debug("Test args: %s", kwargs['test'])
if self._tiers.get_tier(kwargs['test']):
self.run_tier(self._tiers.get_tier(kwargs['test']))
elif self._tiers.get_test(kwargs['test']):
result = self.run_test(
self._tiers.get_test(kwargs['test']))
if result != testcase.TestCase.EX_OK:
- logger.error("The test case '%s' failed.",
+ LOGGER.error("The test case '%s' failed.",
kwargs['test'])
self.overall_result = Result.EX_ERROR
elif kwargs['test'] == "all":
self.run_all()
else:
- logger.error("Unknown test case or tier '%s', "
- "or not supported by "
- "the given scenario '%s'."
- % (kwargs['test'],
- CONST.__getattribute__('DEPLOY_SCENARIO')))
- logger.debug("Available tiers are:\n\n%s",
+ LOGGER.error("Unknown test case or tier '%s', or not "
+ "supported by the given scenario '%s'.",
+ kwargs['test'],
+ CONST.__getattribute__('DEPLOY_SCENARIO'))
+ LOGGER.debug("Available tiers are:\n\n%s",
self._tiers)
return Result.EX_ERROR
else:
self.run_all()
except BlockingTestFailed:
pass
- except Exception:
- logger.exception("Failures when running testcase(s)")
+ except Exception: # pylint: disable=broad-except
+ LOGGER.exception("Failures when running testcase(s)")
self.overall_result = Result.EX_ERROR
if not self._tiers.get_test(kwargs['test']):
self.summary(self._tiers.get_tier(kwargs['test']))
- logger.info("Execution exit value: %s" % self.overall_result)
+ LOGGER.info("Execution exit value: %s", self.overall_result)
return self.overall_result
def summary(self, tier=None):
+ """To generate functest report showing the overall results"""
msg = prettytable.PrettyTable(
header_style='upper', padding_width=5,
field_names=['env var', 'value'])
for env_var in ['INSTALLER_TYPE', 'DEPLOY_SCENARIO', 'BUILD_TAG',
'CI_LOOP']:
msg.add_row([env_var, CONST.__getattribute__(env_var)])
- logger.info("Deployment description:\n\n%s\n", msg)
+ LOGGER.info("Deployment description:\n\n%s\n", msg)
msg = prettytable.PrettyTable(
header_style='upper', padding_width=5,
field_names=['test case', 'project', 'tier',
'duration', 'result'])
tiers = [tier] if tier else self._tiers.get_tiers()
- for tier in tiers:
- for test in tier.get_tests():
+ for each_tier in tiers:
+ for test in each_tier.get_tests():
try:
test_case = self.executed_test_cases[test.get_name()]
except KeyError:
msg.add_row([test.get_name(), test.get_project(),
- tier.get_name(), "00:00", "SKIP"])
+ each_tier.get_name(), "00:00", "SKIP"])
else:
result = 'PASS' if(test_case.is_successful(
) == test_case.EX_OK) else 'FAIL'
@@ -263,13 +275,14 @@ class Runner(object):
[test_case.case_name, test_case.project_name,
self._tiers.get_tier_name(test_case.case_name),
test_case.get_duration(), result])
- for test in tier.get_skipped_test():
+ for test in each_tier.get_skipped_test():
msg.add_row([test.get_name(), test.get_project(),
- tier.get_name(), "00:00", "SKIP"])
- logger.info("FUNCTEST REPORT:\n\n%s\n", msg)
+ each_tier.get_name(), "00:00", "SKIP"])
+ LOGGER.info("FUNCTEST REPORT:\n\n%s\n", msg)
def main():
+ """Entry point"""
logging.config.fileConfig(pkg_resources.resource_filename(
'functest', 'ci/logging.ini'))
logging.captureWarnings(True)