Allow printing playbook output to console
[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 import robot.api
18 from robot.errors import RobotError
19 from robot.reporting import resultwriter
20 import robot.run
21 from robot.utils.robottime import timestamp_to_secs
22 from six import StringIO
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     dir_results = "/var/lib/xtesting/results"
57
58     def __init__(self, **kwargs):
59         super(RobotFramework, self).__init__(**kwargs)
60         self.xml_file = os.path.join(self.res_dir, 'output.xml')
61
62     def parse_results(self):
63         """Parse output.xml and get the details in it."""
64         result = robot.api.ExecutionResult(self.xml_file)
65         visitor = ResultVisitor()
66         result.visit(visitor)
67         try:
68             self.result = 100 * (
69                 result.suite.statistics.critical.passed /
70                 result.suite.statistics.critical.total)
71         except ZeroDivisionError:
72             self.__logger.error("No test has been run")
73         self.start_time = timestamp_to_secs(result.suite.starttime)
74         self.stop_time = timestamp_to_secs(result.suite.endtime)
75         self.details = {}
76         self.details['description'] = result.suite.name
77         self.details['tests'] = visitor.get_data()
78
79     def generate_report(self):
80         """Generate html and xunit outputs"""
81         result = robot.api.ExecutionResult(self.xml_file)
82         writer = resultwriter.ResultWriter(result)
83         return writer.write_results(
84             report='{}/report.html'.format(self.res_dir),
85             log='{}/log.html'.format(self.res_dir),
86             xunit='{}/xunit.xml'.format(self.res_dir))
87
88     def run(self, **kwargs):
89         """Run the RobotFramework suites
90
91         Here are the steps:
92            * create the output directories if required,
93            * get the results in output.xml,
94            * delete temporary files.
95
96         Args:
97             kwargs: Arbitrary keyword arguments.
98
99         Returns:
100             EX_OK if all suites ran well.
101             EX_RUN_ERROR otherwise.
102         """
103         try:
104             suites = kwargs["suites"]
105             variable = kwargs.get("variable", [])
106             variablefile = kwargs.get("variablefile", [])
107             include = kwargs.get("include", [])
108         except KeyError:
109             self.__logger.exception("Mandatory args were not passed")
110             return self.EX_RUN_ERROR
111         if not os.path.exists(self.res_dir):
112             try:
113                 os.makedirs(self.res_dir)
114             except Exception:  # pylint: disable=broad-except
115                 self.__logger.exception("Cannot create %s", self.res_dir)
116                 return self.EX_RUN_ERROR
117         stream = StringIO()
118         robot.run(*suites, variable=variable, variablefile=variablefile,
119                   include=include, output=self.xml_file, log='NONE',
120                   report='NONE', stdout=stream)
121         self.__logger.info("\n%s", stream.getvalue())
122         try:
123             self.parse_results()
124             self.__logger.info("Results were successfully parsed")
125             self.generate_report()
126             self.__logger.info("Results were successfully generated")
127         except RobotError as ex:
128             self.__logger.error("Run suites before publishing: %s", ex.message)
129             return self.EX_RUN_ERROR
130         except Exception:  # pylint: disable=broad-except
131             self.__logger.exception("Cannot parse results")
132             return self.EX_RUN_ERROR
133         return self.EX_OK