 Chromium Code Reviews
 Chromium Code Reviews Issue 2092022:
  This script will create test documentation for AutoTest suites.  (Closed) 
  Base URL: ssh://git@chromiumos-git//autotest.git
    
  
    Issue 2092022:
  This script will create test documentation for AutoTest suites.  (Closed) 
  Base URL: ssh://git@chromiumos-git//autotest.git| Index: utils/docgen/CreateDocs.py | 
| diff --git a/utils/docgen/CreateDocs.py b/utils/docgen/CreateDocs.py | 
| new file mode 100755 | 
| index 0000000000000000000000000000000000000000..e0a26e26a135404acd6345067ff39a71280cec47 | 
| --- /dev/null | 
| +++ b/utils/docgen/CreateDocs.py | 
| @@ -0,0 +1,708 @@ | 
| +#!/usr/bin/python | 
| +# Copyright (c) 2010 The Chromium Authors. All rights reserved. | 
| +# Use of this source code is governed by a BSD-style license that can be | 
| +# found in the LICENSE file. | 
| + | 
| +""" Parse suite control files and make HTML documentation from included tests. | 
| + | 
| +This program will create a list of test cases found in suite files by parsing | 
| +through each suite control file and making a list of all of the jobs called from | 
| +it. Once it has a list of tests, it will parse the AutoTest control file for | 
| +each test and grab the doc strings. These doc strings, along with any | 
| +constraints in the suite control file, will be added to the original test | 
| +script. These new scripts will be placed in a stand alone directory. Doxygen | 
| +will then use these files for the sole purpose of producing HTML documentation | 
| +for all of the tests. Once HTML docs are created some post processing will be | 
| +done against the docs to change a few strings. | 
| + | 
| +If this script is executed without a --src argument, it will assume it is being | 
| +executed from <ChromeOS>/src/third_party/autotest/files/utils/docgen/ directory. | 
| + | 
| +Classes: | 
| + | 
| + DocCreator | 
| + This class is responsible for all processing. It requires the following: | 
| + - Absolute path of suite control files. | 
| + - Absolute path of where to place temporary files it constructs from the | 
| + control files and test scripts. | 
| + This class makes the following assumptions: | 
| + - Each master suite has a README.txt file with general instructions on | 
| + test preparation and usage. | 
| + - The control file for each test has doc strings with labels of: | 
| + - PURPOSE: one line description of why this test exists. | 
| + - CRITERIA: Pass/Failure conditions. | 
| + - DOC: additional test details. | 
| + ReadNode | 
| + This class parses a node from a control file into a key/value pair. In this | 
| + context, a node represents a syntactic construct of an abstract syntax tree. | 
| + The root of the tree is the module object (in this case a control file). If | 
| + suite=True, it will assume the node is from a suite control file. | 
| + | 
| +Doxygen should already be configured with a configuration file called: | 
| +doxygen.conf. This file should live in the same directory with this program. | 
| +If you haven't installed doxygen, you'll need to install this program before | 
| +this script is executed. This program will automatically update the doxygen.conf | 
| +file to match self.src_tests and self.html. | 
| + | 
| +TODO: (kdlucas@google.com) Update ReadNode class to use the replacement module | 
| +for the compiler module, as that has been deprecated. | 
| +""" | 
| + | 
| +__author__ = 'kdlucas@google.com (Kelly Lucas)' | 
| +__version__ = '0.8.0' | 
| + | 
| +import compiler | 
| +import fileinput | 
| +import glob | 
| +import logging | 
| +import optparse | 
| +import os | 
| +import re | 
| +import shutil | 
| +import subprocess | 
| +import sys | 
| + | 
| + | 
| +class DocCreator(object): | 
| + """Process suite control files to combine docstrings and create HTML docs. | 
| + | 
| + The DocCreator class is designed to parse AutoTest suite control files to | 
| + find all of the tests referenced, and build HTML documentation based on the | 
| + docstrings in those files. It will cross reference the test control file | 
| + and any parameters passed through the suite file, with the original test | 
| + case. DocCreator relies on doxygen to actually generate the HTML documents. | 
| + | 
| + The workflow is as follows: | 
| + - Parse the suite file(s) and generate a test list. | 
| + - Locate the test source, and grab the docstrings from the associated | 
| + AutoTest control file. | 
| + - Combine the docstring from the control file with any parameters passed | 
| + in from the suite control file, with the original test case. | 
| + - Write a new test file with the combined docstrings to src_tests. | 
| + - Create HTML documentation by running doxygen against the tests stored | 
| + in self.src_tests. | 
| + | 
| + Implements the following methods: | 
| + - SetLogger() - Gets a logger and sets the formatting. | 
| + - GetTests() - Parse suite control files, create a dictionary of tests. | 
| + - ParseControlFiles() - Runs through all tests and parses control files | 
| + - _CleanDir() - Remove any files in a direcory and create an empty one. | 
| + - _GetDoctString() - Parses docstrings and joins it with constraints. | 
| + - _CreateTest() - Add docstrings and constraints to existing test script | 
| + to form a new test script. | 
| + - CreateMainPage() - Create a mainpage.txt file based on contents of the | 
| + suite README file. | 
| + - _ConfigDoxygen - Updates doxygen.conf to match some attributes this | 
| + script was run with. | 
| + - RunDoxygen() - Executes the doxygen program. | 
| + - CleanDocs() - Changes some text in the HTML files to conform to our | 
| + naming conventions and style. | 
| + | 
| + Depends upon class ReadNode. | 
| + """ | 
| + def __init__(self): | 
| + """Parse command line arguments and set some initial variables.""" | 
| + | 
| + desc="""%prog will scan AutoTest suite control files to build a list of | 
| + test cases called in the suite, and build HTML documentation based on | 
| + the docstrings it finds in the tests, control files, and suite control | 
| + files. | 
| + """ | 
| + | 
| + self.runpath = os.path.abspath('.') | 
| + src = os.path.join(self.runpath, '../../../../../') | 
| + srcdir = os.path.abspath(src) | 
| + | 
| + parser = optparse.OptionParser(description=desc, | 
| + prog='CreateDocs', | 
| + version=__version__, | 
| + usage='%prog') | 
| + parser.add_option('--debug', | 
| + help='Debug level [default: %default]', | 
| + default='debug', | 
| + dest='debug') | 
| + parser.add_option('--docversion', | 
| + help='Specify a version for the documentation' | 
| + '[default: %default]', | 
| + default=None, | 
| + dest='docversion') | 
| + parser.add_option('--doxy', | 
| + help='doxygen configuration file [default: %default]', | 
| + default='doxygen.conf', | 
| + dest='doxyconf') | 
| + parser.add_option('--html', | 
| + help='path to store html docs [default: %default]', | 
| + default='html', | 
| + dest='html') | 
| + parser.add_option('--latex', | 
| + help='path to store latex docs [default: %default]', | 
| + default='latex', | 
| + dest='latex') | 
| + parser.add_option('--layout', | 
| + help='doxygen layout file [default: %default]', | 
| + default='doxygenLayout.xml', | 
| + dest='layout') | 
| + parser.add_option('--log', | 
| + help='Logfile for program output [default: %default]', | 
| + default='docCreator.log', | 
| + dest='logfile') | 
| + parser.add_option('--readme', | 
| + help='filename of suite documentation' | 
| + '[default: %default]', | 
| + default='README.txt', | 
| + dest='readme') | 
| + parser.add_option('--src', | 
| + help='path to chromiumos source root' | 
| + ' [default: %default]', | 
| + default=srcdir, | 
| + dest='src_chromeos') | 
| + #TODO(kdlucas): add an all option that will parse all suites. | 
| + parser.add_option('--suite', | 
| + help='Directory name of suite [default: %default]', | 
| + type='choice', | 
| + default='suite_HWQual', | 
| + choices = [ | 
| + 'suite_Factory', | 
| + 'suite_HWConfig', | 
| + 'suite_HWQual', | 
| + ], | 
| + dest='suite') | 
| + parser.add_option('--tests', | 
| + help='Absolute path of temporary test files' | 
| + ' [default: %default]', | 
| + default='testsource', | 
| + dest='src_tests') | 
| + | 
| + self.options, self.args = parser.parse_args() | 
| + | 
| + | 
| + # Make parameters a little shorter by making the following assignments. | 
| + self.debug = self.options.debug | 
| + self.docversion = self.options.docversion | 
| + self.doxyconf = self.options.doxyconf | 
| + self.html = self.options.html | 
| + self.latex = self.options.latex | 
| + self.layout = self.options.layout | 
| + self.logfile = self.options.logfile | 
| + self.readme = self.options.readme | 
| + self.src_tests = self.options.src_tests | 
| + self.src = self.options.src_chromeos | 
| + self.suite = self.options.suite | 
| + | 
| + autotest_path = 'third_party/autotest/files' | 
| + sitetests = 'client/site_tests' | 
| + tests = 'client/tests' | 
| + self.testcase = {} | 
| + | 
| + self.site_dir = os.path.join(self.src, autotest_path, sitetests) | 
| + self.test_dir = os.path.join(self.src, autotest_path, tests) | 
| + self.suite_dir = os.path.join(self.site_dir, self.suite) | 
| + | 
| + self.logger = self.SetLogger('docCreator') | 
| + self.logger.debug('Executing with debug level: %s', self.debug) | 
| + self.logger.debug('Writing to logfile: %s', self.logfile) | 
| + self.logger.debug('New test directory: %s', self.src_tests) | 
| + self.logger.debug('Chrome OS src directory: %s', self.src) | 
| + self.logger.debug('Test suite: %s', self.suite) | 
| + | 
| + self.suitename = {'suite_Factory': 'Factory Testing', | 
| + 'suite_HWConfig': 'Hardware Configuration', | 
| + 'suite_HWQual': 'Hardware Qualification', | 
| + } | 
| + | 
| + def SetLogger(self, namespace): | 
| + """Create a logger with some good formatting options. | 
| + | 
| + Args: | 
| + namespace: string, name associated with this logger. | 
| + Returns: | 
| + Logger object. | 
| + This method assumes self.logfile and self.debug are already set. | 
| + This logger will write to stdout as well as a log file. | 
| + """ | 
| + | 
| + loglevel = {'debug': logging.DEBUG, | 
| + 'info': logging.INFO, | 
| + 'warning': logging.WARNING, | 
| + 'error': logging.ERROR, | 
| + 'critical': logging.CRITICAL, | 
| + } | 
| + | 
| + logger = logging.getLogger(namespace) | 
| + c = logging.StreamHandler() | 
| + h = logging.FileHandler(os.path.join(self.runpath, self.logfile)) | 
| + hf = logging.Formatter( | 
| + '%(asctime)s %(process)d %(levelname)s: %(message)s') | 
| + cf = logging.Formatter('%(levelname)s: %(message)s') | 
| + logger.addHandler(h) | 
| + logger.addHandler(c) | 
| + h.setFormatter(hf) | 
| + c.setFormatter(cf) | 
| + | 
| + if self.debug in loglevel: | 
| + logger.setLevel(loglevel.get(self.debug, logging.INFO)) | 
| 
petkov
2010/05/26 19:35:17
Remove if / else -- you don't need them any more.
 | 
