Implement TestCase __str__()
[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 = ""
43         self.start_time = ""
44         self.stop_time = ""
45
46     def __str__(self):
47         try:
48             assert self.project_name
49             assert self.case_name
50             result = 'PASS' if(self.is_successful(
51                 ) == TestCase.EX_OK) else 'FAIL'
52             return ('| {0:<23} | {1:<13} | {2:<10} | {3:<13} |'
53                     '\n{4:-<26}{4:-<16}{4:-<13}{4:-<16}{4}'.format(
54                         self.case_name, self.project_name,
55                         self.get_duration(), result, '+'))
56         except AssertionError:
57             self.__logger.error("We cannot print invalid objects")
58             return '| {0:^68} |\n{1:-<26}{1:-<16}{1:-<13}{1:-<16}{1}'.format(
59                 'INVALID OBJECT', '+')
60
61     def get_duration(self):
62         """Return the duration of the test case.
63
64         Returns:
65             duration if start_time and stop_time are set
66             "XX:XX" otherwise.
67         """
68         try:
69             assert self.start_time
70             assert self.stop_time
71             if self.stop_time < self.start_time:
72                 return "XX:XX"
73             return "{0[0]:02.0f}:{0[1]:02.0f}".format(divmod(
74                 self.stop_time - self.start_time, 60))
75         except Exception:  # pylint: disable=broad-except
76             self.__logger.error("Please run test before getting the duration")
77             return "XX:XX"
78
79     def is_successful(self):
80         """Interpret the result of the test case.
81
82         It allows getting the result of TestCase. It completes run()
83         which only returns the execution status.
84
85         It can be overriden if checking result is not suitable.
86
87         Returns:
88             TestCase.EX_OK if result is 'PASS'.
89             TestCase.EX_TESTCASE_FAILED otherwise.
90         """
91         try:
92             assert self.criteria
93             if isinstance(self.result, int) and isinstance(self.criteria, int):
94                 if self.result >= self.criteria:
95                     return TestCase.EX_OK
96             else:
97                 # Backward compatibility
98                 # It must be removed as soon as TestCase subclasses
99                 # stop setting result = 'PASS' or 'FAIL'.
100                 # In this case criteria is unread.
101                 self.__logger.warning(
102                     "Please update result which must be an int!")
103                 if self.result == 'PASS':
104                     return TestCase.EX_OK
105         except AssertionError:
106             self.__logger.error("Please run test before checking the results")
107         return TestCase.EX_TESTCASE_FAILED
108
109     def run(self, **kwargs):
110         """Run the test case.
111
112         It allows running TestCase and getting its execution
113         status.
114
115         The subclasses must override the default implementation which
116         is false on purpose.
117
118         The new implementation must set the following attributes to
119         push the results to DB:
120
121             * result,
122             * start_time,
123             * stop_time.
124
125         Args:
126             kwargs: Arbitrary keyword arguments.
127
128         Returns:
129             TestCase.EX_RUN_ERROR.
130         """
131         # pylint: disable=unused-argument
132         self.__logger.error("Run must be implemented")
133         return TestCase.EX_RUN_ERROR
134
135     def push_to_db(self):
136         """Push the results of the test case to the DB.
137
138         It allows publishing the results and to check the status.
139
140         It could be overriden if the common implementation is not
141         suitable. The following attributes must be set before pushing
142         the results to DB:
143
144             * project_name,
145             * case_name,
146             * result,
147             * start_time,
148             * stop_time.
149
150         Returns:
151             TestCase.EX_OK if results were pushed to DB.
152             TestCase.EX_PUSH_TO_DB_ERROR otherwise.
153         """
154         try:
155             assert self.project_name
156             assert self.case_name
157             assert self.start_time
158             assert self.stop_time
159             pub_result = 'PASS' if self.is_successful(
160                 ) == TestCase.EX_OK else 'FAIL'
161             if ft_utils.push_results_to_db(
162                     self.project_name, self.case_name, self.start_time,
163                     self.stop_time, pub_result, self.details):
164                 self.__logger.info(
165                     "The results were successfully pushed to DB")
166                 return TestCase.EX_OK
167             else:
168                 self.__logger.error("The results cannot be pushed to DB")
169                 return TestCase.EX_PUSH_TO_DB_ERROR
170         except Exception:  # pylint: disable=broad-except
171             self.__logger.exception("The results cannot be pushed to DB")
172             return TestCase.EX_PUSH_TO_DB_ERROR