3 # Copyright (c) 2016 Orange and others.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 """Define the parent class of all Functest TestCases."""
12 from datetime import datetime
19 from functest.utils import decorators
25 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
28 class TestCase(object):
29 """Base model for single test case."""
32 """everything is OK"""
34 EX_RUN_ERROR = os.EX_SOFTWARE
37 EX_PUSH_TO_DB_ERROR = os.EX_SOFTWARE - 1
38 """push_to_db() failed"""
40 EX_TESTCASE_FAILED = os.EX_SOFTWARE - 2
41 """results are false"""
43 _job_name_rule = "(dai|week)ly-(.+?)-[0-9]*"
44 _headers = {'Content-Type': 'application/json'}
45 __logger = logging.getLogger(__name__)
47 def __init__(self, **kwargs):
49 self.project_name = kwargs.get('project_name', 'functest')
50 self.case_name = kwargs.get('case_name', '')
51 self.criteria = kwargs.get('criteria', 100)
58 assert self.project_name
60 result = 'PASS' if(self.is_successful(
61 ) == TestCase.EX_OK) else 'FAIL'
62 msg = prettytable.PrettyTable(
63 header_style='upper', padding_width=5,
64 field_names=['test case', 'project', 'duration',
66 msg.add_row([self.case_name, self.project_name,
67 self.get_duration(), result])
68 return msg.get_string()
69 except AssertionError:
70 self.__logger.error("We cannot print invalid objects")
71 return super(TestCase, self).__str__()
73 def get_duration(self):
74 """Return the duration of the test case.
77 duration if start_time and stop_time are set
81 assert self.start_time
83 if self.stop_time < self.start_time:
85 return "{0[0]:02.0f}:{0[1]:02.0f}".format(divmod(
86 self.stop_time - self.start_time, 60))
87 except Exception: # pylint: disable=broad-except
88 self.__logger.error("Please run test before getting the duration")
91 def is_successful(self):
92 """Interpret the result of the test case.
94 It allows getting the result of TestCase. It completes run()
95 which only returns the execution status.
97 It can be overriden if checking result is not suitable.
100 TestCase.EX_OK if result is 'PASS'.
101 TestCase.EX_TESTCASE_FAILED otherwise.
105 assert self.result is not None
106 if (not isinstance(self.result, str) and
107 not isinstance(self.criteria, str)):
108 if self.result >= self.criteria:
109 return TestCase.EX_OK
111 # Backward compatibility
112 # It must be removed as soon as TestCase subclasses
113 # stop setting result = 'PASS' or 'FAIL'.
114 # In this case criteria is unread.
115 self.__logger.warning(
116 "Please update result which must be an int!")
117 if self.result == 'PASS':
118 return TestCase.EX_OK
119 except AssertionError:
120 self.__logger.error("Please run test before checking the results")
121 return TestCase.EX_TESTCASE_FAILED
123 def run(self, **kwargs):
124 """Run the test case.
126 It allows running TestCase and getting its execution
129 The subclasses must override the default implementation which
132 The new implementation must set the following attributes to
133 push the results to DB:
140 kwargs: Arbitrary keyword arguments.
143 TestCase.EX_RUN_ERROR.
145 # pylint: disable=unused-argument
146 self.__logger.error("Run must be implemented")
147 return TestCase.EX_RUN_ERROR
149 @decorators.can_dump_request_to_file
150 def push_to_db(self):
151 """Push the results of the test case to the DB.
153 It allows publishing the results and checking the status.
155 It could be overriden if the common implementation is not
158 The following attributes must be set before pushing the results to DB:
166 The next vars must be set in env:
175 TestCase.EX_OK if results were pushed to DB.
176 TestCase.EX_PUSH_TO_DB_ERROR otherwise.
179 assert self.project_name
180 assert self.case_name
181 assert self.start_time
182 assert self.stop_time
183 url = os.environ['TEST_DB_URL']
184 data = {"project_name": self.project_name,
185 "case_name": self.case_name,
186 "details": self.details}
187 data["installer"] = os.environ['INSTALLER_TYPE']
188 data["scenario"] = os.environ['DEPLOY_SCENARIO']
189 data["pod_name"] = os.environ['NODE_NAME']
190 data["build_tag"] = os.environ['BUILD_TAG']
191 data["criteria"] = 'PASS' if self.is_successful(
192 ) == TestCase.EX_OK else 'FAIL'
193 data["start_date"] = datetime.fromtimestamp(
194 self.start_time).strftime('%Y-%m-%d %H:%M:%S')
195 data["stop_date"] = datetime.fromtimestamp(
196 self.stop_time).strftime('%Y-%m-%d %H:%M:%S')
198 data["version"] = re.search(
199 TestCase._job_name_rule,
200 os.environ['BUILD_TAG']).group(2)
201 except Exception: # pylint: disable=broad-except
202 data["version"] = "unknown"
204 url, data=json.dumps(data, sort_keys=True),
205 headers=self._headers)
206 req.raise_for_status()
208 "The results were successfully pushed to DB %s", url)
209 except AssertionError:
210 self.__logger.exception(
211 "Please run test before publishing the results")
212 return TestCase.EX_PUSH_TO_DB_ERROR
213 except KeyError as exc:
214 self.__logger.error("Please set env var: " + str(exc))
215 return TestCase.EX_PUSH_TO_DB_ERROR
216 except requests.exceptions.HTTPError:
217 self.__logger.exception("The HTTP request raises issues")
218 return TestCase.EX_PUSH_TO_DB_ERROR
219 except Exception: # pylint: disable=broad-except
220 self.__logger.exception("The results cannot be pushed to DB")
221 return TestCase.EX_PUSH_TO_DB_ERROR
222 return TestCase.EX_OK
225 """Clean the resources.
227 It can be overriden if resources must be deleted after
228 running the test case.