OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """ Parse suite control files and make HTML documentation from included tests. | |
7 | |
8 This program will create a list of test cases found in suite files by parsing | |
9 through each suite control file and making a list of all of the jobs called from | |
10 it. Once it has a list of tests, it will parse the AutoTest control file for | |
11 each test and grab the doc strings. These doc strings, along with any | |
12 constraints in the suite control file, will be added to the original test | |
13 script. These new scripts will be placed in a stand alone directory. Doxygen | |
14 will then use these files for the sole purpose of producing HTML documentation | |
15 for all of the tests. Once HTML docs are created some post processing will be | |
16 done against the docs to change a few strings. | |
17 | |
18 If this script is executed without a --src argument, it will assume it is being | |
19 executed from <ChromeOS>/src/third_party/autotest/files/utils/docgen/ directory. | |
20 | |
21 Classes: | |
22 | |
23 DocCreator | |
24 This class is responsible for all processing. It requires the following: | |
25 - Absolute path of suite control files. | |
26 - Absolute path of where to place temporary files it constructs from the | |
27 control files and test scripts. | |
28 This class makes the following assumptions: | |
29 - Each master suite has a README.txt file with general instructions on | |
30 test preparation and usage. | |
31 - The control file for each test has doc strings with labels of: | |
32 - PURPOSE: one line description of why this test exists. | |
33 - CRITERIA: Pass/Failure conditions. | |
34 - DOC: additional test details. | |
35 ReadNode | |
36 This class parses a node from a control file into a key/value pair. In this | |
37 context, a node represents a syntactic construct of an abstract syntax tree. | |
38 The root of the tree is the module object (in this case a control file). If | |
39 suite=True, it will assume the node is from a suite control file. | |
40 | |
41 Doxygen should already be configured with a configuration file called: | |
42 doxygen.conf. This file should live in the same directory with this program. | |
43 If you haven't installed doxygen, you'll need to install this program before | |
44 this script is executed. This program will automatically update the doxygen.conf | |
45 file to match self.src_tests and self.html. | |
46 | |
47 TODO: (kdlucas@google.com) Update ReadNode class to use the replacement module | |
48 for the compiler module, as that has been deprecated. | |
49 """ | |
50 | |
51 __author__ = 'kdlucas@google.com (Kelly Lucas)' | |
52 __version__ = '0.8.0' | |
53 | |
54 import compiler | |
55 import fileinput | |
56 import glob | |
57 import logging | |
58 import optparse | |
59 import os | |
60 import re | |
61 import shutil | |
62 import subprocess | |
63 import sys | |
64 | |
65 | |
66 class DocCreator(object): | |
67 """Process suite control files to combine docstrings and create HTML docs. | |
68 | |
69 The DocCreator class is designed to parse AutoTest suite control files to | |
70 find all of the tests referenced, and build HTML documentation based on the | |
71 docstrings in those files. It will cross reference the test control file | |
72 and any parameters passed through the suite file, with the original test | |
73 case. DocCreator relies on doxygen to actually generate the HTML documents. | |
74 | |
75 The workflow is as follows: | |
76 - Parse the suite file(s) and generate a test list. | |
77 - Locate the test source, and grab the docstrings from the associated | |
78 AutoTest control file. | |
79 - Combine the docstring from the control file with any parameters passed | |
80 in from the suite control file, with the original test case. | |
81 - Write a new test file with the combined docstrings to src_tests. | |
82 - Create HTML documentation by running doxygen against the tests stored | |
83 in self.src_tests. | |
84 | |
85 Implements the following methods: | |
86 - SetLogger() - Gets a logger and sets the formatting. | |
87 - GetTests() - Parse suite control files, create a dictionary of tests. | |
88 - ParseControlFiles() - Runs through all tests and parses control files | |
89 - _CleanDir() - Remove any files in a direcory and create an empty one. | |
90 - _GetDoctString() - Parses docstrings and joins it with constraints. | |
91 - _CreateTest() - Add docstrings and constraints to existing test script | |
92 to form a new test script. | |
93 - CreateMainPage() - Create a mainpage.txt file based on contents of the | |
94 suite README file. | |
95 - _ConfigDoxygen - Updates doxygen.conf to match some attributes this | |
96 script was run with. | |
97 - RunDoxygen() - Executes the doxygen program. | |
98 - CleanDocs() - Changes some text in the HTML files to conform to our | |
99 naming conventions and style. | |
100 | |
101 Depends upon class ReadNode. | |
102 """ | |
103 def __init__(self): | |
104 """Parse command line arguments and set some initial variables.""" | |
105 | |
106 desc="""%prog will scan AutoTest suite control files to build a list of | |
107 test cases called in the suite, and build HTML documentation based on | |
108 the docstrings it finds in the tests, control files, and suite control | |
109 files. | |
110 """ | |
111 | |
112 self.runpath = os.path.abspath('.') | |
113 src = os.path.join(self.runpath, '../../../../../') | |
114 srcdir = os.path.abspath(src) | |
115 | |
116 parser = optparse.OptionParser(description=desc, | |
117 prog='CreateDocs', | |
118 version=__version__, | |
119 usage='%prog') | |
120 parser.add_option('--debug', | |
121 help='Debug level [default: %default]', | |
122 default='debug', | |
123 dest='debug') | |
124 parser.add_option('--docversion', | |
125 help='Specify a version for the documentation' | |
126 '[default: %default]', | |
127 default=None, | |
128 dest='docversion') | |
129 parser.add_option('--doxy', | |
130 help='doxygen configuration file [default: %default]', | |
131 default='doxygen.conf', | |
132 dest='doxyconf') | |
133 parser.add_option('--html', | |
134 help='path to store html docs [default: %default]', | |
135 default='html', | |
136 dest='html') | |
137 parser.add_option('--latex', | |
138 help='path to store latex docs [default: %default]', | |
139 default='latex', | |
140 dest='latex') | |
141 parser.add_option('--layout', | |
142 help='doxygen layout file [default: %default]', | |
143 default='doxygenLayout.xml', | |
144 dest='layout') | |
145 parser.add_option('--log', | |
146 help='Logfile for program output [default: %default]', | |
147 default='docCreator.log', | |
148 dest='logfile') | |
149 parser.add_option('--readme', | |
150 help='filename of suite documentation' | |
151 '[default: %default]', | |
152 default='README.txt', | |
153 dest='readme') | |
154 parser.add_option('--src', | |
155 help='path to chromiumos source root' | |
156 ' [default: %default]', | |
157 default=srcdir, | |
158 dest='src_chromeos') | |
159 #TODO(kdlucas): add an all option that will parse all suites. | |
160 parser.add_option('--suite', | |
161 help='Directory name of suite [default: %default]', | |
162 type='choice', | |
163 default='suite_HWQual', | |
164 choices = [ | |
165 'suite_Factory', | |
166 'suite_HWConfig', | |
167 'suite_HWQual', | |
168 ], | |
169 dest='suite') | |
170 parser.add_option('--tests', | |
171 help='Absolute path of temporary test files' | |
172 ' [default: %default]', | |
173 default='testsource', | |
174 dest='src_tests') | |
175 | |
176 self.options, self.args = parser.parse_args() | |
177 | |
178 | |
179 # Make parameters a little shorter by making the following assignments. | |
180 self.debug = self.options.debug | |
181 self.docversion = self.options.docversion | |
182 self.doxyconf = self.options.doxyconf | |
183 self.html = self.options.html | |
184 self.latex = self.options.latex | |
185 self.layout = self.options.layout | |
186 self.logfile = self.options.logfile | |
187 self.readme = self.options.readme | |
188 self.src_tests = self.options.src_tests | |
189 self.src = self.options.src_chromeos | |
190 self.suite = self.options.suite | |
191 | |
192 autotest_path = 'third_party/autotest/files' | |
193 sitetests = 'client/site_tests' | |
194 tests = 'client/tests' | |
195 self.testcase = {} | |
196 | |
197 self.site_dir = os.path.join(self.src, autotest_path, sitetests) | |
198 self.test_dir = os.path.join(self.src, autotest_path, tests) | |
199 self.suite_dir = os.path.join(self.site_dir, self.suite) | |
200 | |
201 self.logger = self.SetLogger('docCreator') | |
202 self.logger.debug('Executing with debug level: %s', self.debug) | |
203 self.logger.debug('Writing to logfile: %s', self.logfile) | |
204 self.logger.debug('New test directory: %s', self.src_tests) | |
205 self.logger.debug('Chrome OS src directory: %s', self.src) | |
206 self.logger.debug('Test suite: %s', self.suite) | |
207 | |
208 self.suitename = {'suite_Factory': 'Factory Testing', | |
209 'suite_HWConfig': 'Hardware Configuration', | |
210 'suite_HWQual': 'Hardware Qualification', | |
211 } | |
212 | |
213 def SetLogger(self, namespace): | |
214 """Create a logger with some good formatting options. | |
215 | |
216 Args: | |
217 namespace: string, name associated with this logger. | |
218 Returns: | |
219 Logger object. | |
220 This method assumes self.logfile and self.debug are already set. | |
221 This logger will write to stdout as well as a log file. | |
222 """ | |
223 | |
224 loglevel = {'debug': logging.DEBUG, | |
225 'info': logging.INFO, | |
226 'warning': logging.WARNING, | |
227 'error': logging.ERROR, | |
228 'critical': logging.CRITICAL, | |
229 } | |
230 | |
231 logger = logging.getLogger(namespace) | |
232 c = logging.StreamHandler() | |
233 h = logging.FileHandler(os.path.join(self.runpath, self.logfile)) | |
234 hf = logging.Formatter( | |
235 '%(asctime)s %(process)d %(levelname)s: %(message)s') | |
236 cf = logging.Formatter('%(levelname)s: %(message)s') | |
237 logger.addHandler(h) | |
238 logger.addHandler(c) | |
239 h.setFormatter(hf) | |
240 c.setFormatter(cf) | |
241 | |
242 if self.debug in loglevel: | |
243 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.
| |
244 else: | |
245 logger.setLevel(logging.INFO) | |
246 | |
247 return logger | |
248 | |
249 | |
250 def GetTests(self): | |
251 """Create dictionary of tests based on suite control file contents.""" | |
252 | |
253 suite_search = os.path.join(self.suite_dir, 'control.*') | |
254 for suitefile in glob.glob(suite_search): | |
255 self.logger.debug('Scanning %s for tests', suitefile) | |
256 try: | |
257 suite = compiler.parseFile(suitefile) | |
258 except SyntaxError, e: | |
259 self.logger.error('Error parsing: %s\n%s', (suitefile, e)) | |
260 raise SystemExit | |
261 | |
262 # Walk through each node found in the control file, which in our | |
263 # case will be a call to a test. compiler.walk() will walk through | |
264 # each component node, and call the appropriate function in class | |
265 # ReadNode. The returned key should be a string, and the name of a | |
266 # test. visitor.value should be any extra arguments found in the | |
267 # suite file that are used with that test case. | |
268 for n in suite.node.nodes: | |
269 visitor = ReadNode(suite=True) | |
270 compiler.walk(n, visitor) | |
271 if len(visitor.key) > 1: | |
272 self.logger.debug('Found test %s', visitor.key) | |
273 filtered_input = '' | |
274 # Lines in value should start with ' -' for bullet item. | |
275 if visitor.value: | |
276 lines = visitor.value.split('\n') | |
277 for line in lines: | |
278 if line.startswith(' -'): | |
279 filtered_input += line + '\n' | |
280 # A test could be called multiple times, so see if the key | |
281 # already exists, and if so append the new value. | |
282 if visitor.key in self.testcase: | |
283 s = self.testcase[visitor.key] + filtered_input | |
284 self.testcase[visitor.key] = s | |
285 else: | |
286 self.testcase[visitor.key] = filtered_input | |
287 | |
288 | |
289 def _CleanDir(self, directory): | |
290 """Ensure the directory is available and empty. | |
291 | |
292 Args: | |
293 directory: string, path of directory | |
294 """ | |
295 | |
296 if os.path.isdir(directory): | |
297 try: | |
298 shutil.rmtree(directory) | |
299 except IOError, err: | |
300 self.logger.error('Error cleaning %s\n%s', (directory, err)) | |
301 try: | |
302 os.makedirs(directory) | |
303 except IOError, err: | |
304 self.logger.error('Error creating %s\n%s', (directory, err)) | |
305 self.logger.error('Check your permissions of %s', directory) | |
306 raise SystemExit | |
307 | |
308 | |
309 def ParseControlFiles(self): | |
310 """Get docstrings from control files and add them to new test scripts. | |
311 | |
312 This method will cycle through all of the tests and attempt to find | |
313 their control file. If found, it will parse the docstring from the | |
314 control file, add this to any parameters found in the suite file, and | |
315 add this combined docstring to the original test. These new tests will | |
316 be written in the self.src_tests directory. | |
317 """ | |
318 # Clean some target directories. | |
319 for d in [self.src_tests, self.html]: | |
320 self._CleanDir(d) | |
321 | |
322 for test in self.testcase: | |
323 testdir = os.path.join(self.site_dir, test) | |
324 if not os.path.isdir(testdir): | |
325 testdir = os.path.join(self.test_dir, test) | |
326 | |
327 if os.path.isdir(testdir): | |
328 control_file = os.path.join(testdir, 'control') | |
329 test_file = os.path.join(testdir, test + '.py') | |
330 docstring = self._GetDocString(control_file, test) | |
331 self._CreateTest(test_file, docstring, test) | |
332 else: | |
333 self.logger.warning('Cannot find test: %s', test) | |
334 | |
335 def _GetDocString(self, control_file, test): | |
336 """Get the docstrings from control file and join to suite file params. | |
337 | |
338 Args: | |
339 control_file: string, absolute path to test control file. | |
340 test: string, name of test. | |
341 Returns: | |
342 string: combined docstring with needed markup language for doxygen. | |
343 """ | |
344 | |
345 # Doxygen needs the @package marker. | |
346 package_doc = '## @package ' | |
347 # To allow doxygen to use special commands, we must use # for comments. | |
348 comment = '# ' | |
349 endlist = ' .\n' | |
350 control_dict = {} | |
351 output = [] | |
352 temp = [] | |
353 tempstring = '' | |
354 docstring = '' | |
355 keys = ['\\brief\n', '<H3>Pass/Fail Criteria:</H3>\n', | |
356 '<H3>Author</H3>\n', '<H3>Test Duration</H3>\n', | |
357 '<H3>Category</H3>\n', '<H3>Test Type</H3>\n', | |
358 '<H3>Test Class</H3>\n', '<H3>Notest</H3>\n', | |
359 ] | |
360 | |
361 try: | |
362 control = compiler.parseFile(control_file) | |
363 except SyntaxError, e: | |
364 self.logger.error('Error parsing: %s\n%s', (control_file, e)) | |
365 return None | |
366 | |
367 for n in control.node.nodes: | |
368 visitor = ReadNode() | |
369 compiler.walk(n, visitor) | |
370 control_dict[visitor.key] = visitor.value | |
371 | |
372 for k in keys: | |
373 if k in control_dict: | |
374 if len(control_dict[k]) > 1: | |
375 if k != test: | |
376 temp.append(k) | |
377 temp.append(control_dict[k]) | |
378 if control_dict[k]: | |
379 temp.append(endlist) | |
380 # Add constraints and extra args after the Criteria section. | |
381 if 'Criteria:' in k: | |
382 if self.testcase[test]: | |
383 temp.append('<H3>Arguments:</H3>\n') | |
384 temp.append(self.testcase[test]) | |
385 # '.' character at the same level as the '-' tells | |
386 # doxygen this is the end of the list. | |
387 temp.append(endlist) | |
388 | |
389 output.append(package_doc + test + '\n') | |
390 tempstring = "".join(temp) | |
391 lines = tempstring.split('\n') | |
392 for line in lines: | |
393 # Doxygen requires a '#' character to add special doxygen commands. | |
394 comment_line = comment + line + '\n' | |
395 output.append(comment_line) | |
396 | |
397 docstring = "".join(output) | |
398 | |
399 return docstring | |
400 | |
401 | |
402 def _CreateTest(self, test_file, docstring, test): | |
403 """Create a new test with the combined docstrings from multiple sources. | |
404 | |
405 Args: | |
406 test_file: string, file name of new test to write. | |
407 docstring: string, the docstring to add to the existing test. | |
408 test: string, name of the test. | |
409 | |
410 This method is used to create a temporary copy of a new test, that will | |
411 be a combination of the original test plus the docstrings from the | |
412 control file, and any constraints from the suite control file. | |
413 """ | |
414 | |
415 class_def = 'class ' + test | |
416 pathname = os.path.join(self.src_tests, test + '.py') | |
417 | |
418 # Open the test and write out new test with added docstrings | |
419 try: | |
420 f = open(test_file, 'r') | |
421 except IOError, err: | |
422 self.logger.error('Error while reading %s\n%s', (test_file, err)) | |
423 return | |
424 lines = f.readlines() | |
425 f.close() | |
426 | |
427 try: | |
428 f = open(pathname, 'w') | |
429 except IOError, err: | |
430 self.logger.error('Error creating %s\n%s', (pathname, err)) | |
431 return | |
432 | |
433 for line in lines: | |
434 if class_def in line: | |
435 f.write(docstring) | |
436 f.write('\n') | |
437 f.write(line) | |
438 f.close() | |
439 | |
440 def CreateMainPage(self): | |
441 """Create a main page to provide content for index.html. | |
442 | |
443 This method assumes a file named README.txt is located in your suite | |
444 directory with general instructions on setting up and using the suite. | |
445 If your README file is in another file, ensure you pass a --readme | |
446 option with the correct filename. To produce a better looking | |
447 landing page, use the '-' character for list items. This method assumes | |
448 os commands start with '$'. | |
449 """ | |
450 | |
451 # Define some strings that Doxygen uses for specific formatting. | |
452 cstart = '/**' | |
453 cend = '**/' | |
454 mp = '@mainpage' | |
455 section_begin = '@section ' | |
456 vstart = '@verbatim ' | |
457 vend = ' @endverbatim\n' | |
458 | |
459 # Define some characters we expect to delineate sections in the README. | |
460 sec_char = '==========' | |
461 command_prompt = '$ ' | |
462 command_cont = '\\' | |
463 | |
464 command = False | |
465 comment = False | |
466 section = False | |
467 sec_ctr = 0 | |
468 | |
469 readme_file = os.path.join(self.suite_dir, self.readme) | |
470 mainpage_file = os.path.join(self.src_tests, 'mainpage.txt') | |
471 | |
472 try: | |
473 f = open(readme_file, 'r') | |
474 except IOError, err: | |
475 self.logger.error('Error opening %s\n%s', (readme_file, err)) | |
476 return | |
477 try: | |
478 fw = open(mainpage_file, 'w') | |
479 except IOError, err: | |
480 self.logger.error('Error opening %s\n%s', (mainpage_file, err)) | |
481 return | |
482 | |
483 lines = f.readlines() | |
484 f.close() | |
485 | |
486 fw.write(cstart) | |
487 fw.write('\n') | |
488 fw.write(mp) | |
489 fw.write('\n') | |
490 | |
491 for line in lines: | |
492 if sec_char in line: | |
493 comment = True | |
494 section = not section | |
495 elif section: | |
496 sec_ctr += 1 | |
497 section_name = ' section%d ' % sec_ctr | |
498 fw.write(section_begin + section_name + line) | |
499 else: | |
500 # comment is used to denote when we should start recording text | |
501 # from the README file. Some of the initial text is not needed. | |
502 if comment: | |
503 if command_prompt in line: | |
504 line = line.rstrip() | |
505 if line[-1] == command_cont: | |
506 fw.write(vstart + line[:-1]) | |
507 command = True | |
508 else: | |
509 fw.write(vstart + line + vend) | |
510 elif command: | |
511 line = line.strip() | |
512 if line[-1] == command_cont: | |
513 fw.write(line) | |
514 else: | |
515 fw.write(line + vend) | |
516 command = False | |
517 else: | |
518 fw.write(line) | |
519 | |
520 fw.write('\n') | |
521 fw.write(cend) | |
522 fw.close() | |
523 | |
524 def _ConfigDoxygen(self): | |
525 """Set Doxygen configuration to match our options.""" | |
526 | |
527 doxy_config = { | |
528 'ALPHABETICAL_INDEX': 'YES', | |
529 'EXTRACT_ALL': 'YES', | |
530 'EXTRACT_LOCAL_METHODS': 'YES', | |
531 'EXTRACT_PRIVATE': 'YES', | |
532 'EXTRACT_STATIC': 'YES', | |
533 'FILE_PATTERNS': '*.py *.txt', | |
534 'FULL_PATH_NAMES ': 'YES', | |
535 'GENERATE_TREEVIEW': 'YES', | |
536 'HTML_DYNAMIC_SECTIONS': 'YES', | |
537 'HTML_FOOTER': 'footer.html', | |
538 'HTML_HEADER': 'header.html', | |
539 'HTML_OUTPUT ': self.html, | |
540 'INLINE_SOURCES': 'YES', | |
541 'INPUT ': self.src_tests, | |
542 'JAVADOC_AUTOBRIEF': 'YES', | |
543 'LATEX_OUTPUT ': self.latex, | |
544 'LAYOUT_FILE ': self.layout, | |
545 'OPTIMIZE_OUTPUT_JAVA': 'YES', | |
546 'PROJECT_NAME ': self.suitename[self.suite], | |
547 'PROJECT_NUMBER': self.docversion, | |
548 'SOURCE_BROWSER': 'YES', | |
549 'STRIP_CODE_COMMENTS': 'NO', | |
550 'TAB_SIZE': '4', | |
551 'USE_INLINE_TREES': 'YES', | |
552 } | |
553 | |
554 doxy_layout = { | |
555 'tab type="mainpage"': 'title="%s"' % | |
556 self.suitename[self.suite], | |
557 'tab type="namespaces"': 'title="Tests"', | |
558 'tab type="namespacemembers"': 'title="Test Functions"', | |
559 } | |
560 | |
561 for line in fileinput.input(self.doxyconf, inplace=1): | |
562 for k in doxy_config: | |
563 if line.startswith(k): | |
564 line = '%s = %s\n' % (k, doxy_config[k]) | |
565 print line, | |
566 | |
567 for line in fileinput.input('header.html', inplace=1): | |
568 if line.startswith('<H2>'): | |
569 line = '<H2>%s</H2>\n' % self.suitename[self.suite] | |
570 print line, | |
571 | |
572 for line in fileinput.input(self.layout, inplace=1): | |
573 for k in doxy_layout: | |
574 if line.find(k) != -1: | |
575 line = line.replace('title=""', doxy_layout[k]) | |
576 print line, | |
577 | |
578 | |
579 def RunDoxygen(self, doxyargs): | |
580 """Execute Doxygen on the files in the self.src_tests directory. | |
581 | |
582 Args: | |
583 doxyargs: string, any command line args to be passed to doxygen. | |
584 """ | |
585 | |
586 doxycmd = 'doxygen %s' % doxyargs | |
587 | |
588 p = subprocess.Popen(doxycmd, shell=True, stdout=subprocess.PIPE, | |
589 stderr=subprocess.PIPE) | |
590 p.wait() | |
591 cmdoutput = p.stdout.read() | |
592 | |
593 if p.returncode: | |
594 self.logger.error('Error while running %s', doxycmd) | |
595 self.logger.error(cmdoutput) | |
596 else: | |
597 self.logger.info('%s successfully ran', doxycmd) | |
598 | |
599 | |
600 def CreateDocs(self): | |
601 """Configure and execute Doxygen to create HTML docuements.""" | |
602 | |
603 # First run doxygen with args to create default configuration files. | |
604 # Create layout xml file. | |
605 doxyargs = '-l %s' % self.layout | |
606 self.RunDoxygen(doxyargs) | |
607 | |
608 # Create doxygen configuration file. | |
609 doxyargs = '-g %s' % self.doxyconf | |
610 self.RunDoxygen(doxyargs) | |
611 | |
612 # Edit the configuration files to match our options. | |
613 self._ConfigDoxygen() | |
614 | |
615 # Run doxygen with configuration file as argument. | |
616 self.RunDoxygen(self.doxyconf) | |
617 | |
618 | |
619 def CleanDocs(self): | |
620 """Run some post processing on the newly created docs.""" | |
621 | |
622 logo_image = 'customLogo.gif' | |
623 | |
624 # Key = original string, value = replacement string. | |
625 replace = { | |
626 '>Package': '>Test', | |
627 } | |
628 | |
629 docpages = os.path.join(self.html, '*.html') | |
630 files = glob.glob(docpages) | |
631 for file in files: | |
632 for line in fileinput.input(file, inplace=1): | |
633 for k in replace: | |
634 if line.find(k) != -1: | |
635 line = line.replace(k, replace[k]) | |
636 print line, | |
637 | |
638 shutil.copy(logo_image, self.html) | |
639 | |
640 self.logger.info('Sanitized documentation completed.') | |
641 | |
642 | |
643 class ReadNode(object): | |
644 """Parse a compiler node object from a control file. | |
645 | |
646 Args: | |
647 suite: boolean, set to True if parsing nodes from a suite control file. | |
648 """ | |
649 | |
650 def __init__(self, suite=False): | |
651 self.key = '' | |
652 self.value = '' | |
653 self.testdef = False | |
654 self.suite = suite | |
655 self.bullet = ' - ' | |
656 | |
657 def visitName(self, n): | |
658 if n.name == 'job': | |
659 self.testdef = True | |
660 | |
661 def visitConst(self, n): | |
662 if self.testdef: | |
663 self.key = str(n.value) | |
664 self.testdef = False | |
665 else: | |
666 self.value += str(n.value) + '\n' | |
667 | |
668 def visitKeyword(self, n): | |
669 if n.name != 'constraints': | |
670 self.value += self.bullet + n.name + ': ' | |
671 for item in n.expr: | |
672 if isinstance(item, compiler.ast.Const): | |
673 for i in item: | |
674 self.value += self.bullet + str(i) + '\n' | |
675 self.value += ' .\n' | |
676 else: | |
677 self.value += str(item) + '\n' | |
678 | |
679 | |
680 def visitAssName(self, n): | |
681 # To remove section from appearing in the documentation, set value = ''. | |
682 sections = { | |
683 'AUTHOR': '', | |
684 'CRITERIA': '<H3>Pass/Fail Criteria:</H3>\n', | |
685 'DOC': '<H3>Notes</H3>\n', | |
686 'NAME': '', | |
687 'PURPOSE': '\\brief\n', | |
688 'TIME': '<H3>Test Duration</H3>\n', | |
689 'TEST_CATEGORY': '<H3>Category</H3>\n', | |
690 'TEST_CLASS': '<H3>Test Class</H3>\n', | |
691 'TEST_TYPE': '<H3>Test Type</H3>\n', | |
692 } | |
693 | |
694 if not self.suite: | |
695 self.key = sections.get(n.name, n.name) | |
696 | |
697 | |
698 def main(): | |
699 doc = DocCreator() | |
700 doc.GetTests() | |
701 doc.ParseControlFiles() | |
702 doc.CreateMainPage() | |
703 doc.CreateDocs() | |
704 doc.CleanDocs() | |
705 | |
706 | |
707 if __name__ == '__main__': | |
708 main() | |
OLD | NEW |