#!/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(
' Nr | Code | Expected |
\n')
fout.write(' ' + str(num) + ' | ')
fout.write('' + strtoxml(
node['code']).replace('\\n', '\n') + ' | ')
fout.write(
'' + strtoxml(node['expected']).replace('\\n', ' ') + ' | ')
fout.write('
\n')
if testclass is not None:
fout.write('
\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 = ' '
print(s)
print('')
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(' Name | Errors | All |
\n')
for functionname in functionNames:
findex.write(' ' + functionname + ' | ')
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('0 | ')
else:
findex.write('' + str(numerr) + ' | ')
findex.write('' + str(numall) + ' | ')
findex.write('
\n')
findex.write('
\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'])