| #!/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() |
| |