aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLarry Lira <larry@expertisesolutions.com.br>2018-09-04 20:47:23 -0300
committerLarry Lira <larry@expertisesolutions.com.br>2018-09-05 15:52:47 -0300
commitca6383bf7371b5472491e836666eed9ac69ee001 (patch)
tree2d24d5a857d379033ab5098efcd127aab10ea3e1
parentefl_ui_image: apply some image options as retained way. (diff)
downloadefl-ca6383bf7371b5472491e836666eed9ac69ee001.tar.gz
eolian: Added Eolian Test Generator
-rw-r--r--src/scripts/pyolian/eolian_lib.py1
-rw-r--r--src/scripts/testgen/__init__.py0
-rw-r--r--src/scripts/testgen/ekeys.py217
-rw-r--r--src/scripts/testgen/suitegen.py110
-rwxr-xr-xsrc/scripts/testgen/testgen.py122
-rw-r--r--src/scripts/testgen/testgenerator.c.template179
-rw-r--r--src/scripts/testgen/testgenerator.cs.template110
7 files changed, 739 insertions, 0 deletions
diff --git a/src/scripts/pyolian/eolian_lib.py b/src/scripts/pyolian/eolian_lib.py
index 3cd7bad285..5556b2c22f 100644
--- a/src/scripts/pyolian/eolian_lib.py
+++ b/src/scripts/pyolian/eolian_lib.py
@@ -14,6 +14,7 @@ root_path = os.path.abspath(os.path.join(script_path, '..', '..', '..'))
search_in = [
os.path.join(root_path, 'src', 'lib', 'eolian', '.libs'),
os.path.join(root_path, 'build', 'src', 'lib', 'eolian'),
+ os.path.join(root_path, 'build', 'src', 'lib', 'eolian', '.libs'),
]
if 'EOLIAN_SO_DIR' in os.environ:
diff --git a/src/scripts/testgen/__init__.py b/src/scripts/testgen/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/scripts/testgen/__init__.py
diff --git a/src/scripts/testgen/ekeys.py b/src/scripts/testgen/ekeys.py
new file mode 100644
index 0000000000..20f633e159
--- /dev/null
+++ b/src/scripts/testgen/ekeys.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+
+
+class EKeys:
+ def __init__(self, ext):
+ self.ext = ext
+ self.dicttypes = {}
+ self.keywords = []
+ self.verbs = []
+ self.blacklist = []
+ self.keyloads = ["init", "shutdown", "custom"]
+
+ def type_convert(self, eotype):
+ return eotype.name
+
+ def print_arg(self, eoarg):
+ return "arg_{}".format(eoarg.name)
+
+
+class EMonoKeys(EKeys):
+ def __init__(self, ext):
+ super().__init__(ext)
+ self.dicttypes = {
+ "byte": "sbyte",
+ "llong": "long",
+ "int8": "sbyte",
+ "int16": "short",
+ "int32": "int",
+ "int64": "long",
+ "ssize": "long",
+ "ubyte": "byte",
+ "ullong": "ulong",
+ "uint8": "byte",
+ "uint16": "ushort",
+ "uint32": "uint",
+ "uint64": "ulong",
+ "size": "ulong",
+ "ptrdiff": "long",
+ "intptr": "System.IntPtr",
+ "uintptr": "System.IntPtr",
+ "void_ptr": "System.IntPtr",
+ "void": "System.IntPtr", # only if is out/inout
+ "Error": "eina.Error",
+ "string": "System.String",
+ "mstring": "System.String",
+ "stringshare": "System.String",
+ "any_value": "eina.Value",
+ "any_value_ptr": "eina.Value"
+ # complex Types
+ ,
+ "list": "eina.List",
+ "inlist": "eina.Inlist",
+ "array": "eina.Array",
+ "inarray": "eina.Inarray",
+ "hash": "eina.Hash",
+ "promise": "int",
+ "future": "int",
+ "iterator": "eina.Iterator",
+ "accessor": "int",
+ }
+
+ self.keywords = [
+ "delete",
+ "do",
+ "lock",
+ "event",
+ "in",
+ "object",
+ "interface",
+ "string",
+ "internal",
+ "fixed",
+ "base",
+ ]
+
+ self.verbs = [
+ "add",
+ "get",
+ "is",
+ "del",
+ "thaw",
+ "freeze",
+ "save",
+ "wait",
+ "eject",
+ "raise",
+ "lower",
+ "load",
+ "dup",
+ "reset",
+ "unload",
+ "close",
+ "set",
+ "interpolate",
+ "has",
+ "grab",
+ "check",
+ "find",
+ "ungrab",
+ "unset",
+ "clear",
+ "pop",
+ "new",
+ "peek",
+ "push",
+ "update",
+ "show",
+ "move",
+ "hide",
+ "calculate",
+ "resize",
+ "attach",
+ "pack",
+ "unpack",
+ "emit",
+ "call",
+ "append",
+ ]
+
+ self.blacklist = [
+ "efl_event_callback_array_priority_add",
+ "efl_player_position_get",
+ "efl_text_font_source_get",
+ "efl_text_font_source_set",
+ "efl_ui_focus_manager_focus_get",
+ "efl_ui_widget_focus_set",
+ "efl_ui_widget_focus_get",
+ "efl_ui_text_password_get",
+ "efl_ui_text_password_set",
+ "elm_interface_scrollable_repeat_events_get",
+ "elm_interface_scrollable_repeat_events_set",
+ "elm_wdg_item_del",
+ "elm_wdg_item_focus_get",
+ "elm_wdg_item_focus_set",
+ "elm_interface_scrollable_mirrored_set",
+ "evas_obj_table_mirrored_get",
+ "evas_obj_table_mirrored_set",
+ "edje_obj_load_error_get",
+ "efl_ui_focus_user_parent_get",
+ "efl_canvas_object_scale_get", # duplicated signature
+ "efl_canvas_object_scale_set", # duplicated signature
+ "efl_access_parent_get",
+ "efl_access_name_get",
+ "efl_access_name_set",
+ "efl_access_root_get",
+ "efl_access_type_get",
+ "efl_access_role_get",
+ "efl_access_action_description_get",
+ "efl_access_action_description_set",
+ "efl_access_image_description_get",
+ "efl_access_image_description_set",
+ "efl_access_component_layer_get", # duplicated signature
+ "efl_access_component_alpha_get",
+ "efl_access_component_size_get",
+ "efl_ui_spin_button_loop_get",
+ "efl_ui_list_model_size_get",
+ "efl_ui_list_relayout_layout_do",
+ ]
+
+ def escape_keyword(self, key):
+ return key in self.keywords and "kw_{}".format(key) or key
+
+ def direction_get(self, name):
+ if name == "INOUT":
+ return "ref "
+ elif name == "OUT":
+ return "out "
+ return None
+
+ def klass_name(self, eo_name):
+ names = eo_name.split(".")
+ namespaces = [self.escape_keyword(x.lower()) for x in names[:-1]]
+ k_name = names[-1]
+ return ".".join(namespaces + [k_name])
+
+ def type_convert(self, eotype):
+ if eotype.type == eotype.type.VOID:
+ return "System.IntPtr"
+
+ new_type = self.dicttypes.get(eotype.name, self.klass_name(eotype.name))
+ if new_type != "int" and eotype.base_type:
+ new_type = "{}<{}>".format(
+ new_type,
+ self.dicttypes.get(
+ eotype.base_type.name, self.klass_name(eotype.base_type.name)
+ ),
+ )
+
+ return new_type
+
+ def print_arg(self, eoarg):
+ r = super().print_arg(eoarg)
+ prefix = self.direction_get(eoarg.direction.name) or None
+
+ if prefix == "out" and (
+ eoarg.type.name == "Eina.Slice" or eoarg.type.name == "Eina.Rw_Slice"
+ ):
+ prefix = "ref"
+
+ if (
+ not prefix
+ and eoarg.type.is_ptr
+ and eoarg.type.type == eoarg.type.type.REGULAR
+ and eoarg.type.typedecl
+ and eoarg.type.typedecl.type == eoarg.type.typedecl.type.STRUCT
+ ):
+ prefix = "ref"
+
+ return prefix and " ".join([prefix, r]) or r
+
+
+def GetKey(ext):
+ if ext == ".cs":
+ return EMonoKeys(ext)
+ else:
+ return EKeys(ext)
diff --git a/src/scripts/testgen/suitegen.py b/src/scripts/testgen/suitegen.py
new file mode 100644
index 0000000000..7762e74c4f
--- /dev/null
+++ b/src/scripts/testgen/suitegen.py
@@ -0,0 +1,110 @@
+import itertools
+import os
+from testgen.ekeys import GetKey
+
+
+class BaseItem:
+ def __init__(self, path, keys, prefix=""):
+ self.path = path
+ self.keys = keys
+ self.prefix = prefix
+
+ def __getattr__(self, attr):
+ if not attr.split("_")[-1] in self.keys.keyloads:
+ raise AttributeError
+
+ filename = os.path.join(self.path, self.prefix + attr) + self.keys.ext
+ if os.path.isfile(filename):
+ with open(filename, "r") as f:
+ return f.read()
+ return None
+
+
+class ComItem(BaseItem):
+ def __init__(self, comp, path, keys):
+ super().__init__(path, keys)
+ self.comp = comp
+
+ def __getattr__(self, attr):
+ if hasattr(self.comp, attr):
+ return getattr(self.comp, attr)
+ return super().__getattr__(attr)
+
+
+class FuncItem(ComItem):
+ def __init__(self, comp, path, keys):
+ print(comp.name)
+ super().__init__(comp, os.path.join(path, comp.name), keys)
+
+ @property
+ def format_name(self):
+ names = self.comp.name.split("_")
+ if names[-1] in self.keys.verbs:
+ names.insert(0, names.pop())
+ return "".join([name.capitalize() for name in names])
+
+
+class ClassItem(ComItem):
+ def __init__(self, comp, path, keys):
+ self.myname = os.path.splitext(comp.file)[0]
+ super().__init__(comp, os.path.join(path, self.myname), keys)
+
+ self.methods = [
+ FuncItem(m, self.path, keys)
+ for m in self.comp.methods
+ if m.full_c_method_name not in self.keys.blacklist
+ ]
+
+ self.properties = [
+ FuncItem(p, self.path, keys)
+ for p in self.comp.properties
+ if (
+ p.getter_scope == p.getter_scope.PUBLIC
+ and p.full_c_getter_name not in self.keys.blacklist
+ )
+ or (
+ p.setter_scope == p.setter_scope.PUBLIC
+ and p.full_c_setter_name not in self.keys.blacklist
+ )
+ ]
+
+ def __iter__(self):
+ return itertools.chain(self.methods, self.properties)
+
+
+class SuiteGen(BaseItem):
+ def __init__(self, name, testname, filename, path, template=None):
+ keys = GetKey(os.path.splitext(filename)[1])
+ super().__init__(path, keys, name + "_")
+ self.name = name
+ self.testname = testname
+ self.fullname = "{}_{}".format(name, testname)
+ self.filename = filename
+ self.template = template
+ self.clslist = []
+
+ if not self.template:
+ script_path = os.path.dirname(os.path.realpath(__file__))
+ self.template = os.path.join(
+ script_path, "testgenerator{}.template".format(self.keys.ext)
+ )
+
+ def __iter__(self):
+ return iter(self.clslist)
+
+ def type_convert(self, eotype):
+ return self.keys.type_convert(eotype)
+
+ def print_arg(self, eoarg):
+ return self.keys.print_arg(eoarg)
+
+ def load(self, eolian_db, eofiles):
+ self.clslist.clear()
+ for eofile in eofiles:
+ eocls = eolian_db.class_by_file_get(os.path.basename(eofile))
+ if not eocls or eocls.type != eocls.type.REGULAR:
+ continue
+
+ cls = ClassItem(eocls, self.path, self.keys)
+ cls.myfullname = "{}_{}".format(self.fullname, cls.myname)
+ self.clslist.append(cls)
diff --git a/src/scripts/testgen/testgen.py b/src/scripts/testgen/testgen.py
new file mode 100755
index 0000000000..007342bc99
--- /dev/null
+++ b/src/scripts/testgen/testgen.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+
+import os
+import sys
+import datetime
+from copy import copy
+
+script_path = os.path.dirname(os.path.realpath(__file__))
+
+if "EFL_DIR" in os.environ:
+ root_path = os.environ["EFL_DIR"]
+else:
+ root_path = os.path.abspath(os.path.join(script_path, "..", "..", ".."))
+
+sys.path.insert(0, os.path.join(root_path, "src", "scripts"))
+
+from pyolian import eolian
+from pyolian import pyratemp
+from testgen.suitegen import SuiteGen
+
+# Use .eo files from the source tree (not the installed ones)
+SCAN_FOLDER = os.path.join(root_path, "src", "lib")
+
+# create main eolian state
+eolian_db = eolian.Eolian_State()
+if not isinstance(eolian_db, eolian.Eolian_State):
+ raise (RuntimeError("Eolian, failed to create Eolian state"))
+
+# eolian source tree scan
+if not eolian_db.directory_add(SCAN_FOLDER):
+ raise (RuntimeError("Eolian, failed to scan source directory"))
+
+# Parse all known eo files
+if not eolian_db.all_eot_files_parse():
+ raise (RuntimeError("Eolian, failed to parse all EOT files"))
+
+if not eolian_db.all_eo_files_parse():
+ raise (RuntimeError("Eolian, failed to parse all EO files"))
+
+# cleanup the database on exit
+import atexit
+
+
+def cleanup_db():
+ global eolian_db
+ del eolian_db
+
+
+atexit.register(cleanup_db)
+
+"""
+It will find methods and functions with owned return and without other params
+"""
+
+
+class Template(pyratemp.Template):
+ def __init__(
+ self,
+ filename,
+ encoding="utf-8",
+ loader_class=pyratemp.LoaderFile,
+ parser_class=pyratemp.Parser,
+ renderer_class=pyratemp.Renderer,
+ eval_class=pyratemp.EvalPseudoSandbox,
+ ):
+
+ global_ctx = {}
+ global_ctx.update(
+ {
+ # Template info
+ "date": datetime.datetime.now(),
+ "template_file": os.path.basename(filename),
+ }
+ )
+
+ self.template_filename = filename
+ pyratemp.Template.__init__(
+ self,
+ filename=filename,
+ encoding=encoding,
+ data=global_ctx,
+ loader_class=loader_class,
+ parser_class=parser_class,
+ renderer_class=renderer_class,
+ eval_class=eval_class,
+ )
+
+ def render(self, suite, verbose=True):
+ # Build the context for the template
+ ctx = {}
+ ctx["suite"] = suite
+ # render with the augmented context
+ output = self(**ctx)
+
+ if suite.filename is not None:
+ # write to file
+ with open(suite.filename, "w") as f:
+ f.write(output)
+
+
+if __name__ == "__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser(description="Pyolian search owned functions.")
+ parser.add_argument("testname", help="The Test Name to use. (REQUIRED)")
+ parser.add_argument("suitename", help="The Suite Name to use. (REQUIRED)")
+ parser.add_argument("filename", help="Build file dest. (REQUIRED)")
+ parser.add_argument("eofiles", nargs="*", help="The Eolian File to use.")
+
+ args = parser.parse_args()
+
+ testdir = os.path.join(root_path, "src", "tests", args.testname)
+
+ suite = SuiteGen(args.suitename, args.testname, args.filename, testdir)
+ suite.load(eolian_db, args.eofiles)
+
+ t = Template(suite.template)
+ # try:
+ t.render(suite)
+# except:
+# print("ERROR RENDERING - Cannot create file: {}".format(suite.filename))
diff --git a/src/scripts/testgen/testgenerator.c.template b/src/scripts/testgen/testgenerator.c.template
new file mode 100644
index 0000000000..accf09fe7b
--- /dev/null
+++ b/src/scripts/testgen/testgenerator.c.template
@@ -0,0 +1,179 @@
+<!--(macro m_show)-->
+ <!--(if mshow)-->
+${mshow}$#!
+ <!--(end)-->
+<!--(end)-->
+<!--(macro init)-->
+ Eo *parent = NULL;
+ Eo *obj = NULL;
+
+ <!--(if exists("mfunc") and mfunc!= None)-->
+${mfunc}$
+ <!--(elif exists("mcls") and mcls!= None)-->
+${mcls}$
+ <!--(else)-->
+ obj = efl_add_ref(${cls.c_name}$, parent);
+ fail_if(!obj, "ERROR: Cannot init ${cls.name}$!\n");
+ <!--(end)-->
+<!--(end)-->
+<!--(macro shutdown)-->
+ /** shutdown **/
+ <!--(if exists("mfunc") and mfunc != None)-->
+${mfunc}$
+ <!--(elif exists("mcls") and mcls != None)-->
+${mcls}$
+ <!--(end)-->
+ efl_unref(obj);
+<!--(end)-->
+<!--(macro arg_default)-->
+ <!--(if arg.type.name == "__builtin_free_cb" or arg.type.is_ptr or arg.type.type == arg.type.type.CLASS or arg.type.builtin_type == arg.type.builtin_type.STRING)-->NULL<!--(elif arg.type.builtin_type == arg.type.builtin_type.ANY_VALUE)-->EINA_VALUE_EMPTY<!--(elif arg.type.name in ['Eina.Slice', 'Eina.Rw_Slice', 'Eina.Position2D', 'Eina.Rect', 'Eina.Vector2'] )-->{}<!--(else)-->0<!--(end)-->;
+<!--(end)-->
+<!--(macro args_declaration)-->
+ <!--(for arg in args)-->
+ <!--(if arg.type.typedecl and arg.type.typedecl.type == arg.type.typedecl.type.FUNCTION_POINTER)-->
+ void * arg_${arg.name}$_data = NULL;
+ ${arg.type.c_type_return}$ arg_${arg.name}$ = NULL;
+ Eina_Free_Cb arg_${arg.name}$_free_cb = NULL;
+ <!--(else)-->
+ ${arg.type.c_type_return}$ arg_${arg.name}$ = ${arg_default(arg=arg)}$
+ <!--(end)-->
+ <!--(end)-->
+<!--(end)-->
+<!--(macro print_arg)-->
+ <!--(if arg.type.typedecl and arg.type.typedecl.type == arg.type.typedecl.type.FUNCTION_POINTER)-->
+arg_${arg.name}$_data, arg_${arg.name}$, arg_${arg.name}$_free_cb
+ <!--(else)-->
+ <!--(if arg.direction == arg.direction.OUT)-->&<!--(end)-->arg_${arg.name}$
+ <!--(end)-->
+<!--(end)-->
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <check.h>
+#include "efl_check.h"
+${m_show(mshow=suite.custom)}$#!
+#include <Elementary.h>
+
+<!--(for cls in suite)-->
+void ${cls.myfullname}$_test(TCase *tc);
+<!--(end)-->
+
+static const Efl_Test_Case etc[] = {
+<!--(for cls in suite)-->
+ { "${suite.name.capitalize()}$ ${suite.testname.capitalize()}$ ${cls.myname.capitalize()}$", ${cls.myfullname}$_test },
+<!--(end)-->
+ { NULL, NULL }
+};
+
+<!--(for cls in suite)-->
+
+/**************** TEST CASE ${cls.c_name}$ ****************/
+${m_show(mshow=cls.custom)}$#!
+
+START_TEST(${cls.myfullname}$_smoke)
+{
+${init(mcls=cls.init)}$
+${shutdown(mcls=cls.shutdown)}$
+}
+END_TEST
+
+ <!--(for func in cls.methods)-->
+START_TEST(${cls.myfullname}$_${func.full_c_method_name}$)
+{
+${args_declaration(args=func.parameters)}$${init(mcls=cls.init,mfunc=func.init)}$
+${m_show(mshow=func.arg_init)}$#!
+ <!--(if func.method_return_type)-->${func.method_return_type.c_type_return}$ r = <!--(end)-->${func.full_c_method_name}$(obj<!--(for arg in func.parameters)-->,${print_arg(arg=arg)}$<!--(end)-->);
+ <!--(if func.method_return_type)-->(void)r;<!--(end)-->
+${m_show(mshow=func.arg_shutdown)}$#!
+${shutdown(mcls=cls.shutdown,mfunc=func.shutdown)}$
+}
+END_TEST
+
+ <!--(end)-->
+
+ <!--(for func in cls.properties)-->
+ <!--(if func.getter_scope == func.getter_scope.PUBLIC)-->
+START_TEST(${cls.myfullname}$_${func.full_c_getter_name}$)
+{
+ <!--(if len(list(func.getter_values)) > 1)-->
+${args_declaration(args=func.getter_values)}$
+ <!--(end)-->
+${args_declaration(args=func.getter_keys)}$${init(mcls=cls.init,mfunc=func.get_init)}$
+${m_show(mshow=func.arg_get_init)}$#!
+ <!--(if len(list(func.getter_values)) == 1)-->
+ ${list(func.getter_values)[0].type.c_type_return}$ r = ${func.full_c_getter_name}$(obj<!--(for arg in func.getter_keys)-->, arg_${arg.name}$<!--(end)-->);
+ (void)r;
+ <!--(else)-->
+ ${func.full_c_getter_name}$(obj<!--(for arg in func.getter_values)-->, &arg_${arg.name}$<!--(end)--><!--(for arg in func.getter_keys)-->, arg_${arg.name}$<!--(end)-->);
+ <!--(end)-->
+${m_show(mshow=func.arg_get_shutdown)}$#!
+${shutdown(mcustom=cls.shutdown,mfunc=func.get_shutdown)}$
+}
+END_TEST
+
+ <!--(end)-->
+ <!--(if func.setter_scope == func.setter_scope.PUBLIC)-->
+START_TEST(${cls.myfullname}$_${func.full_c_setter_name}$)
+{
+${args_declaration(args=func.setter_keys)}$${args_declaration(args=func.setter_values)}$${init(mcls=cls.init,mfunc=func.set_init)}$
+${m_show(mshow=func.arg_set_init)}$#!
+ ${func.full_c_setter_name}$(obj<!--(for arg in func.setter_keys)-->, arg_${arg.name}$<!--(end)--><!--(for arg in func.setter_values)-->, arg_${arg.name}$<!--(end)-->);
+${m_show(mshow=func.arg_set_shutdown)}$#!
+${shutdown(mcls=cls.shutdown,mfunc=func.set_shutdown)}$
+}
+END_TEST
+
+ <!--(end)-->
+ <!--(end)-->
+void ${cls.myfullname}$_test(TCase *tc)
+{
+ tcase_add_test(tc, ${cls.myfullname}$_smoke);
+ <!--(for func in cls.methods)-->
+ tcase_add_test(tc, ${cls.myfullname}$_${func.full_c_method_name}$);
+ <!--(end)-->
+ <!--(for func in cls.properties)-->
+ <!--(if func.getter_scope == func.getter_scope.PUBLIC)-->
+ tcase_add_test(tc, ${cls.myfullname}$_${func.full_c_getter_name}$);
+ <!--(end)-->
+ <!--(if func.setter_scope == func.setter_scope.PUBLIC)-->
+ tcase_add_test(tc, ${cls.myfullname}$_${func.full_c_setter_name}$);
+ <!--(end)-->
+ <!--(end)-->
+}
+<!--(end)-->
+
+SUITE_INIT(${suite.name}$)
+{
+ fail_if(!eina_init(), "ERROR: Cannot init Eina!\n");
+ fail_if(!ecore_init(), "ERROR: Cannot init Ecore!\n");
+ fail_if(!efl_object_init(), "ERROR: Cannot init EO!\n");
+${m_show(mshow=suite.init)}$#!
+}
+
+SUITE_SHUTDOWN(${suite.name}$)
+{
+${m_show(mshow=suite.shutdown)}$#!
+ ecore_shutdown();
+ eina_shutdown();
+}
+
+int
+main(int argc, char **argv)
+{
+ int failed_count;
+
+ if (!_efl_test_option_disp(argc, argv, etc))
+ return 0;
+
+#ifdef NEED_RUN_IN_TREE
+ putenv("EFL_RUN_IN_TREE=1");
+#endif
+
+ failed_count = _efl_suite_build_and_run(argc - 1, (const char **)argv + 1,
+ "${suite.fullname}$", etc, SUITE_INIT_FN(${suite.name}$), SUITE_SHUTDOWN_FN(${suite.name}$));
+
+ return (failed_count == 0) ? 0 : 255;
+}
+
diff --git a/src/scripts/testgen/testgenerator.cs.template b/src/scripts/testgen/testgenerator.cs.template
new file mode 100644
index 0000000000..e54f09c257
--- /dev/null
+++ b/src/scripts/testgen/testgenerator.cs.template
@@ -0,0 +1,110 @@
+<!--(macro m_show)-->
+ <!--(if mshow)-->
+${mshow}$#!
+ <!--(end)-->
+<!--(end)-->
+<!--(macro def_obj)-->${'.'.join(param.type.namespaces).lower()}$.${param.type.short_name}$ arg_${param.name}$ = null;<!--(end)-->
+<!--(macro def_param)-->
+ <!--(if param.type.type == param.type.type.CLASS)-->${def_obj(param=param)}$<!--(else)-->${suite.type_convert(param.type)}$ arg_${param.name}$ = default(${suite.type_convert(param.type)}$);<!--(end)-->
+<!--(end)-->
+<!--(macro def_params)-->
+ <!--(for p in parameters)-->
+ ${def_param(param=p)}$
+ <!--(end)-->
+<!--(end)-->
+using System;
+
+namespace TestSuite
+{
+<!--(if suite.custom)-->${suite.custom}$<!--(end)-->
+<!--(for cls in suite)-->
+/**************** TEST CASE ${cls.c_name}$ ****************/
+class Test${cls.name.replace('.','')}$
+{
+
+ ${'.'.join(cls.namespaces).lower()}$.${cls.short_name}$ obj;
+${m_show(mshow=cls.custom)}$#!
+
+ public void SetUp()
+ {
+${m_show(mshow=suite.init)}$#!
+ <!--(if cls.init)-->
+${cls.init}$
+ <!--(else)-->
+ obj = new ${'.'.join(cls.namespaces).lower()}$.${cls.short_name}$Concrete();
+ <!--(end)-->
+ }
+
+ public void TearDown()
+ {
+${m_show(mshow=suite.shutdown)}$#!
+ <!--(if cls.shutdown)-->
+${cls.shutdown}$
+ <!--(else)-->
+ obj.Dispose();
+ obj = null;
+ <!--(end)-->
+ }
+
+ public void smoke()
+ {
+ }
+
+#! METHODS
+ <!--(for func in cls.methods)-->
+ public void ${func.name}$()
+ {
+ <!--(if len(list(func.parameters)) > 0)-->
+${def_params(parameters=func.parameters)}$
+ <!--(end)-->
+${m_show(mshow=func.arg_init)}$#!
+${m_show(mshow=func.init)}$#!
+ <!--(if func.method_return_type)-->var r = <!--(end)-->obj.${func.format_name}$(${', '.join([ suite.print_arg(p) for p in func.parameters])}$);
+${m_show(mshow=func.arg_shutdown)}$#!
+${m_show(mshow=func.shutdown)}$#!
+ }
+
+ <!--(end)-->
+#! PROPERTIES FUNCTION GET
+ <!--(for func in cls.properties)-->
+ <!--(if func.getter_scope == func.getter_scope.PUBLIC)-->
+ public void ${func.name}$_pget()
+ {
+ <!--(if len(list(func.getter_values)) > 1)-->
+${def_params(parameters=func.getter_values)}$
+ <!--(end)-->
+ <!--(if len(list(func.getter_keys)) > 0)-->
+${def_params(parameters=func.getter_keys)}$
+ <!--(end)-->
+${m_show(mshow=func.arg_get_init)}$#!
+${m_show(mshow=func.get_init)}$#!
+ <!--(if len(list(func.getter_values)) == 1)-->
+ var arg_${list(func.getter_values)[0].name}$ = obj.Get${func.format_name}$(${', '.join(['arg_{}'.format(param.name) for param in func.getter_keys])}$);
+ <!--(else)-->
+ obj.Get${func.format_name}$(${', '.join([suite.print_arg(p) for p in func.getter_keys] + ['out arg_{}'.format(p.name) for p in func.getter_values])}$);
+ <!--(end)-->
+${m_show(mshow=func.arg_get_shutdown)}$#!
+${m_show(mshow=func.get_shutdown)}$#!
+ }
+
+ <!--(end)-->
+#! PROPERTIES FUNCTION SET
+ <!--(if func.setter_scope == func.setter_scope.PUBLIC)-->
+ public void ${func.name}$_pset()
+ {
+${def_params(parameters=func.setter_values)}$
+ <!--(if len(list(func.setter_keys)) > 0)-->
+${def_params(parameters=func.setter_keys)}$
+ <!--(end)-->
+${m_show(mshow=func.arg_set_init)}$#!
+${m_show(mshow=func.set_init)}$#!
+ obj.Set${func.format_name}$(${', '.join([suite.print_arg(p) for p in list(func.setter_keys) + list(func.setter_values)])}$);
+${m_show(mshow=func.arg_set_shutdown)}$#!
+${m_show(mshow=func.set_shutdown)}$#!
+ }
+
+ <!--(end)-->
+ <!--(end)-->
+}
+<!--(end)-->
+}