#!/usr/bin/python
# Copyright (c) 2017 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Script extracts interested data (name, documentation, message, status) from
robot framework output file (output.xml) and prints in specified format (wiki,
html, rst) to defined output file.
Supported formats:
- html
- rst
:TODO:
- wiki
- md
:Example:
robot_output_parser_publish.py -i output.xml" -o "tests.rst" -f "rst" -s 3 -l 2
The example reads the data from "output.xml", writes the output to "tests.rst"
in rst format. It will start on the 3rd level of xml structure and the generated
document hierarchy will start on the 2nd level.
"""
import argparse
import re
import sys
import json
import string
from robot.api import ExecutionResult, ResultVisitor
class ExecutionChecker(ResultVisitor):
"""Class to traverse through the test suite structure.
The functionality implemented in this class generates a json file. Its
structure is:
[
{
"level": "Level of the suite, type: str",
"title": "Title of the suite, type: str",
"doc": "Documentation of the suite, type: str",
"table": [
["TC name", "TC doc", "message or status"],
["TC name", "TC doc", "message or status"],
... other test cases ...
["Name", "Documentation", "Message or Status"]
]
},
... other test suites ...
]
.. note:: The header of the table with TCs is at the and of the table.
"""
def __init__(self, args):
self.formatting = args.formatting
def visit_suite(self, suite):
"""Implements traversing through the suite and its direct children.
:param suite: Suite to process.
:type suite: Suite
:returns: Nothing.
"""
if self.start_suite(suite) is not False:
if suite.tests:
sys.stdout.write(',"tests":[')
else:
sys.stdout.write('},')
suite.suites.visit(self)
suite.tests.visit(self)
if suite.tests:
if "ndrdisc" in suite.longname.lower():
hdr = '["Name","Documentation","Message"]'
else:
hdr = '["Name","Documentation","Status"]'
sys.stdout.write(hdr + ']},')
self.end_suite(suite)
def start_suite(self, suite):
"""Called when suite starts.
:param suite: Suite to process.
:type suite: Suite
:returns: Nothing.
"""
level = len(suite.longname.split("."))
sys.stdout.write('{')
sys.stdout.write('"level":"' + str(level) + '",')
sys.stdout.write('"title":"' + suite.name.replace('"', "'") + '",')
sys.stdout.write('"doc":"' + suite.doc.replace('"', "'").
replace('\n', ' ').replace('\r', '').
replace('*[', ' |br| *[') + '"')
def end_suite(self, suite):
"""Called when suite ends.
:param suite: Suite to process.
:type suite: Suite
:returns: Nothing.
"""
pass
def visit_test(self, test):
"""Implements traversing through the test.
:param test: Test to process.
:type test: Test
:returns: Nothing.
"""
if self.start_test(test) is not False:
self.end_test(test)
def start_test(self, test):
"""Called when test starts.
:param test: Test to process.
:type test: Test
:returns: Nothing.
"""
name = test.name.replace('"', "'")
doc = test.doc.replace('"', "'").replace('\n', ' ').replace('\r', '').\
replace('[', ' |br| [')
if any("NDRPDRDISC" in tag for tag in test.tags):
msg = test.message.replace('\n', ' |br| ').replace('\r', ''). \
replace('"', "'")
sys.stdout.write('["' + name + '","' + doc + '","' + msg + '"]')
else:
sys.stdout.write(
'["' + name + '","' + doc + '","' + test.status + '"]')
def end_test(self, test):
"""Called when test ends.
:param test: Test to process.
:type test: Test
:returns: Nothing.
"""
sys.stdout.write(',')
def do_html(data, args):
"""Generation of a html file from json data.
:param data: List of suites from json file.
:param args: Parsed arguments.
:type data: list of dict
:type args: ArgumentParser
:returns: Nothing.
"""
shift = int(args.level)
start = int(args.start)
output = open(args.output, 'w')
output.write('')
for item in data:
if int(item['level']) < start:
continue
level = str(int(item['level']) - start + shift)
output.write('
' + re.sub(r"(\*)(.*?)(\*)", r"\2", item['doc'],
0, flags=re.MULTILINE).
replace(' |br| ', '
') + '
' + data[-1][0] + ' | ' table += '' + data[-1][1] + ' | ' table += '' + data[-1][2] + ' |
---|---|---|
' + element.replace(' |br| ', ' ') + ' | '
table += '