| + else: | 
| + logger.setLevel(logging.INFO) | 
| + | 
| + return logger | 
| + | 
| + | 
| + def GetTests(self): | 
| + """Create dictionary of tests based on suite control file contents.""" | 
| + | 
| + suite_search = os.path.join(self.suite_dir, 'control.*') | 
| + for suitefile in glob.glob(suite_search): | 
| + self.logger.debug('Scanning %s for tests', suitefile) | 
| + try: | 
| + suite = compiler.parseFile(suitefile) | 
| + except SyntaxError, e: | 
| + self.logger.error('Error parsing: %s\n%s', (suitefile, e)) | 
| + raise SystemExit | 
| + | 
| + # Walk through each node found in the control file, which in our | 
| + # case will be a call to a test. compiler.walk() will walk through | 
| + # each component node, and call the appropriate function in class | 
| + # ReadNode. The returned key should be a string, and the name of a | 
| + # test. visitor.value should be any extra arguments found in the | 
| + # suite file that are used with that test case. | 
| + for n in suite.node.nodes: | 
| + visitor = ReadNode(suite=True) | 
| + compiler.walk(n, visitor) | 
| + if len(visitor.key) > 1: | 
| + self.logger.debug('Found test %s', visitor.key) | 
| + filtered_input = '' | 
| + # Lines in value should start with ' -' for bullet item. | 
| + if visitor.value: | 
| + lines = visitor.value.split('\n') | 
| + for line in lines: | 
| + if line.startswith(' -'): | 
| + filtered_input += line + '\n' | 
| + # A test could be called multiple times, so see if the key | 
| + # already exists, and if so append the new value. | 
| + if visitor.key in self.testcase: | 
| + s = self.testcase[visitor.key] + filtered_input | 
| + self.testcase[visitor.key] = s | 
| + else: | 
| + self.testcase[visitor.key] = filtered_input | 
| + | 
| + | 
| + def _CleanDir(self, directory): | 
| + """Ensure the directory is available and empty. | 
| + | 
| + Args: | 
| + directory: string, path of directory | 
| + """ | 
| + | 
| + if os.path.isdir(directory): | 
| + try: | 
| + shutil.rmtree(directory) | 
| + except IOError, err: | 
| + self.logger.error('Error cleaning %s\n%s', (directory, err)) | 
| + try: | 
| + os.makedirs(directory) | 
| + except IOError, err: | 
| + self.logger.error('Error creating %s\n%s', (directory, err)) | 
| + self.logger.error('Check your permissions of %s', directory) | 
| + raise SystemExit | 
| + | 
| + | 
| + def ParseControlFiles(self): | 
| + """Get docstrings from control files and add them to new test scripts. | 
| + | 
| + This method will cycle through all of the tests and attempt to find | 
| + their control file. If found, it will parse the docstring from the | 
| + control file, add this to any parameters found in the suite file, and | 
| + add this combined docstring to the original test. These new tests will | 
| + be written in the self.src_tests directory. | 
| + """ | 
| + # Clean some target directories. | 
| + for d in [self.src_tests, self.html]: | 
| + self._CleanDir(d) | 
| + | 
| + for test in self.testcase: | 
| + testdir = os.path.join(self.site_dir, test) | 
| + if not os.path.isdir(testdir): | 
| + testdir = os.path.join(self.test_dir, test) | 
| + | 
| + if os.path.isdir(testdir): | 
| + control_file = os.path.join(testdir, 'control') | 
| + test_file = os.path.join(testdir, test + '.py') | 
| + docstring = self._GetDocString(control_file, test) | 
| + self._CreateTest(test_file, docstring, test) | 
| + else: | 
| + self.logger.warning('Cannot find test: %s', test) | 
| + | 
| + def _GetDocString(self, control_file, test): | 
| + """Get the docstrings from control file and join to suite file params. | 
| + | 
| + Args: | 
| + control_file: string, absolute path to test control file. | 
| + test: string, name of test. | 
| + Returns: | 
| + string: combined docstring with needed markup language for doxygen. | 
| + """ | 
| + | 
| + # Doxygen needs the @package marker. | 
| + package_doc = '## @package ' | 
| + # To allow doxygen to use special commands, we must use # for comments. | 
| + comment = '# ' | 
| + endlist = ' .\n' | 
| + control_dict = {} | 
| + output = [] | 
| + temp = [] | 
| + tempstring = '' | 
| + docstring = '' | 
| + keys = ['\\brief\n', '<H3>Pass/Fail Criteria:</H3>\n', | 
| + '<H3>Author</H3>\n', '<H3>Test Duration</H3>\n', | 
| + '<H3>Category</H3>\n', '<H3>Test Type</H3>\n', | 
| + '<H3>Test Class</H3>\n', '<H3>Notest</H3>\n', | 
| + ] | 
| + | 
| + try: | 
| + control = compiler.parseFile(control_file) | 
| + except SyntaxError, e: | 
| + self.logger.error('Error parsing: %s\n%s', (control_file, e)) | 
| + return None | 
| + | 
| + for n in control.node.nodes: | 
| + visitor = ReadNode() | 
| + compiler.walk(n, visitor) | 
| + control_dict[visitor.key] = visitor.value | 
| + | 
| + for k in keys: | 
| + if k in control_dict: | 
| + if len(control_dict[k]) > 1: | 
| + if k != test: | 
| + temp.append(k) | 
| + temp.append(control_dict[k]) | 
| + if control_dict[k]: | 
| + temp.append(endlist) | 
| + # Add constraints and extra args after the Criteria section. | 
| + if 'Criteria:' in k: | 
| + if self.testcase[test]: | 
| + temp.append('<H3>Arguments:</H3>\n') | 
| + temp.append(self.testcase[test]) | 
| + # '.' character at the same level as the '-' tells | 
| + # doxygen this is the end of the list. | 
| + temp.append(endlist) | 
| + | 
| + output.append(package_doc + test + '\n') | 
| + tempstring = "".join(temp) | 
| + lines = tempstring.split('\n') | 
| + for line in lines: | 
| + # Doxygen requires a '#' character to add special doxygen commands. | 
| + comment_line = comment + line + '\n' | 
| + output.append(comment_line) | 
| + | 
| + docstring = "".join(output) | 
| + | 
| + return docstring | 
| + | 
| + | 
| + def _CreateTest(self, test_file, docstring, test): | 
| + """Create a new test with the combined docstrings from multiple sources. | 
| + | 
| + Args: | 
| + test_file: string, file name of new test to write. | 
| + docstring: string, the docstring to add to the existing test. | 
| + test: string, name of the test. | 
| + | 
| + This method is used to create a temporary copy of a new test, that will | 
| + be a combination of the original test plus the docstrings from the | 
| + control file, and any constraints from the suite control file. | 
| + """ | 
| + | 
| + class_def = 'class ' + test | 
| + pathname = os.path.join(self.src_tests, test + '.py') | 
| + | 
| + # Open the test and write out new test with added docstrings | 
| + try: | 
| + f = open(test_file, 'r') | 
| + except IOError, err: | 
| + self.logger.error('Error while reading %s\n%s', (test_file, err)) | 
| + return | 
| + lines = f.readlines() | 
| + f.close() | 
| + | 
| + try: | 
| + f = open(pathname, 'w') | 
| + except IOError, err: | 
| + self.logger.error('Error creating %s\n%s', (pathname, err)) | 
| + return | 
| + | 
| + for line in lines: | 
| + if class_def in line: | 
| + f.write(docstring) | 
| + f.write('\n') | 
| + f.write(line) | 
| + f.close() | 
| + | 
| + def CreateMainPage(self): | 
| + """Create a main page to provide content for index.html. | 
| + | 
| + This method assumes a file named README.txt is located in your suite | 
| + directory with general instructions on setting up and using the suite. | 
| + If your README file is in another file, ensure you pass a --readme | 
| + option with the correct filename. To produce a better looking | 
| + landing page, use the '-' character for list items. This method assumes | 
| + os commands start with '$'. | 
| + """ | 
| + | 
| + # Define some strings that Doxygen uses for specific formatting. | 
| + cstart = '/**' | 
| + cend = '**/' | 
| + mp = '@mainpage' | 
| + section_begin = '@section ' | 
| + vstart = '@verbatim ' | 
| + vend = ' @endverbatim\n' | 
| + | 
| + # Define some characters we expect to delineate sections in the README. | 
| + sec_char = '==========' | 
| + command_prompt = '$ ' | 
| + command_cont = '\\' | 
| + | 
| + command = False | 
| + comment = False | 
| + section = False | 
| + sec_ctr = 0 | 
| + | 
| + readme_file = os.path.join(self.suite_dir, self.readme) | 
| + mainpage_file = os.path.join(self.src_tests, 'mainpage.txt') | 
| + | 
| + try: | 
| + f = open(readme_file, 'r') | 
| + except IOError, err: | 
| + self.logger.error('Error opening %s\n%s', (readme_file, err)) | 
| + return | 
| + try: | 
| + fw = open(mainpage_file, 'w') | 
| + except IOError, err: | 
| + self.logger.error('Error opening %s\n%s', (mainpage_file, err)) | 
| + return | 
| + | 
| + lines = f.readlines() | 
| + f.close() | 
| + | 
| + fw.write(cstart) | 
| + fw.write('\n') | 
| + fw.write(mp) | 
| + fw.write('\n') | 
| + | 
| + for line in lines: | 
| + if sec_char in line: | 
| + comment = True | 
| + section = not section | 
| + elif section: | 
| + sec_ctr += 1 | 
| + section_name = ' section%d ' % sec_ctr | 
| + fw.write(section_begin + section_name + line) | 
| + else: | 
| + # comment is used to denote when we should start recording text | 
| + # from the README file. Some of the initial text is not needed. | 
| + if comment: | 
| + if command_prompt in line: | 
| + line = line.rstrip() | 
| + if line[-1] == command_cont: | 
| + fw.write(vstart + line[:-1]) | 
| + command = True | 
| + else: | 
| + fw.write(vstart + line + vend) | 
| + elif command: | 
| + line = line.strip() | 
| + if line[-1] == command_cont: | 
| + fw.write(line) | 
| + else: | 
| + fw.write(line + vend) | 
| + command = False | 
| + else: | 
| + fw.write(line) | 
| + | 
| + fw.write('\n') | 
| + fw.write(cend) | 
| + fw.close() | 
| + | 
| + def _ConfigDoxygen(self): | 
| + """Set Doxygen configuration to match our options.""" | 
| + | 
| + doxy_config = { | 
| + 'ALPHABETICAL_INDEX': 'YES', | 
| + 'EXTRACT_ALL': 'YES', | 
| + 'EXTRACT_LOCAL_METHODS': 'YES', | 
| + 'EXTRACT_PRIVATE': 'YES', | 
| + 'EXTRACT_STATIC': 'YES', | 
| + 'FILE_PATTERNS': '*.py *.txt', | 
| + 'FULL_PATH_NAMES ': 'YES', | 
| + 'GENERATE_TREEVIEW': 'YES', | 
| + 'HTML_DYNAMIC_SECTIONS': 'YES', | 
| + 'HTML_FOOTER': 'footer.html', | 
| + 'HTML_HEADER': 'header.html', | 
| + 'HTML_OUTPUT ': self.html, | 
| + 'INLINE_SOURCES': 'YES', | 
| + 'INPUT ': self.src_tests, | 
| + 'JAVADOC_AUTOBRIEF': 'YES', | 
| + 'LATEX_OUTPUT ': self.latex, | 
| + 'LAYOUT_FILE ': self.layout, | 
| + 'OPTIMIZE_OUTPUT_JAVA': 'YES', | 
| + 'PROJECT_NAME ': self.suitename[self.suite], | 
| + 'PROJECT_NUMBER': self.docversion, | 
| + 'SOURCE_BROWSER': 'YES', | 
| + 'STRIP_CODE_COMMENTS': 'NO', | 
| + 'TAB_SIZE': '4', | 
| + 'USE_INLINE_TREES': 'YES', | 
| + } | 
| + | 
| + doxy_layout = { | 
| + 'tab type="mainpage"': 'title="%s"' % | 
| + self.suitename[self.suite], | 
| + 'tab type="namespaces"': 'title="Tests"', | 
| + 'tab type="namespacemembers"': 'title="Test Functions"', | 
| + } | 
| + | 
| + for line in fileinput.input(self.doxyconf, inplace=1): | 
| + for k in doxy_config: | 
| + if line.startswith(k): | 
| + line = '%s = %s\n' % (k, doxy_config[k]) | 
| + print line, | 
| + | 
| + for line in fileinput.input('header.html', inplace=1): | 
| + if line.startswith('<H2>'): | 
| + line = '<H2>%s</H2>\n' % self.suitename[self.suite] | 
| + print line, | 
| + | 
| + for line in fileinput.input(self.layout, inplace=1): | 
| + for k in doxy_layout: | 
| + if line.find(k) != -1: | 
| + line = line.replace('title=""', doxy_layout[k]) | 
| + print line, | 
| + | 
| + | 
| + def RunDoxygen(self, doxyargs): | 
| + """Execute Doxygen on the files in the self.src_tests directory. | 
| + | 
| + Args: | 
| + doxyargs: string, any command line args to be passed to doxygen. | 
| + """ | 
| + | 
| + doxycmd = 'doxygen %s' % doxyargs | 
| + | 
| + p = subprocess.Popen(doxycmd, shell=True, stdout=subprocess.PIPE, | 
| + stderr=subprocess.PIPE) | 
| + p.wait() | 
| + cmdoutput = p.stdout.read() | 
| + | 
| + if p.returncode: | 
| + self.logger.error('Error while running %s', doxycmd) | 
| + self.logger.error(cmdoutput) | 
| + else: | 
| + self.logger.info('%s successfully ran', doxycmd) | 
| + | 
| + | 
| + def CreateDocs(self): | 
| + """Configure and execute Doxygen to create HTML docuements.""" | 
| + | 
| + # First run doxygen with args to create default configuration files. | 
| + # Create layout xml file. | 
| + doxyargs = '-l %s' % self.layout | 
| + self.RunDoxygen(doxyargs) | 
| + | 
| + # Create doxygen configuration file. | 
| + doxyargs = '-g %s' % self.doxyconf | 
| + self.RunDoxygen(doxyargs) | 
| + | 
| + # Edit the configuration files to match our options. | 
| + self._ConfigDoxygen() | 
| + | 
| + # Run doxygen with configuration file as argument. | 
| + self.RunDoxygen(self.doxyconf) | 
| + | 
| + | 
| + def CleanDocs(self): | 
| + """Run some post processing on the newly created docs.""" | 
| + | 
| + logo_image = 'customLogo.gif' | 
| + | 
| + # Key = original string, value = replacement string. | 
| + replace = { | 
| + '>Package': '>Test', | 
| + } | 
| + | 
| + docpages = os.path.join(self.html, '*.html') | 
| + files = glob.glob(docpages) | 
| + for file in files: | 
| + for line in fileinput.input(file, inplace=1): | 
| + for k in replace: | 
| + if line.find(k) != -1: | 
| + line = line.replace(k, replace[k]) | 
| + print line, | 
| + | 
| + shutil.copy(logo_image, self.html) | 
| + | 
| + self.logger.info('Sanitized documentation completed.') | 
| + | 
| + | 
| +class ReadNode(object): | 
| + """Parse a compiler node object from a control file. | 
| + | 
| + Args: | 
| + suite: boolean, set to True if parsing nodes from a suite control file. | 
| + """ | 
| + | 
| + def __init__(self, suite=False): | 
| + self.key = '' | 
| + self.value = '' | 
| + self.testdef = False | 
| + self.suite = suite | 
| + self.bullet = ' - ' | 
| + | 
| + def visitName(self, n): | 
| + if n.name == 'job': | 
| + self.testdef = True | 
| + | 
| + def visitConst(self, n): | 
| + if self.testdef: | 
| + self.key = str(n.value) | 
| + self.testdef = False | 
| + else: | 
| + self.value += str(n.value) + '\n' | 
| + | 
| + def visitKeyword(self, n): | 
| + if n.name != 'constraints': | 
| + self.value += self.bullet + n.name + ': ' | 
| + for item in n.expr: | 
| + if isinstance(item, compiler.ast.Const): | 
| + for i in item: | 
| + self.value += self.bullet + str(i) + '\n' | 
| + self.value += ' .\n' | 
| + else: | 
| + self.value += str(item) + '\n' | 
| + | 
| + | 
| + def visitAssName(self, n): | 
| + # To remove section from appearing in the documentation, set value = ''. | 
| + sections = { | 
| + 'AUTHOR': '', | 
| + 'CRITERIA': '<H3>Pass/Fail Criteria:</H3>\n', | 
| + 'DOC': '<H3>Notes</H3>\n', | 
| + 'NAME': '', | 
| + 'PURPOSE': '\\brief\n', | 
| + 'TIME': '<H3>Test Duration</H3>\n', | 
| + 'TEST_CATEGORY': '<H3>Category</H3>\n', | 
| + 'TEST_CLASS': '<H3>Test Class</H3>\n', | 
| + 'TEST_TYPE': '<H3>Test Type</H3>\n', | 
| + } | 
| + | 
| + if not self.suite: | 
| + self.key = sections.get(n.name, n.name) | 
| + | 
| + | 
| +def main(): | 
| + doc = DocCreator() | 
| + doc.GetTests() | 
| + doc.ParseControlFiles() | 
| + doc.CreateMainPage() | 
| + doc.CreateDocs() | 
| + doc.CleanDocs() | 
| + | 
| + | 
| +if __name__ == '__main__': | 
| + main() |