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