Add no:cacheprovider by default
[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         self.deny_skipping = kwargs.get("deny_skipping", False)
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             if self.deny_skipping:
68                 self.result = 100 * (
69                     result.suite.statistics.passed /
70                     result.suite.statistics.total)
71             else:
72                 self.result = 100 * ((
73                     result.suite.statistics.passed +
74                     result.suite.statistics.skipped) /
75                     result.suite.statistics.total)
76         except ZeroDivisionError:
77             self.__logger.error("No test has been run")
78         self.start_time = timestamp_to_secs(result.suite.starttime)
79         self.stop_time = timestamp_to_secs(result.suite.endtime)
80         self.details = {}
81         self.details['description'] = result.suite.name
82         self.details['tests'] = visitor.get_data()
83
84     def generate_report(self):
85         """Generate html and xunit outputs"""
86         result = robot.api.ExecutionResult(self.xml_file)
87         writer = resultwriter.ResultWriter(result)
88         return writer.write_results(
89             report=f'{self.res_dir}/report.html',
90             log=f'{self.res_dir}/log.html',
91             xunit=f'{self.res_dir}/xunit.xml')
92
93     def run(self, **kwargs):
94         """Run the RobotFramework suites
95
96         Here are the steps:
97            * create the output directories if required,
98            * get the results in output.xml,
99            * delete temporary files.
100
101         Args:
102             kwargs: Arbitrary keyword arguments.
103
104         Returns:
105             EX_OK if all suites ran well.
106             EX_RUN_ERROR otherwise.
107         """
108         try:
109             suites = kwargs.pop("suites")
110         except KeyError:
111             self.__logger.exception("Mandatory args were not passed")
112             return self.EX_RUN_ERROR
113         if not os.path.exists(self.res_dir):
114             try:
115                 os.makedirs(self.res_dir)
116             except Exception:  # pylint: disable=broad-except
117                 self.__logger.exception("Cannot create %s", self.res_dir)
118                 return self.EX_RUN_ERROR
119         stream = StringIO()
120         kwargs["output"] = self.xml_file
121         kwargs["log"] = "NONE"
122         kwargs["report"] = "NONE"
123         kwargs["stdout"] = stream
124         robot.run(*suites, **kwargs)
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