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
20 from functest.utils import env
26 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
29 class TestCase(object):
30 """Base model for single test case."""
33 """everything is OK"""
35 EX_RUN_ERROR = os.EX_SOFTWARE
38 EX_PUSH_TO_DB_ERROR = os.EX_SOFTWARE - 1
39 """push_to_db() failed"""
41 EX_TESTCASE_FAILED = os.EX_SOFTWARE - 2
42 """results are false"""
44 _job_name_rule = "(dai|week)ly-(.+?)-[0-9]*"
45 _headers = {'Content-Type': 'application/json'}
46 __logger = logging.getLogger(__name__)
48 def __init__(self, **kwargs):
50 self.project_name = kwargs.get('project_name', 'functest')
51 self.case_name = kwargs.get('case_name', '')
52 self.criteria = kwargs.get('criteria', 100)
59 assert self.project_name
61 result = 'PASS' if(self.is_successful(
62 ) == TestCase.EX_OK) else 'FAIL'
63 msg = prettytable.PrettyTable(
64 header_style='upper', padding_width=5,
65 field_names=['test case', 'project', 'duration',
67 msg.add_row([self.case_name, self.project_name,
68 self.get_duration(), result])
69 return msg.get_string()
70 except AssertionError:
71 self.__logger.error("We cannot print invalid objects")
72 return super(TestCase, self).__str__()
74 def get_duration(self):
75 """Return the duration of the test case.
78 duration if start_time and stop_time are set
82 assert self.start_time
84 if self.stop_time < self.start_time:
86 return "{0[0]:02.0f}:{0[1]:02.0f}".format(divmod(
87 self.stop_time - self.start_time, 60))
88 except Exception: # pylint: disable=broad-except
89 self.__logger.error("Please run test before getting the duration")
92 def is_successful(self):
93 """Interpret the result of the test case.
95 It allows getting the result of TestCase. It completes run()
96 which only returns the execution status.
98 It can be overriden if checking result is not suitable.
101 TestCase.EX_OK if result is 'PASS'.
102 TestCase.EX_TESTCASE_FAILED otherwise.
106 assert self.result is not None
107 if (not isinstance(self.result, str) and
108 not isinstance(self.criteria, str)):
109 if self.result >= self.criteria:
110 return TestCase.EX_OK
112 # Backward compatibility
113 # It must be removed as soon as TestCase subclasses
114 # stop setting result = 'PASS' or 'FAIL'.
115 # In this case criteria is unread.
116 self.__logger.warning(
117 "Please update result which must be an int!")
118 if self.result == 'PASS':
119 return TestCase.EX_OK
120 except AssertionError:
121 self.__logger.error("Please run test before checking the results")
122 return TestCase.EX_TESTCASE_FAILED
124 def run(self, **kwargs):
125 """Run the test case.
127 It allows running TestCase and getting its execution
130 The subclasses must override the default implementation which
133 The new implementation must set the following attributes to
134 push the results to DB:
141 kwargs: Arbitrary keyword arguments.
144 TestCase.EX_RUN_ERROR.
146 # pylint: disable=unused-argument
147 self.__logger.error("Run must be implemented")
148 return TestCase.EX_RUN_ERROR
150 @decorators.can_dump_request_to_file
151 def push_to_db(self):
152 """Push the results of the test case to the DB.
154 It allows publishing the results and checking the status.
156 It could be overriden if the common implementation is not
159 The following attributes must be set before pushing the results to DB:
167 The next vars must be set in env:
176 TestCase.EX_OK if results were pushed to DB.
177 TestCase.EX_PUSH_TO_DB_ERROR otherwise.
180 assert self.project_name
181 assert self.case_name
182 assert self.start_time
183 assert self.stop_time
184 url = env.get('TEST_DB_URL')
185 data = {"project_name": self.project_name,
186 "case_name": self.case_name,
187 "details": self.details}
188 data["installer"] = env.get('INSTALLER_TYPE')
189 data["scenario"] = env.get('DEPLOY_SCENARIO')
190 data["pod_name"] = env.get('NODE_NAME')
191 data["build_tag"] = env.get('BUILD_TAG')
192 data["criteria"] = 'PASS' if self.is_successful(
193 ) == TestCase.EX_OK else 'FAIL'
194 data["start_date"] = datetime.fromtimestamp(
195 self.start_time).strftime('%Y-%m-%d %H:%M:%S')
196 data["stop_date"] = datetime.fromtimestamp(
197 self.stop_time).strftime('%Y-%m-%d %H:%M:%S')
199 data["version"] = re.search(
200 TestCase._job_name_rule,
201 env.get('BUILD_TAG')).group(2)
202 except Exception: # pylint: disable=broad-except
203 data["version"] = "unknown"
205 url, data=json.dumps(data, sort_keys=True),
206 headers=self._headers)
207 req.raise_for_status()
209 "The results were successfully pushed to DB %s", url)
210 except AssertionError:
211 self.__logger.exception(
212 "Please run test before publishing the results")
213 return TestCase.EX_PUSH_TO_DB_ERROR
214 except requests.exceptions.HTTPError:
215 self.__logger.exception("The HTTP request raises issues")
216 return TestCase.EX_PUSH_TO_DB_ERROR
217 except Exception: # pylint: disable=broad-except
218 self.__logger.exception("The results cannot be pushed to DB")
219 return TestCase.EX_PUSH_TO_DB_ERROR
220 return TestCase.EX_OK
223 """Clean the resources.
225 It can be overriden if resources must be deleted after
226 running the test case.