Conform ODL with last framework updates
[functest.git] / functest / core / testcase.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2016 Orange and others.
4 #
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
9
10 """Define the parent class of all Functest TestCases."""
11
12 import logging
13 import os
14
15 import functest.utils.functest_utils as ft_utils
16
17 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
18
19
20 class TestCase(object):
21     """Base model for single test case."""
22
23     EX_OK = os.EX_OK
24     """everything is OK"""
25
26     EX_RUN_ERROR = os.EX_SOFTWARE
27     """run() failed"""
28
29     EX_PUSH_TO_DB_ERROR = os.EX_SOFTWARE - 1
30     """push_to_db() failed"""
31
32     EX_TESTCASE_FAILED = os.EX_SOFTWARE - 2
33     """results are false"""
34
35     __logger = logging.getLogger(__name__)
36
37     def __init__(self, **kwargs):
38         self.details = {}
39         self.project_name = kwargs.get('project_name', 'functest')
40         self.case_name = kwargs.get('case_name', '')
41         self.criteria = kwargs.get('criteria', 100)
42         self.result = 0
43         self.start_time = 0
44         self.stop_time = 0
45
46     def get_duration(self):
47         """Return the duration of the test case.
48
49         Returns:
50             duration if start_time and stop_time are set
51             "XX:XX" otherwise.
52         """
53         try:
54             assert self.start_time
55             assert self.stop_time
56             if self.stop_time < self.start_time:
57                 return "XX:XX"
58             return "{0[0]:02.0f}:{0[1]:02.0f}".format(divmod(
59                 self.stop_time - self.start_time, 60))
60         except Exception:
61             self.__logger.error("Please run test before getting the duration")
62             return "XX:XX"
63
64     def is_successful(self):
65         """Interpret the result of the test case.
66
67         It allows getting the result of TestCase. It completes run()
68         which only returns the execution status.
69
70         It can be overriden if checking result is not suitable.
71
72         Returns:
73             TestCase.EX_OK if result is 'PASS'.
74             TestCase.EX_TESTCASE_FAILED otherwise.
75         """
76         try:
77             assert self.criteria
78             if (not isinstance(self.result, str) and
79                     not isinstance(self.criteria, str)):
80                 if self.result >= self.criteria:
81                     return TestCase.EX_OK
82             else:
83                 # Backward compatibility
84                 # It must be removed as soon as TestCase subclasses
85                 # stop setting result = 'PASS' or 'FAIL'.
86                 # In this case criteria is unread.
87                 self.__logger.warning(
88                     "Please update result which must be an int!")
89                 if self.result == 'PASS':
90                     return TestCase.EX_OK
91         except AssertionError:
92             self.__logger.error("Please run test before checking the results")
93         return TestCase.EX_TESTCASE_FAILED
94
95     def run(self, **kwargs):
96         """Run the test case.
97
98         It allows running TestCase and getting its execution
99         status.
100
101         The subclasses must override the default implementation which
102         is false on purpose.
103
104         The new implementation must set the following attributes to
105         push the results to DB:
106
107             * result,
108             * start_time,
109             * stop_time.
110
111         Args:
112             kwargs: Arbitrary keyword arguments.
113
114         Returns:
115             TestCase.EX_RUN_ERROR.
116         """
117         # pylint: disable=unused-argument
118         self.__logger.error("Run must be implemented")
119         return TestCase.EX_RUN_ERROR
120
121     def push_to_db(self):
122         """Push the results of the test case to the DB.
123
124         It allows publishing the results and to check the status.
125
126         It could be overriden if the common implementation is not
127         suitable. The following attributes must be set before pushing
128         the results to DB:
129
130             * project_name,
131             * case_name,
132             * result,
133             * start_time,
134             * stop_time.
135
136         Returns:
137             TestCase.EX_OK if results were pushed to DB.
138             TestCase.EX_PUSH_TO_DB_ERROR otherwise.
139         """
140         try:
141             assert self.project_name
142             assert self.case_name
143             assert self.start_time
144             assert self.stop_time
145             pub_result = 'PASS' if self.is_successful(
146                 ) == TestCase.EX_OK else 'FAIL'
147             if ft_utils.push_results_to_db(
148                     self.project_name, self.case_name, self.start_time,
149                     self.stop_time, pub_result, self.details):
150                 self.__logger.info(
151                     "The results were successfully pushed to DB")
152                 return TestCase.EX_OK
153             else:
154                 self.__logger.error("The results cannot be pushed to DB")
155                 return TestCase.EX_PUSH_TO_DB_ERROR
156         except Exception:  # pylint: disable=broad-except
157             self.__logger.exception("The results cannot be pushed to DB")
158             return TestCase.EX_PUSH_TO_DB_ERROR