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