blob: cd3d42d21aee94d46f2fa9be84d7d2aac2440c9f [file] [log] [blame]
#!/usr/bin/env python
from __future__ import with_statement
from string import Template
import re, fnmatch, os
VERSION = "0.7.0"
TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\(\s*(void)?\s*\))\s*\{"
TEMPLATE_MAIN = Template(
r"""
/*
* Clay v${version}
*
* This is an autogenerated file. Do not modify.
* To add new unit tests or suites, regenerate the whole
* file with `./clay`
*/
#define clay_print(...) ${clay_print}
${clay_library}
${extern_declarations}
static const struct clay_func _all_callbacks[] = {
${test_callbacks}
};
static const struct clay_suite _all_suites[] = {
${test_suites}
};
static const char _suites_str[] = "${suites_str}";
int main(int argc, char *argv[])
{
return clay_test(
argc, argv, _suites_str,
_all_callbacks, ${cb_count},
_all_suites, ${suite_count}
);
}
""")
TEMPLATE_SUITE = Template(
r"""
{
"${clean_name}",
${initialize},
${cleanup},
${cb_ptr}, ${cb_count}
}
""")
def main():
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-c', '--clay-path', dest='clay_path')
parser.add_option('-o', '--output', dest='output')
parser.add_option('-v', '--report-to', dest='print_mode', default='stdout')
options, args = parser.parse_args()
for folder in args:
builder = ClayTestBuilder(folder,
clay_path = options.clay_path,
output = options.output,
print_mode = options.print_mode)
builder.render()
class ClayTestBuilder:
def __init__(self, folder_name, output = None, clay_path = None, print_mode = 'stdout'):
self.declarations = []
self.callbacks = []
self.suites = []
self.suite_list = []
self.clay_path = clay_path
self.print_mode = print_mode
self.output = output or os.path.join(folder_name, "clay_main.c")
self.output_header = os.path.join(folder_name, "clay.h")
self.modules = ["clay.c", "clay_sandbox.c"]
print("Loading test suites...")
file_list = os.listdir(folder_name)
for fname in fnmatch.filter(file_list, "*.c"):
with open(fname) as f:
tcount = self._process_test_file(fname, f.read())
if tcount:
print (" %s: %d tests" % (fname, tcount))
def render(self):
template = TEMPLATE_MAIN.substitute(
version = VERSION,
clay_print = self._get_print_method(),
clay_library = self._get_library(),
extern_declarations = "\n".join(self.declarations),
suites_str = ", ".join(self.suite_list),
test_callbacks = ",\n\t".join(self.callbacks),
cb_count = len(self.callbacks),
test_suites = ",\n\t".join(self.suites),
suite_count = len(self.suites),
)
with open(self.output, "w") as out:
out.write(template)
with open(self.output_header, "w") as out:
out.write(self._load_file('clay.h'))
print ('Written test suite to "%s"' % self.output)
print ('Written header to "%s"' % self.output_header)
#####################################################
# Internal methods
#####################################################
def _get_print_method(self):
return {
'stdout' : 'printf(__VA_ARGS__)',
'stderr' : 'fprintf(stderr, __VA_ARGS__)',
'silent' : ''
}[self.print_mode]
def _load_file(self, filename):
if self.clay_path:
filename = os.path.join(self.clay_path, filename)
with open(filename) as cfile:
return cfile.read()
else:
import zlib, base64, sys
content = CLAY_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_library(self):
return "\n".join(self._load_file(f) for f in self.modules)
def _parse_comment(self, comment):
comment = comment[2:-2]
comment = comment.splitlines()
comment = [line.strip() for line in comment]
comment = "\n".join(comment)
return comment
def _process_test_file(self, file_name, contents):
file_name = os.path.basename(file_name)
file_name, _ = os.path.splitext(file_name)
regex_string = TEST_FUNC_REGEX % file_name
regex = re.compile(regex_string, re.MULTILINE)
callbacks = []
initialize = cleanup = "{NULL, NULL, 0}"
for (declaration, symbol, short_name, _) in regex.findall(contents):
self.declarations.append("extern %s;" % declaration)
func_ptr = '{"%s", &%s, %d}' % (
short_name, symbol, len(self.suites)
)
if short_name == 'initialize':
initialize = func_ptr
elif short_name == 'cleanup':
cleanup = func_ptr
else:
callbacks.append(func_ptr)
if not callbacks:
return 0
suite = TEMPLATE_SUITE.substitute(
clean_name = file_name,
initialize = initialize,
cleanup = cleanup,
cb_ptr = "&_all_callbacks[%d]" % len(self.callbacks),
cb_count = len(callbacks)
).strip()
self.callbacks += callbacks
self.suites.append(suite)
self.suite_list.append(file_name)
return len(callbacks)
CLAY_FILES = {
"clay.c" : r"""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""",
"clay_sandbox.c" : r"""eJx1VG1P2zAQ/uz8iiPVSrKGETa0acr6YRJlqii0akFMopUVEgesJU6J3Wps8N93jkNeCquiJvE9d/c895IeT2KWAL0eX3z6aPUIvnDBYDb5fnk6nZ/TxWgG+8vlvtVjqWT/Axxqu4h5YllShYpHEN2HBdAoDR/pOlT3N8f+18+roDZzoSwu6TZMeUxVti5BTpQLqYzre33gWn8tIlWxiRRoR/wLLFIwtSkEOKk+cjTOg75ULgyH4EO/bxHiLOh4cTKeO1J9kIpmecxceHpCizZMLs7aBtf4hFHEpKziXdPpmQnoBtZzhzbKb1Muyd5ukoQVHkj+h1EFKRN3NXtTjlJZZd6GBY3yjVAwhONgB2MCMrHF4hTyZoUYDEPsy/MZCrI90E/lbWTuV4vRfDafno4nI9sC8oxFBlJl4vjS63SY8AScH0xdsmw90wKck+vp/MQ1lD0wSlwXU1Z1Pgrq3pIkL8DhSMkPgMO3RkkAgwF3S6Y7MhB8xxQ+OLUmvsKikpLJHh7qXNpLcbFhSNhYXk+HhpoUeiZEtH50XuqOJu+l6jp0izshzxZe9dj4O/2EKGWhoEmexqx4ewJbh7mkURbrruzWldhFDIcSDh9gab+TS9sOqp3RpgwOiqRtqApaBo3yLAtF/LIjRIp1gcwSpzKYucrrd9ereHhQcmx2Qj5KxbIa15a6zXkM5T5uhETjbf7b0WdGYbsGzdbuzr7xf+3d1Ed7URXytBxcu8TrBv4sf7YWV6+IbnU5A92NatLXuluM2pPp6wgYCDPhQOBDl7qJ3voGaegBHK1gb9j5fpmh6gIHAy2gjQrMGGEmPXkNGgZajNdIr1Nnv7AZ6zYp/UW5uJpMdkUYdMyLjnr/i++7mqz/Fj667+LfRjaLgOT/ATlX1I8=""",
"clay.h" : r"""eJy9lF9PwjAUxZ/Zp7huLxtZCL4ikhgDkYT4IonxqSntnTTWdvaP4re33TA6HCG+8LTb2/b+zjnZlolKcayAkNvVzRNZzx/W5I6QJAtNofBPP8mEYtJzhKl1XIrNaDtLknctODBJPwmh1qJxeTIQygHTigsntCqTQaht6GypgWElJJbtERkoB7tojDYHPY6WGVHvZ8WLdqu95IRutHHF1W8NFh1hEqnydd508+F+WbTrYVFCW+iavnmM178NNxNevXWkDlZy3NWmhEgvugabnQJm1zAuQ0qL5WpOSKxWy/umShdesagXGJUSKho88wmkkP3MLOGy6CHHsyfJ06Pg+a5G5pBD1VHgdCPipIQ95hT/4rjzIMCgtZEsLCjtwBmPfeAumW2RvZwn93HRhz5v8L0azpD7+DD3xnYP8RhnjeG7bIMdpcdeow9qlFDP/5n72F4B7k18uIjBHTIfs5ykHf0Y/ixV8gUUh4yr"""
}
if __name__ == '__main__':
main()