Merge "Additional Unit Tests for core modules"
[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             if (not isinstance(self.result, str) and
98                     not isinstance(self.criteria, str)):
99                 if self.result >= self.criteria:
100                     return TestCase.EX_OK
101             else:
102                 # Backward compatibility
103                 # It must be removed as soon as TestCase subclasses
104                 # stop setting result = 'PASS' or 'FAIL'.
105                 # In this case criteria is unread.
106                 self.__logger.warning(
107                     "Please update result which must be an int!")
108                 if self.result == 'PASS':
109                     return TestCase.EX_OK
110         except AssertionError:
111             self.__logger.error("Please run test before checking the results")
112         return TestCase.EX_TESTCASE_FAILED
113
114     def run(self, **kwargs):
115         """Run the test case.
116
117         It allows running TestCase and getting its execution
118         status.
119
120         The subclasses must override the default implementation which
121         is false on purpose.
122
123         The new implementation must set the following attributes to
124         push the results to DB:
125
126             * result,
127             * start_time,
128             * stop_time.
129
130         Args:
131             kwargs: Arbitrary keyword arguments.
132
133         Returns:
134             TestCase.EX_RUN_ERROR.
135         """
136         # pylint: disable=unused-argument
137         self.__logger.error("Run must be implemented")
138         return TestCase.EX_RUN_ERROR
139
140     def push_to_db(self):
141         """Push the results of the test case to the DB.
142
143         It allows publishing the results and to check the status.
144
145         It could be overriden if the common implementation is not
146         suitable. The following attributes must be set before pushing
147         the results to DB:
148
149             * project_name,
150             * case_name,
151             * result,
152             * start_time,
153             * stop_time.
154
155         Returns:
156             TestCase.EX_OK if results were pushed to DB.
157             TestCase.EX_PUSH_TO_DB_ERROR otherwise.
158         """
159         try:
160             assert self.project_name
161             assert self.case_name
162             assert self.start_time
163             assert self.stop_time
164             pub_result = 'PASS' if self.is_successful(
165                 ) == TestCase.EX_OK else 'FAIL'
166             if ft_utils.push_results_to_db(
167                     self.project_name, self.case_name, self.start_time,
168                     self.stop_time, pub_result, self.details):
169                 self.__logger.info(
170                     "The results were successfully pushed to DB")
171                 return TestCase.EX_OK
172             else:
173                 self.__logger.error("The results cannot be pushed to DB")
174                 return TestCase.EX_PUSH_TO_DB_ERROR
175         except Exception:  # pylint: disable=broad-except
176             self.__logger.exception("The results cannot be pushed to DB")
177             return TestCase.EX_PUSH_TO_DB_ERROR