libgit2/tests-clar/clar
Scott J. Goldman 06ac3e7f34 Fix clar generated code to compile on MINGW32
MINGW32 does not define _mktemp_s, so we can just use _mktemp instead. See
the non-compressed/non-base64-encoded version of the patch here:
http://gist.github.com/2605249
2012-05-05 13:16:48 -07:00

310 lines
16 KiB
Python
Executable File

#!/usr/bin/env python
from __future__ import with_statement
from string import Template
import re, fnmatch, os
VERSION = "0.10.0"
TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\(\s*void\s*\))\s*\{"
EVENT_CB_REGEX = re.compile(
r"^(void\s+clar_on_(\w+)\(\s*void\s*\))\s*\{",
re.MULTILINE)
SKIP_COMMENTS_REGEX = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE)
CLAR_HEADER = """
/*
* Clar v%s
*
* This is an autogenerated file. Do not modify.
* To add new unit tests or suites, regenerate the whole
* file with `./clar`
*/
""" % VERSION
CLAR_EVENTS = [
'init',
'shutdown',
'test',
'suite'
]
def main():
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-c', '--clar-path', dest='clar_path')
parser.add_option('-v', '--report-to', dest='print_mode', default='default')
options, args = parser.parse_args()
for folder in args or ['.']:
builder = ClarTestBuilder(folder,
clar_path = options.clar_path,
print_mode = options.print_mode)
builder.render()
class ClarTestBuilder:
def __init__(self, path, clar_path = None, print_mode = 'default'):
self.declarations = []
self.suite_names = []
self.callback_data = {}
self.suite_data = {}
self.event_callbacks = []
self.clar_path = os.path.abspath(clar_path) if clar_path else None
self.path = os.path.abspath(path)
self.modules = [
"clar_sandbox.c",
"clar_fixtures.c",
"clar_fs.c"
]
self.modules.append("clar_print_%s.c" % print_mode)
print("Loading test suites...")
for root, dirs, files in os.walk(self.path):
module_root = root[len(self.path):]
module_root = [c for c in module_root.split(os.sep) if c]
tests_in_module = fnmatch.filter(files, "*.c")
for test_file in tests_in_module:
full_path = os.path.join(root, test_file)
test_name = "_".join(module_root + [test_file[:-2]])
with open(full_path) as f:
self._process_test_file(test_name, f.read())
if not self.suite_data:
raise RuntimeError(
'No tests found under "%s"' % folder_name)
def render(self):
main_file = os.path.join(self.path, 'clar_main.c')
with open(main_file, "w") as out:
out.write(self._render_main())
header_file = os.path.join(self.path, 'clar.h')
with open(header_file, "w") as out:
out.write(self._render_header())
print ('Written Clar suite to "%s"' % self.path)
#####################################################
# Internal methods
#####################################################
def _render_cb(self, cb):
return '{"%s", &%s}' % (cb['short_name'], cb['symbol'])
def _render_suite(self, suite):
template = Template(
r"""
{
"${clean_name}",
${initialize},
${cleanup},
${cb_ptr}, ${cb_count}
}
""")
callbacks = {}
for cb in ['initialize', 'cleanup']:
callbacks[cb] = (self._render_cb(suite[cb])
if suite[cb] else "{NULL, NULL}")
return template.substitute(
clean_name = suite['name'].replace("_", "::"),
initialize = callbacks['initialize'],
cleanup = callbacks['cleanup'],
cb_ptr = "_clar_cb_%s" % suite['name'],
cb_count = suite['cb_count']
).strip()
def _render_callbacks(self, suite_name, callbacks):
template = Template(
r"""
static const struct clar_func _clar_cb_${suite_name}[] = {
${callbacks}
};
""")
callbacks = [
self._render_cb(cb)
for cb in callbacks
if cb['short_name'] not in ('initialize', 'cleanup')
]
return template.substitute(
suite_name = suite_name,
callbacks = ",\n\t".join(callbacks)
).strip()
def _render_event_overrides(self):
overrides = []
for event in CLAR_EVENTS:
if event in self.event_callbacks:
continue
overrides.append(
"#define clar_on_%s() /* nop */" % event
)
return '\n'.join(overrides)
def _render_header(self):
template = Template(self._load_file('clar.h'))
declarations = "\n".join(
"extern %s;" % decl
for decl in sorted(self.declarations)
)
return template.substitute(
extern_declarations = declarations,
)
def _render_main(self):
template = Template(self._load_file('clar.c'))
suite_names = sorted(self.suite_names)
suite_data = [
self._render_suite(self.suite_data[s])
for s in suite_names
]
callbacks = [
self._render_callbacks(s, self.callback_data[s])
for s in suite_names
]
callback_count = sum(
len(cbs) for cbs in self.callback_data.values()
)
return template.substitute(
clar_modules = self._get_modules(),
clar_callbacks = "\n".join(callbacks),
clar_suites = ",\n\t".join(suite_data),
clar_suite_count = len(suite_data),
clar_callback_count = callback_count,
clar_event_overrides = self._render_event_overrides(),
)
def _load_file(self, filename):
if self.clar_path:
filename = os.path.join(self.clar_path, filename)
with open(filename) as cfile:
return cfile.read()
else:
import zlib, base64, sys
content = CLAR_FILES[filename]
if sys.version_info >= (3, 0):
content = bytearray(content, 'utf_8')
content = base64.b64decode(content)
content = zlib.decompress(content)
return str(content)
else:
content = base64.b64decode(content)
return zlib.decompress(content)
def _get_modules(self):
return "\n".join(self._load_file(f) for f in self.modules)
def _skip_comments(self, text):
def _replacer(match):
s = match.group(0)
return "" if s.startswith('/') else s
return re.sub(SKIP_COMMENTS_REGEX, _replacer, text)
def _process_test_file(self, suite_name, contents):
contents = self._skip_comments(contents)
self._process_events(contents)
self._process_declarations(suite_name, contents)
def _process_events(self, contents):
for (decl, event) in EVENT_CB_REGEX.findall(contents):
if event not in CLAR_EVENTS:
continue
self.declarations.append(decl)
self.event_callbacks.append(event)
def _process_declarations(self, suite_name, contents):
callbacks = []
initialize = cleanup = None
regex_string = TEST_FUNC_REGEX % suite_name
regex = re.compile(regex_string, re.MULTILINE)
for (declaration, symbol, short_name) in regex.findall(contents):
data = {
"short_name" : short_name,
"declaration" : declaration,
"symbol" : symbol
}
if short_name == 'initialize':
initialize = data
elif short_name == 'cleanup':
cleanup = data
else:
callbacks.append(data)
if not callbacks:
return
tests_in_suite = len(callbacks)
suite = {
"name" : suite_name,
"initialize" : initialize,
"cleanup" : cleanup,
"cb_count" : tests_in_suite
}
if initialize:
self.declarations.append(initialize['declaration'])
if cleanup:
self.declarations.append(cleanup['declaration'])
self.declarations += [
callback['declaration']
for callback in callbacks
]
callbacks.sort(key=lambda x: x['short_name'])
self.callback_data[suite_name] = callbacks
self.suite_data[suite_name] = suite
self.suite_names.append(suite_name)
print(" %s (%d tests)" % (suite_name, tests_in_suite))
CLAR_FILES = {
"clar.c" : r"""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""",
"clar_print_default.c" : r"""eJyFU01P4zAQPSe/YqgU1a5Cuadi98ap4rLaE6DIxA5YSu3InnQPK/479jgFB9FycuZ53vObj5QeBeoOjlZL6Abh2tFpg602Gln4AFQe285OBmuIsZ80qhPQWeMRulfhYJMujDgoz8v/ZcGiJP+k78qCpHu22lshlYRKJjXfQOUfzaqG+CJfvJCrZgp/UDhUMpAC+laWZ6rwrxNK+8/8XEkElHPWJeBcBQnKmB9YRt6Vn0YfTfJYkCunRuuwpVzPLlqnHPJtpsOp0x7d1GFKowTY0EF2T09CaCyHO6GHyamG+hokeO6q8k1TeWCV5/AQgko+wcM1hiOml0VBqte/qNAsjr2I4cpYkMp3To+o7YLS6yFnDNqE8U2HZ+W+6MzowhecFmHOS009+BfK0j2w+SJ7HK5u4f7vfs+D/DmdLJ0vp3N5f6yJTlm+5sl62Me0M1klCehD35X8uj+RsFsixMlWuuqC38SG37C+W0MD6+36B380Ifb9f0gmbjZgrB1hc7Pc3uTokrR4Dru6kA6DqGG73ZLwUbSDDlfCvYw7Cn38KVmMa0gzK479XJ5HGWZBeE0UnjjKSDaHb+U7mrWGAw==""",
"clar_print_tap.c" : r"""eJyNVMFu2zAMPVtfwbgIYBu2gWK3BmuxnYthh+02wFBtORXmSIYkZxiG/vso2m6lJF12skk9ko+PlJh13MkWjlp20A7cNKORyjVSSZfhDzhhXdPqSbkSvG0n6cTqaLWyDtpnbqCYDxQ/CJuzPyzJfMr8LXy3ugLgiW/FEYU+S799+gpHYazUCm4//FBpvmMvjL1D2T5PrtO/1HXa3iGM0WZ2/A/d2BcE7xhLZA/ZJkqYvPZwAyO3VnTAhwG2HRHLbI7NlAFJbCwRgxVRYM/lgIEYxA9a7U+jg4IlxiVxtjXNbV1vu/Nq78tIaUlDNR3WEVtnptbNMAJAQZ9AOkR7Lda6AFVVzSMLfDhzy/cC7mBr35qo7udeDnYfw63A8Uv3+460OMtGowE4y0b+GOqbhwtQ74+RPYp+Cen9MXKQakV2IdL7G5TjSZh8XY/lqBO2NXJ0fqM3H+HL98fHcFkAAsApgeAoj5Wu6/ra5dCKVie8sLQP/hrOF2I2ifXsmNePJryW2lq/hNVCDIkvK/oAqdIO9M8UxUjx48/ChK8mlmMJ0SdyRozaLDtnsysd0Fizy29ORPMGiqJAkv5DCga4f5fgT0gnKoE7WXqBqcCRN4PEI272445MzIQB3i5hWd9+oWHxNZrwtUk/o0iAvxug/T2eAqiET5HPOYXqssV8YX8BFTvXlQ==""",
"clar_sandbox.c" : r"""eJydVWtP4kAU/dz+iism0gpKfWQ3G9YPm+gasioEMJgomdR2KhPplMwM7KLxv++dTqEP0DVrTKjcO+eec+6cKpWvWADBxBdAgqkvyMxXk/tT79uXcdu2pSkzrmwmycKfspCoeJY2OUHCpTJH9/UXrv1qW4PhjyEZglR42mIROBrC0eUm7Enlws4ZeK5tWYKqueDgrfp2BqQzOO/08cChVCROQupW+7Jnxw8CKmWGOiLdXy6cadi2/VbiHDFe5JsyfZxHERVNkOyFEgVTyp8M9V0W8ZBGQEadm5Nj28pwjMqse4EGBcmcKziD03alx+BTvkCjhLwfYw8aYtWG1z3UVWuCfko/Lszn7eCi3+t3f3auLmo2WG8oEaxsEtN6o0SAwxDHawOD7/n4NjQazE3hK7Ox+YkqfHDWRNgYjbGMyfilNlWfUozPqZ6SVjbXq1vNCJQpeDBbOivvsNRcOaehC0uyrDcbf22rtQ+dCNSE6m4mEh5TtC1MqOR19NNfgs+XasL4UxOUWIJKYC4ptHA+7Lfsd0jVdL2W8arSMsUSswIxJLVLp5Ia6EuqhjSe9TSocz7q9s9dc6wJBq5y+XYpD1lkdA0nTIJcSkXjtaApe6YooKRFiw/mQqTCmaCBSrD4gbjDd5UdfiRr9efBUTEAi4SFkEZ6zqXPw8fkj6O/S2OqCRTy7o11gOoPXj1XjVcDI1FMRDBBFcgSaRYMiSQRcQGsmkL0k01DklEwStc8CrdXF4jy2TRNTi3F09bcpT81nbZ1ZFcvjXLAcw4m3klUpOVigIpvHu2WbSEYTkO/8aEsoqr+FXD1PBExLu2FpnT1onvdQecOMKm/fRGCnPpyQmW65EKUrY0oaxF5iKv7YNk+HtJ9WFalBPVWfR219SIqGFrZARyN9RsX+82gcr3RyMH0PVpdu7wLGpppM1/ONmdxDDZllgF6xjgNHUKuOzeXo5NjQtyMXPyMkZmVjqLMm9urq4296P74Wd+34la9r5638S9EH8BkF0enKytPJfKf92ML7v8QWb1i8NQn5a5XmOe6HKEU4fMhhr29banbngCNYpJdJLrVixI9+y8zN4F5""",
"clar_fixtures.c" : r"""eJyFUV1LwzAUfW5+xZU9rLUVJ4ggZQ9DFAUfZEwQSglZmrBAl5Qkk6n43236tWbKfMvNOfecc+81llhBgSppLNAN0XCOuNjbnWa4InYTjpE1MSzxuD1Vki2L0BcKTKfn0EYgu57d3uRpjYhPhi1opSwumUwRCvo3zMFYXT9C5xA5stWSVh9hI5FAa+wUFG//osgJCA5tmQ1SF3CVw9kcppfTCAWBj8ZxDg3UN4/zZ7MaHBrHSBw7vpcJ4mGS5Ijtai9qnannNqk1q7myXU+KvhGaCF4wDnfPiyV+eHpbvS7v8cti9YjGq6Yl7lzCkxfo1L0j/lJOwOtrUrwrUcDBBRsii7Xan3bjBlNVL2WUzuMkgGlJdLuIP21oyYjcVf/a6G3ozXTQPRqmsZkwWQiOfgAVGffP""",
"clar_fs.c" : r"""eJylVdtu20YQfSa/YkAD8TKWY8dJX6L0wXDEVqgsBhINN7UFhiGX1qIkl9hd+dLG/57ZCynJUWEkfZE0s7NnZufMGe2xsqAlpJfj6ZsT399DgzUUojhKo8npb3Mg+ud8PBlNE/hq/NP4LJ5G49n5aTKOp71zNJvFs4vx06DzPz6MZ6HvS5UplkO+zAS89EtWUd7KtM3UkuS8kcqdGE/o/+t71tYm/ArTi8lk6HuS/UNTBRVtbtRyAGzo+x4rgaQ2zMaFvucJqlaicdd8z15AHKkE/rbxIQI6+DqrKp4TF3YAJ2GH/AxwTeu8fTBRA0jtl0Xp0K+sucAsx9suzPPauX2v5AIIMxYweO9AhnBwwELAbvTFXLGFrmf/aF+X4/Uu2L++3scEjwjmitRnQ/+x7/0tZ0XXecIaBTUv6AC22i/5SuRPnQWVynAy/z3CSYg/zpPZxVkCJQLp4m2YvYqVbJHrEHU7bJgG+y7IZNBQf1HBz2nNxQN5oeEHoDnnJdlOHYa2aa18dRetmlxziI8ZOl8bCV5ruk3u3ptw9OlUnaeMquxGorOfd/OcKs2kpEKlBFuMibHUuKUCm8gbW1aoOTge4HFwyZqC30l4EgdlhmYR+J4tVVBK1q0wpnv0U4JkKmqygxTDQEdfFKcfRpNRMsKx6zgzM7oLL+c4oz9A80aSs/jjp40U6bpmA46t0vgVzZpVS7TLApg3lOwe55A6ivMqE04hwcsgtCB7tJK0KxdH0pdLWlUpXylii3IVZuLm9mphsPXg6gsrqeXECtwH+Kl7jF96sLj4m6z1i773cGw1VLYCb5dEqoIKodnzgvmDVLQGtLl4B5/t7c+Q40ZwFL66bgLNmUfvmSKHr0Onsg5eT4LFp/c0vyWm1uPFwBTdBd9lTGGwvjCAF7b+Ad4b9mq9HP05TubJaXIxJ/b8f3DZU2lNU9Ivi+G2VNcL1dopLh3dt17IuC0LpHVDwuvA9TLtT21LrHm1EXlo9ly/s/4rwC5C1z00g6MvrDnK22DovCYoOJz1jpPFpsaN6412udkJndTNwdtF/zdiFF6vpMJxlNKIfD12hjQj7MiwD4qD7jkovbfcSEvtlVlTfOH3uxX+rKg3NL3B0dvFrh6I+rselNtN6F68oxk/+2araVBLuv3SZ6RvZL5q3BVi9r52bTgeUfZNwUr/G9kaoSs=""",
"clar.h" : r"""eJy9Vctu2zAQPEdfwVo9WIIQp9c0DWAENmLACIrUQXojaHIVEZVJlaQaAUX/vSQlP/Rw3PTgk6nlDmd2d0iHPBUMUoTx3XL6iFezbyt8j3EQ2iAX0IsHIRc0LxmgG21YzteX2W0Q/JKcIZoThTHRGpQZBxdcGESlYNxwKZLgwq61jWREoTjlOSR1Sm5ZOruglFSdGANNFS+asxxQZ7LMGSZrqUz0eacBazCY5kBEWYx9bBw3n1H9HUcJqheyID9LsOAtNtUtqDs25Knrj+/CfPF99fQ4w1+nq/vgUJ2D8sqUCsbtMn0MC7JpsTRhTQRby+o9kK26NyAh2J6nQTCJ4wDFaOrnYduGNoQqqdErNxmCqsg55Qb5XqMNaE1ewOZPdpO3rJtSG1zYieKxBagEuSlE7UH7nQjdfkFXiXXLfLGcYexWy8WDX43mpaBeACV5jlJiZ8+u0QiF+zMT9CnqEbvM08Q3R3lnVQHUAENpS4CRXsMJBTXJafoPx+u2/Mr21RFzjYQ0yKgShni3s7rLgP74jzlRhzvToK6iPvOZJzUk4QyDuopOXCoh//E6NZKGbtjD03I5fBU6oMOe90BN6TtE2811+nHTnapjb7c9Q9+CPVF7r3Rhb9biU7qIwUrmUlFnInuafQ8nr0QJLl666r2AAZ8cc8cK7EtbX4bL0fBj0TC959TnGoJYqdyPcSRQAS2dq65HA57zOjZgMsnspiMhLlf7+j7+hsqAEvhw50+w/TP4C4S1nfY="""
}
if __name__ == '__main__':
main()