diff options
author | Tibor Frank <tifrank@cisco.com> | 2018-05-14 14:35:46 +0200 |
---|---|---|
committer | Tibor Frank <tifrank@cisco.com> | 2018-05-14 14:36:43 +0200 |
commit | 9964155b56020914494f7341c2d1162f2ac9d720 (patch) | |
tree | bf60e93fb4aca7cbbc81e919cf2d91e9abf2ff29 /resources/tools/report_gen/run_robot_teardown_data.py | |
parent | fd6fedc983a7d1796eb2531782be8a37b6d0921d (diff) |
CSIT-1078: Optimize input data files download and processing
Change-Id: I973238d98d549555c0d43c91f9fd96a9209065ac
Signed-off-by: Tibor Frank <tifrank@cisco.com>
Diffstat (limited to 'resources/tools/report_gen/run_robot_teardown_data.py')
-rwxr-xr-x | resources/tools/report_gen/run_robot_teardown_data.py | 635 |
1 files changed, 0 insertions, 635 deletions
diff --git a/resources/tools/report_gen/run_robot_teardown_data.py b/resources/tools/report_gen/run_robot_teardown_data.py deleted file mode 100755 index df2aae5769..0000000000 --- a/resources/tools/report_gen/run_robot_teardown_data.py +++ /dev/null @@ -1,635 +0,0 @@ -#!/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, VAT command history or table from Show -Runtime command) from robot framework output file (output.xml) and prints in -specified format (wiki, html, rst) to defined output file. - -Supported formats: - - html - - rst - - wiki - -:TODO: - - md - -:Example: - -run_robot_teardown_data.py -i "output.xml" -o "tests.rst" -d "VAT_H" -f "rst" --s 3 -l 2 - -The example reads the VAT command history 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. - -:Example: - -run_robot_teardown_data.py -i "output.xml" -o "tests.rst" -f "rst" -d "SH_RUN" - -r "(.*)(lisp)(.*)" - -The example reads the data from "output.xml", writes the output to "tests.rst" -in rst format. It will start on the 1st level of xml structure and the generated -document hierarchy will start on the 1st level (default values). -Only the test suites which match the given regular expression are processed. -""" - -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", "VAT history or show runtime"], - ["TC name", "VAT history or show runtime"], - ... other test cases ... - ["Name","VAT command history or VPP operational data"] - ] - }, - ... other test suites ... - ] - - .. note:: The header of the table with TCs is at the end of the table. - """ - - def __init__(self, args): - self.formatting = args.formatting - self.data = args.data - self.tagin = " |prein| " - self.tagout = " |preout| " - if self.data == "VAT_H": - self.lookup_kw = "Show Vat History On All Duts" - self.column_name = "VPP API Test (VAT) Commands History - " \ - "Commands Used Per Test Case" - elif self.data == "SH_RUN": - self.lookup_kw = "Vpp Show Runtime" - self.column_name = "VPP Operational Data - Outputs of " \ - "'show runtime' at NDR packet rate" - else: - raise ValueError("{0} look-up not implemented.".format(self.data)) - self.lookup_kw_nr = 0 - self.lookup_msg_nr = 0 - - 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: - hdr = '["Name","' + self.column_name + '"]' - 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: - test.keywords.visit(self) - 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('"', "'") - sys.stdout.write('["' + name + '","' + self.tagin) - - def end_test(self, test): - """Called when test ends. - - :param test: Test to process. - :type test: Test - :returns: Nothing. - """ - sys.stdout.write(self.tagout + '"],') - - def visit_keyword(self, kw): - """Implements traversing through the keyword and its child keywords. - - :param kw: Keyword to process. - :type kw: Keyword - :returns: Nothing. - """ - if self.start_keyword(kw) is not False: - self.end_keyword(kw) - - def start_keyword(self, kw): - """Called when keyword starts. Default implementation does nothing. - - :param kw: Keyword to process. - :type kw: Keyword - :returns: Nothing. - """ - try: - if kw.type == "teardown": - self.lookup_kw_nr = 0 - self.visit_teardown_kw(kw) - except AttributeError: - pass - - def end_keyword(self, kw): - """Called when keyword ends. Default implementation does nothing. - - :param kw: Keyword to process. - :type kw: Keyword - :returns: Nothing. - """ - pass - - def visit_teardown_kw(self, kw): - """Implements traversing through the teardown keyword and its child - keywords. - - :param kw: Keyword to process. - :type kw: Keyword - :returns: Nothing. - """ - for keyword in kw.keywords: - if self.start_teardown_kw(keyword) is not False: - self.visit_teardown_kw(keyword) - self.end_teardown_kw(keyword) - - def start_teardown_kw(self, kw): - """Called when teardown keyword starts. Default implementation does - nothing. - - :param kw: Keyword to process. - :type kw: Keyword - :returns: Nothing. - """ - if kw.name.count(self.lookup_kw): - self.lookup_kw_nr += 1 - self.lookup_msg_nr = 0 - kw.messages.visit(self) - - def end_teardown_kw(self, kw): - """Called when keyword ends. Default implementation does nothing. - - :param kw: Keyword to process. - :type kw: Keyword - :returns: Nothing. - """ - pass - - def visit_message(self, msg): - """Implements visiting the message. - - :param msg: Message to process. - :type msg: Message - :returns: Nothing. - """ - if self.start_message(msg) is not False: - self.end_message(msg) - - def start_message(self, msg): - """Called when message starts. Default implementation does nothing. - - :param msg: Message to process. - :type msg: Message - :returns: Nothing. - """ - if self.data == "VAT_H": - self.vat_history(msg) - elif self.data == "SH_RUN": - self.show_run(msg) - - def end_message(self, msg): - """Called when message ends. Default implementation does nothing. - - :param msg: Message to process. - :type msg: Message - :returns: Nothing. - """ - pass - - def vat_history(self, msg): - """Called when extraction of VAT command history is required. - - :param msg: Message to process. - :type msg: Message - :returns: Nothing. - """ - if msg.message.count("VAT command history:"): - self.lookup_msg_nr += 1 - text = re.sub("[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3} " - "VAT command history:", "", msg.message, count=1).\ - replace('\n', ' |br| ').replace('\r', '').replace('"', "'") - if self.lookup_msg_nr > 1: - sys.stdout.write(" |br| ") - sys.stdout.write("*DUT" + str(self.lookup_msg_nr) + ":*" + text) - - def show_run(self, msg): - """Called when extraction of VPP operational data (output of CLI command - Show Runtime) is required. - - :param msg: Message to process. - :type msg: Message - :returns: Nothing. - """ - if msg.message.count("vat# Thread "): - self.lookup_msg_nr += 1 - text = msg.message.replace("vat# ", "").\ - replace("return STDOUT ", "").replace('\n', ' |br| ').\ - replace('\r', '').replace('"', "'") - if self.lookup_msg_nr == 1: - sys.stdout.write("*DUT" + str(self.lookup_kw_nr) + - ":* |br| " + text) - - -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('<html>') - for item in data: - if int(item['level']) < start: - continue - level = str(int(item['level']) - start + shift) - output.write('<h' + level + '>' + item['title'].lower() + - '</h' + level + '>') - output.write('<p>' + re.sub(r"(\*)(.*?)(\*)", r"<b>\2</b>", item['doc'], - 0, flags=re.MULTILINE). - replace(' |br| ', '<br>') + '</p>') - try: - output.write(gen_html_table(item['tests'])) - except KeyError: - continue - output.write('</html>') - output.close() - - -def gen_html_table(data): - """Generates a table with TCs' names and VAT command histories / VPP - operational data in html format. There is no css used. - - :param data: Json data representing a table with TCs. - :type data: str - :returns: Table with TCs' names and VAT command histories / VPP operational - data in html format. - :rtype: str - """ - - table = '<table width=100% border=1><tr>' - table += '<th width=30%>' + data[-1][0] + '</th>' - table += '<th width=70%>' + data[-1][1] + '</th></tr>' - - for item in data[0:-1]: - table += '<tr>' - for element in item: - table += '<td>' + re.sub(r"(\*)(.*?)(\*)", r"<b>\2</b>", element, - 0, flags=re.MULTILINE).\ - replace(' |br| ', '<br>').replace(' |prein| ', '<pre>').\ - replace(' |preout| ', '</pre>') + '</td>' - table += '</tr></table>' - - return table - - -def do_rst(data, args): - """Generation of a rst 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. - """ - - hdrs = ['=', '-', '`', "'", '.', '~', '*', '+', '^'] - shift = int(args.level) - start = int(args.start) - - output = open(args.output, 'w') - output.write('\n.. |br| raw:: html\n\n <br />\n\n') - output.write('\n.. |prein| raw:: html\n\n <pre>\n\n') - output.write('\n.. |preout| raw:: html\n\n </pre>\n\n') - - if args.title: - output.write(args.title + '\n' + - hdrs[shift - 1] * - len(args.title) + '\n\n') - - for item in data: - if int(item['level']) < start: - continue - if 'ndrchk' in item['title'].lower(): - continue - output.write(item['title'].lower() + '\n' + - hdrs[int(item['level']) - start + shift] * - len(item['title']) + '\n\n') - output.write(item['doc'].replace('*', '**').replace('|br|', '\n\n -') + - '\n\n') - try: - test_set = list() - for test in item['tests']: - test_data = list() - test_data.append(test[0]) - test_data.append(test[1].replace('*', '**')) - test_set.append(test_data) - output.write(gen_rst_table(test_set) + '\n\n') - except KeyError: - continue - output.close() - - -def gen_rst_table(data): - """Generates a table with TCs' names and VAT command histories / VPP - operational data in rst format. - - :param data: Json data representing a table with TCs. - :type data: str - :returns: Table with TCs' names and VAT command histories / VPP operational - data in rst format. - :rtype: str - """ - - table = [] - # max size of each column - lengths = map(max, zip(*[[len(str(elt)) for elt in item] for item in data])) - - start_of_line = '| ' - vert_separator = ' | ' - end_of_line = ' |' - line_marker = '-' - - meta_template = vert_separator.join(['{{{{{0}:{{{0}}}}}}}'.format(i) - for i in range(len(lengths))]) - template = '{0}{1}{2}'.format(start_of_line, meta_template.format(*lengths), - end_of_line) - # determine top/bottom borders - to_separator = string.maketrans('| ', '+-') - start_of_line = start_of_line.translate(to_separator) - vert_separator = vert_separator.translate(to_separator) - end_of_line = end_of_line.translate(to_separator) - separator = '{0}{1}{2}'.format(start_of_line, vert_separator. - join([x * line_marker for x in lengths]), - end_of_line) - # determine header separator - th_separator_tr = string.maketrans('-', '=') - start_of_line = start_of_line.translate(th_separator_tr) - line_marker = line_marker.translate(th_separator_tr) - vertical_separator = vert_separator.translate(th_separator_tr) - end_of_line = end_of_line.translate(th_separator_tr) - th_separator = '{0}{1}{2}'.format(start_of_line, vertical_separator. - join([x * line_marker for x in lengths]), - end_of_line) - # prepare table - table.append(separator) - # set table header - titles = data[-1] - table.append(template.format(*titles)) - table.append(th_separator) - # generate table rows - for item in data[0:-2]: - table.append(template.format(item[0], item[1])) - table.append(separator) - table.append(template.format(data[-2][0], data[-2][1])) - table.append(separator) - return '\n'.join(table) - - -def do_md(data, args): - """Generation of a rst 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. - """ - raise NotImplementedError("Export to 'md' format is not implemented.") - - -def do_wiki(data, args): - """Generation of a wiki page 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') - - for item in data: - if int(item['level']) < start: - continue - if 'ndrchk' in item['title'].lower(): - continue - mark = "=" * (int(item['level']) - start + shift) + ' ' - output.write(mark + item['title'].lower() + mark + '\n') - try: - output.write(gen_wiki_table(item['tests'], mark) + - '\n\n') - except KeyError: - continue - output.close() - - -def gen_wiki_table(data, mark): - """Generates a table with TCs' names and VAT command histories / VPP - operational data in wiki format. - - :param data: Json data representing a table with TCs. - :type data: str - :returns: Table with TCs' names and VAT command histories / VPP operational - data in wiki format. - :rtype: str - """ - - table = '{| class="wikitable"\n' - header = "" - mark = mark[0:-2] + "= " - for item in data[-1]: - header += '!{}\n'.format(item) - table += header - for item in data[0:-1]: - msg = item[1].replace('*', mark).replace(' |br| ', '\n\n').\ - replace(' |prein| ', '<pre>').replace(' |preout| ', '</pre>') - table += '|-\n|{}\n|{}\n'.format(item[0], msg) - table += '|}\n' - - return table - - -def process_robot_file(args): - """Process data from robot output.xml file and generate defined file type. - - :param args: Parsed arguments. - :type args: ArgumentParser - :return: Nothing. - """ - - old_sys_stdout = sys.stdout - sys.stdout = open(args.output + '.json', 'w') - - result = ExecutionResult(args.input) - checker = ExecutionChecker(args) - - sys.stdout.write('[') - result.visit(checker) - sys.stdout.write('{}]') - sys.stdout.close() - sys.stdout = old_sys_stdout - - with open(args.output + '.json', 'r') as json_file: - data = json.load(json_file) - data.pop(-1) - - if args.regex: - results = list() - regex = re.compile(args.regex) - for item in data: - if re.search(regex, item['title'].lower()): - results.append(item) - else: - results = data - - if args.formatting == 'rst': - do_rst(results, args) - elif args.formatting == 'wiki': - do_wiki(results, args) - elif args.formatting == 'html': - do_html(results, args) - elif args.formatting == 'md': - do_md(results, args) - - -def parse_args(): - """Parse arguments from cmd line. - - :return: Parsed arguments. - :rtype ArgumentParser - """ - - parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse. - RawDescriptionHelpFormatter) - parser.add_argument("-i", "--input", - required=True, - type=argparse.FileType('r'), - help="Robot XML log file") - parser.add_argument("-o", "--output", - type=str, - required=True, - help="Output file") - parser.add_argument("-d", "--data", - type=str, - required=True, - help="Required data: VAT_H (VAT history), SH_RUN " - "(show runtime output)") - parser.add_argument("-f", "--formatting", - required=True, - choices=['html', 'wiki', 'rst', 'md'], - help="Output file format") - parser.add_argument("-s", "--start", - type=int, - default=1, - help="The first level to be taken from xml file") - parser.add_argument("-l", "--level", - type=int, - default=1, - help="The level of the first chapter in generated file") - parser.add_argument("-r", "--regex", - type=str, - default=None, - help="Regular expression used to select test suites. " - "If None, all test suites are selected.") - parser.add_argument("-t", "--title", - type=str, - default=None, - help="Title of the output.") - - return parser.parse_args() - - -if __name__ == "__main__": - sys.exit(process_robot_file(parse_args())) |