3 # Copyright (c) 2017 Orange and others.
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
10 """Define classes required to run any Robot suites."""
12 from __future__ import division
17 from io import StringIO
19 from robot.errors import RobotError
20 from robot.reporting import resultwriter
22 from robot.utils.robottime import timestamp_to_secs
24 from xtesting.core import testcase
26 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
29 class ResultVisitor(robot.api.ResultVisitor):
30 """Visitor to get result details."""
35 def visit_test(self, test):
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)
47 """Get the details of the result."""
51 class RobotFramework(testcase.TestCase):
52 """RobotFramework runner."""
54 __logger = logging.getLogger(__name__)
56 def __init__(self, **kwargs):
57 super().__init__(**kwargs)
58 self.xml_file = os.path.join(self.res_dir, 'output.xml')
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()
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)
74 self.details['description'] = result.suite.name
75 self.details['tests'] = visitor.get_data()
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')
86 def run(self, **kwargs):
87 """Run the RobotFramework suites
90 * create the output directories if required,
91 * get the results in output.xml,
92 * delete temporary files.
95 kwargs: Arbitrary keyword arguments.
98 EX_OK if all suites ran well.
99 EX_RUN_ERROR otherwise.
102 suites = kwargs["suites"]
103 variable = kwargs.get("variable", [])
104 variablefile = kwargs.get("variablefile", [])
105 include = kwargs.get("include", [])
107 self.__logger.exception("Mandatory args were not passed")
108 return self.EX_RUN_ERROR
109 if not os.path.exists(self.res_dir):
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
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())
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