#!/usr/bin/env python # # Cppcheck - A tool for static C/C++ code analysis # Copyright (C) 2007-2016 Cppcheck team. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Extract test cases information from Cppcheck test file """ import os import sys import re class Extract: """ Read Cppcheck test file and create data representation """ # array that stores all the test cases nodes = [] def parseFile(self, filename): """ parse test file and add info to the nodes variable """ name = '[0-9a-zA-Z_]+' string = '\\"(.+)\\"' testclass = None functionName = None code = None fin = open(filename, 'r') for line in fin: # testclass starts res = re.match('class (' + name + ')', line) if res is not None: testclass = res.group(1) # end of testclass if re.match('};', line) is not None: testclass = None # function start res = re.match('\\s+void (' + name + ')\\(\\)', line) if res is not None: functionName = res.group(1) elif re.match('\\s+}', line) is not None: functionName = None if functionName is None: continue # check res = re.match('\s+check.*\(' + string, line) if res is not None: code = res.group(1) # code.. if code is not None: res = re.match('\\s+' + string, line) if res is not None: code = code + res.group(1) # assert res = re.match('\\s+ASSERT_EQUALS\\(\\"([^"]*)\\",', line) if res is not None and code is not None: node = {'testclass': testclass, 'functionName': functionName, 'code': code, 'expected': res.group(1)} self.nodes.append(node) code = None # close test file fin.close() def strtoxml(s): """Convert string to xml/html format""" return s.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') def trimname(name): """Trim test name. Trailing underscore and digits are removed""" while name[-1].isdigit(): name = name[:-1] if name[-1] == '_': name = name[:-1] return name def writeHtmlFile(nodes, functionName, filename, errorsOnly): """Write html file for a function name""" fout = open(filename, 'w') fout.write('\n') fout.write('\n') fout.write(' \n') fout.write('\n') fout.write('\n') fout.write('Home -- ') if errorsOnly: fout.write('All test cases') else: fout.write( 'Error test cases') fout.write('

') testclass = None num = 0 for node in nodes: if errorsOnly and node['expected'] == '': continue if trimname(node['functionName']) == functionName: num = num + 1 if not testclass: testclass = node['testclass'] fout.write( '

' + node['testclass'] + '::' + functionName + '

') fout.write('\n') fout.write( ' \n') fout.write(' ') fout.write('') fout.write( '') fout.write('\n') if testclass is not None: fout.write('
NrCodeExpected
' + str(num) + '
' + strtoxml(
                node['code']).replace('\\n', '\n') + '
' + strtoxml(node['expected']).replace('\\n', '
') + '
\n') fout.write('\n') fout.close() if len(sys.argv) <= 1 or '--help' in sys.argv: print('Extract test cases from test file') print( 'Syntax: extracttests.py [--html=folder] [--xml] [--code=folder] path/testfile.cpp') sys.exit(0) # parse command line xml = False filename = None htmldir = None codedir = None for arg in sys.argv[1:]: if arg == '--xml': xml = True elif arg.startswith('--html='): htmldir = arg[7:] elif arg.startswith('--code='): codedir = arg[7:] elif arg.endswith('.cpp'): filename = arg else: print('Invalid option: ' + arg) sys.exit(1) # extract test cases if filename is not None: # parse test file e = Extract() e.parseFile(filename) # generate output if xml: print('') print('') count = 0 for node in e.nodes: s = ' ') elif htmldir is not None: if not htmldir.endswith('/'): htmldir += '/' if not os.path.exists(htmldir): os.mkdir(htmldir) findex = open(htmldir + 'index.htm', 'w') findex.write('\n') findex.write('\n') findex.write(' \n') findex.write('\n') findex.write('\n') findex.write('

' + filename + '

\n') functionNames = [] for node in e.nodes: functionname = trimname(node['functionName']) if functionname not in functionNames: functionNames.append(functionname) functionNames.sort() findex.write('\n') findex.write(' \n') for functionname in functionNames: findex.write(' ') numall = 0 numerr = 0 for node in e.nodes: if trimname(node['functionName']) == functionname: numall = numall + 1 if node['expected'] != '': numerr = numerr + 1 if numerr == 0: findex.write('') else: findex.write('') findex.write('') findex.write('\n') findex.write('
NameErrorsAll
' + functionname + '
0
' + str(numerr) + '
' + str(numall) + '
\n') findex.write('') findex.close() # create files for each functionName for functionName in functionNames: writeHtmlFile(e.nodes, functionName, htmldir + 'errors-' + functionName + '.htm', True) writeHtmlFile(e.nodes, functionName, htmldir + 'all-' + functionName + '.htm', False) elif codedir: testnum = 0 if not codedir.endswith('/'): codedir = codedir + '/' if not os.path.exists(codedir): os.mkdir(codedir) errors = open(codedir + 'errors.txt', 'w') for node in e.nodes: testnum = testnum + 1 functionName = node['functionName'] code = node['code'] code = code.replace('\\n', '\n') code = code.replace('\\"', '"') expected = node['expected'] filename = '0000' + str(testnum) + '-' filename = filename[-4:] filename += functionName + '.cpp' # source code fout = open(codedir + filename, 'w') fout.write(code) fout.close() # write 'expected' to errors.txt if expected != '': expected = expected.replace('\\n', '\n') expected = expected.replace('\\"', '"') expected = re.sub( '\\[test.cp?p?:', '[' + filename + ':', expected) errors.write(expected) errors.close() else: for node in e.nodes: print(node['functionName'])