Commit 394951ad4c09c7bd08c5cb1bc8bc48f67d8024b9

Patrick Steinhardt 2019-06-07T14:11:29

tests: allow for simple data-driven tests Right now, we're not able to use data-driven tests at all. E.g. given a set of tests which we'd like to repeat with different test data, one has to hand-write any outer loop that iterates over the test data and then call each of the test functions. Next to being bothersome, this also has the downside that error reporting is quite lacking, as one never knows which test data actually produced failures. So let's implement the ability to re-run complete test modules with changing test data. To retain backwards compatibility and enable trivial addition of new runs with changed test data, we simply extend clar's generate.py. Instead of scanning for a single `_initialize` function, each test module may now implement multiple `_initialize_foo` functions. The generated test harness will then run all test functions for each of the provided initializer functions, making it possible to provide different test data inside of each of the initializer functions. Example: ``` void test_git_example__initialize_with_nonbare_repo(void) { g_repo = cl_git_sandbox_init("testrepo"); } void test_git_example__initialize_with_bare_repo(void) { g_repo = cl_git_sandbox_init("testrepo.git"); } void test_git_example__cleanup(void) { cl_git_sandbox_cleanup(); } void test_git_example__test1(void) { test1(); } void test_git_example__test2(void) { test2(); } ``` Executing this test module will cause the following output: ``` $ ./libgit2_clar -sgit::example git::example (with nonbare repo).. git::example (with bare repo).. ```

diff --git a/tests/generate.py b/tests/generate.py
index 0a94d49..572a57f 100644
--- a/tests/generate.py
+++ b/tests/generate.py
@@ -24,8 +24,8 @@ class Module(object):
         def render(self):
             out = "\n".join("extern %s;" % cb['declaration'] for cb in self.module.callbacks) + "\n"
 
-            if self.module.initialize:
-                out += "extern %s;\n" % self.module.initialize['declaration']
+            for initializer in self.module.initializers:
+                out += "extern %s;\n" % initializer['declaration']
 
             if self.module.cleanup:
                 out += "extern %s;\n" % self.module.cleanup['declaration']
@@ -41,7 +41,19 @@ class Module(object):
 
     class InfoTemplate(Template):
         def render(self):
-            return Template(
+            templates = []
+
+            initializers = self.module.initializers
+            if len(initializers) == 0:
+                initializers = [ None ]
+
+            for initializer in initializers:
+                name = self.module.clean_name()
+                if initializer and initializer['short_name'].startswith('initialize_'):
+                    variant = initializer['short_name'][len('initialize_'):]
+                    name += " (%s)" % variant.replace('_', ' ')
+
+                template = Template(
             r"""
     {
         "${clean_name}",
@@ -49,14 +61,17 @@ class Module(object):
     ${cleanup},
         ${cb_ptr}, ${cb_count}, ${enabled}
     }"""
-            ).substitute(
-                clean_name = self.module.clean_name(),
-                initialize = self._render_callback(self.module.initialize),
-                cleanup = self._render_callback(self.module.cleanup),
-                cb_ptr = "_clar_cb_%s" % self.module.name,
-                cb_count = len(self.module.callbacks),
-                enabled = int(self.module.enabled)
-            )
+                ).substitute(
+                    clean_name = name,
+                    initialize = self._render_callback(initializer),
+                    cleanup = self._render_callback(self.module.cleanup),
+                    cb_ptr = "_clar_cb_%s" % self.module.name,
+                    cb_count = len(self.module.callbacks),
+                    enabled = int(self.module.enabled)
+                )
+                templates.append(template)
+
+            return ','.join(templates)
 
     def __init__(self, name):
         self.name = name
@@ -86,7 +101,7 @@ class Module(object):
         regex = re.compile(TEST_FUNC_REGEX % self.name, re.MULTILINE)
 
         self.callbacks = []
-        self.initialize = None
+        self.initializers = []
         self.cleanup = None
 
         for (declaration, symbol, short_name) in regex.findall(contents):
@@ -96,8 +111,8 @@ class Module(object):
                 "symbol" : symbol
             }
 
-            if short_name == 'initialize':
-                self.initialize = data
+            if short_name.startswith('initialize'):
+                self.initializers.append(data)
             elif short_name == 'cleanup':
                 self.cleanup = data
             else: