Change working dir
[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['text'] = test.message
43         output['elapsedtime'] = test.elapsedtime
44         self._data.append(output)
45
46     def get_data(self):
47         """Get the details of the result."""
48         return self._data
49
50
51 class RobotFramework(testcase.TestCase):
52     """RobotFramework runner."""
53
54     __logger = logging.getLogger(__name__)
55
56     def __init__(self, **kwargs):
57         super().__init__(**kwargs)
58         self.xml_file = os.path.join(self.res_dir, 'output.xml')
59
60     def parse_results(self):
61         """Parse output.xml and get the details in it."""
62         result = robot.api.ExecutionResult(self.xml_file)
63         visitor = ResultVisitor()
64         result.visit(visitor)
65         try:
66             self.result = 100 * (
67                 result.suite.statistics.passed /
68                 result.suite.statistics.total)
69         except ZeroDivisionError:
70             self.__logger.error("No test has been run")
71         self.start_time = timestamp_to_secs(result.suite.starttime)
72         self.stop_time = timestamp_to_secs(result.suite.endtime)
73         self.details = {}
74         self.details['description'] = result.suite.name
75         self.details['tests'] = visitor.get_data()
76
77     def generate_report(self):
78         """Generate html and xunit outputs"""
79         result = robot.api.ExecutionResult(self.xml_file)
80         writer = resultwriter.ResultWriter(result)
81         return writer.write_results(
82             report=f'{self.res_dir}/report.html',
83             log=f'{self.res_dir}/log.html',
84             xunit=f'{self.res_dir}/xunit.xml')
85
86     def run(self, **kwargs):
87         """Run the RobotFramework suites
88
89         Here are the steps:
90            * create the output directories if required,
91            * get the results in output.xml,
92            * delete temporary files.
93
94         Args:
95             kwargs: Arbitrary keyword arguments.
96
97         Returns:
98             EX_OK if all suites ran well.
99             EX_RUN_ERROR otherwise.
100         """
101         try:
102             suites = kwargs["suites"]
103             variable = kwargs.get("variable", [])
104             variablefile = kwargs.get("variablefile", [])
105             include = kwargs.get("include", [])
106         except KeyError:
107             self.__logger.exception("Mandatory args were not passed")
108             return self.EX_RUN_ERROR
109         if not os.path.exists(self.res_dir):
110             try:
111                 os.makedirs(self.res_dir)
112             except Exception:  # pylint: disable=broad-except
113                 self.__logger.exception("Cannot create %s", self.res_dir)
114                 return self.EX_RUN_ERROR
115         stream = StringIO()
116         robot.run(*suites, variable=variable, variablefile=variablefile,
117                   include=include, output=self.xml_file, log='NONE',
118                   report='NONE', stdout=stream)
119         self.__logger.info("\n%s", stream.getvalue())
120         try:
121             self.parse_results()
122             self.__logger.info("Results were successfully parsed")
123             self.generate_report()
124             self.__logger.info("Results were successfully generated")
125         except RobotError as ex:
126             self.__logger.error("Run suites before publishing: %s", ex.message)
127             return self.EX_RUN_ERROR
128         except Exception:  # pylint: disable=broad-except
129             self.__logger.exception("Cannot parse results")
130             return self.EX_RUN_ERROR
131         return self.EX_OK