Merge "Drop six"
[functest-xtesting.git] / xtesting / core / robotframework.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2017 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 classes required to run any Robot suites."""
11
12 from __future__ import division
13
14 import logging
15 import os
16
17 from io import StringIO
18 import robot.api
19 from robot.errors import RobotError
20 from robot.reporting import resultwriter
21 import robot.run
22 from robot.utils.robottime import timestamp_to_secs
23
24 from xtesting.core import testcase
25
26 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
27
28
29 class ResultVisitor(robot.api.ResultVisitor):
30     """Visitor to get result details."""
31
32     def __init__(self):
33         self._data = []
34
35     def visit_test(self, test):
36         output = {}
37         output['name'] = test.name
38         output['parent'] = test.parent.name
39         output['status'] = test.status
40         output['starttime'] = test.starttime
41         output['endtime'] = test.endtime
42         output['critical'] = test.critical
43         output['text'] = test.message
44         output['elapsedtime'] = test.elapsedtime
45         self._data.append(output)
46
47     def get_data(self):
48         """Get the details of the result."""
49         return self._data
50
51
52 class RobotFramework(testcase.TestCase):
53     """RobotFramework runner."""
54
55     __logger = logging.getLogger(__name__)
56
57     def __init__(self, **kwargs):
58         super(RobotFramework, self).__init__(**kwargs)
59         self.xml_file = os.path.join(self.res_dir, 'output.xml')
60
61     def parse_results(self):
62         """Parse output.xml and get the details in it."""
63         result = robot.api.ExecutionResult(self.xml_file)
64         visitor = ResultVisitor()
65         result.visit(visitor)
66         try:
67             self.result = 100 * (
68                 result.suite.statistics.critical.passed /
69                 result.suite.statistics.critical.total)
70         except ZeroDivisionError:
71             self.__logger.error("No test has been run")
72         self.start_time = timestamp_to_secs(result.suite.starttime)
73         self.stop_time = timestamp_to_secs(result.suite.endtime)
74         self.details = {}
75         self.details['description'] = result.suite.name
76         self.details['tests'] = visitor.get_data()
77
78     def generate_report(self):
79         """Generate html and xunit outputs"""
80         result = robot.api.ExecutionResult(self.xml_file)
81         writer = resultwriter.ResultWriter(result)
82         return writer.write_results(
83             report='{}/report.html'.format(self.res_dir),
84             log='{}/log.html'.format(self.res_dir),
85             xunit='{}/xunit.xml'.format(self.res_dir))
86
87     def run(self, **kwargs):
88         """Run the RobotFramework suites
89
90         Here are the steps:
91            * create the output directories if required,
92            * get the results in output.xml,
93            * delete temporary files.
94
95         Args:
96             kwargs: Arbitrary keyword arguments.
97
98         Returns:
99             EX_OK if all suites ran well.
100             EX_RUN_ERROR otherwise.
101         """
102         try:
103             suites = kwargs["suites"]
104             variable = kwargs.get("variable", [])
105             variablefile = kwargs.get("variablefile", [])
106             include = kwargs.get("include", [])
107         except KeyError:
108             self.__logger.exception("Mandatory args were not passed")
109             return self.EX_RUN_ERROR
110         if not os.path.exists(self.res_dir):
111             try:
112                 os.makedirs(self.res_dir)
113             except Exception:  # pylint: disable=broad-except
114                 self.__logger.exception("Cannot create %s", self.res_dir)
115                 return self.EX_RUN_ERROR
116         stream = StringIO()
117         robot.run(*suites, variable=variable, variablefile=variablefile,
118                   include=include, output=self.xml_file, log='NONE',
119                   report='NONE', stdout=stream)
120         self.__logger.info("\n%s", stream.getvalue())
121         try:
122             self.parse_results()
123             self.__logger.info("Results were successfully parsed")
124             self.generate_report()
125             self.__logger.info("Results were successfully generated")
126         except RobotError as ex:
127             self.__logger.error("Run suites before publishing: %s", ex.message)
128             return self.EX_RUN_ERROR
129         except Exception:  # pylint: disable=broad-except
130             self.__logger.exception("Cannot parse results")
131             return self.EX_RUN_ERROR
132         return self.EX_OK