Compare commits

...

4 Commits

Author SHA1 Message Date
Taehyub Kim 8ca4dd068c testgen: sync the latest eolian grammar
Summary: sync the latest eolian grammar

Reviewers: lauromoura, woohyun, Jaehyun_Cho

Subscribers: felipealmeida, cedric, #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D10386
2019-10-16 00:46:59 -03:00
Larry Lira 98d7ee312b eolian: Added Eolian test generator
The test generator allows templates to be used to generate boilerplate
for unit tests

Some automated tests were giving different results regarding @cref
Slices
2019-10-16 00:46:59 -03:00
Lauro Moura 0a9441da15 pyolian: Add a method to get the hierarchy of extensions
Summary:
To be used by the test generator

Depends on D10420

Test Plan: test in the diff

Reviewers: DaveMDS, herb, segfaultxavi

Subscribers: cedric, brunobelo, #reviewers, felipealmeida, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D10421
2019-10-16 00:46:56 -03:00
Lauro Moura 1e7ec9dd16 pyolian: full_inherit must recurse parent too
Test Plan: run tests

Reviewers: segfaultxavi, herb, DaveMDS

Subscribers: cedric, #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D10420
2019-10-16 00:28:55 -03:00
45 changed files with 1405 additions and 0 deletions

View File

@ -732,6 +732,23 @@ class Class(Object):
def extensions(self):
return Iterator(Class, lib.eolian_class_extensions_get(self))
@cached_property
def extensions_hierarchy(self):
visited = set()
queue = [ext for ext in self.extensions]
while queue:
current = queue.pop()
if current in visited:
continue
visited.add(current)
queue.extend(current.extensions)
return visited
@cached_property
def inherits_full(self):
li = []
@ -739,6 +756,7 @@ class Class(Object):
def do_class_recursive(cls):
if cls.parent:
li.append(cls.parent)
do_class_recursive(cls.parent)
for other in cls.extensions:
if other not in li:
li.append(other)

View File

@ -846,6 +846,8 @@ class EvalPseudoSandbox:
"complex" : builtins.complex,
"dict" : builtins.dict,
"enumerate" : builtins.enumerate,
"filter" : builtins.filter,
"next" : builtins.next,
"float" : builtins.float,
"int" : builtins.int,
"list" : builtins.list,

View File

@ -699,3 +699,16 @@ class TestEolianExpression(object):
# exp.binary_operator # TODO find a better test (only works for BINARY expr)
# exp.binary_lhs # TODO find a better test (only works for BINARY expr)
# exp.binary_rhs # TODO find a better test (only works for BINARY expr)
class TestEolianInherits(object):
def test_inherits_full(self, eolian_db):
cls = eolian_db.class_by_name_get('Efl.Ui.Widget')
assert 'Efl.Object' in cls.inherits_full
def test_extensions_hierarchy(self, eolian_db):
cls = eolian_db.class_by_name_get('Efl.Ui.Widget')
# inherited extension
assert any(x.name == 'Efl.Gfx.Stack' for x in cls.extensions_hierarchy)
# direct extension
assert any(x.name == 'Efl.Access.Object' for x in cls.extensions_hierarchy)

View File

@ -0,0 +1,146 @@
Testgen: Template-based Eolian tests generator
===============================================================================
Testgen is a Python Script using the Pyolian to generate tests rendering
templates with custom files, this can be a easy way to expand the
API test coveraged.
Testgen can generate tests C and to other bingings languages only
adding new language specialized templates.
Installation
============
There is nothing to install to use the generator, everything is included in
the efl source tree and it is intended to work directly inside the tree,
usually at efl tests compilation time (make check).
The only requirement is that **the source tree must be already built** (not
installed) because pyolian search the eolian .so/.dll inside the source tree.
If you built the efl tree in a custom location (fe, you build out-of-tree) you
can tell pyolian where to find the built eolian .so files using the
`EOLIAN_SO_DIR` environment variable.
Command line usage
==================
The simplest way to use the generator is from the command line, using the
`src/scripts/testgen/testgen.py` command, the `--help` option state:
```
usage: testgen.py [-h] testname suitename filename [eofiles [eofiles ...]]
Eolian Test Generator.
positional arguments:
testname The Test Name used to find custom and template files. (REQUIRED)
suitename The Suite Name used to find custom files. (REQUIRED)
filename Generated test file destination. (REQUIRED)
eofiles The Eolian Files to use.
optional arguments:
-h, --help show this help message and exit
```
Use .c extension in <filename> to generate C tests or .cs to CSharp
To test this generator in `src/scripts/testgen` you can run:
```
./testgen.py automated efl efl_automated_test.c efl_loop.eo
```
This will rendere the automated tests using files in `src/tests/automated` with
suite name `efl_automated` and with Efl.Loop Class as Test Case
`efl_automated_efl_loop_test`
or run:
```
./testgen.py automated eio eio_automated_test.c eio_sentry.eo eio_model.eo
```
This will rendere with suite name `eio_automated` and with Eio.Sentry and
Eio.Model Class as Test Cases `eio_automated_eio_sentry_test` and
`eio_automated_eio_model_test`
How customise a Generated Test
==============================
Testgen use the filesystem to find custom files if you need customise a test,
add/write follow files in src/tests:
Suite custom files
* `src/test/<testname>/`
|-> <suitename>_custom.c #add include files, functions or structs
|-> <suitename>_init.c #add code in SUITE_INIT
|-> <suitename>_shutdown.c #add code in SUITE_SHUTDOWN
Class Test case custom files
* `src/test/<testname>/<class_name>/` #use lowercase and `_` separator
|-> custom.c #add include files, functions or structs
|-> init.c #add default way to create the object of this class
|-> shutdown.c #add default way to free the object
Funtions Tests
- Tests methodes custom files
* `src/test/<testname>/<class_name>/<method_name>`
|-> arg_init.c #initialize method arguments (arg_<argument_name>)
|-> init.c #add how to create the object (replace default)
|-> arg_shutdown.c #free arguments
|-> shutdown.c #add how to free the object (replace default)
- Tests properties custom files
* `src/test/<testname>/<class_name>/<property_name>`
| -- Property Get --
|-> arg_get_init.c #initialize property get arguments (arg_<argument_name>)
|-> get_init.c #how to create the object (replace default)
|-> arg_get_shutdown.c #free arguments
|-> get_shutdown.c #how to free the object (replace default)
| -- Property Set --
|-> arg_set_init.c #initialize propety set arguments (arg_<argument_name>)
|-> set_init.c #how to create the object (replace default)
|-> arg_set_shutdown.c #free arguments
|-> set_shutdown.c #how to free the object (replace default)
Event Tests
- Tests Events custom files
* `src/test/<testname>/<class_name>/<event_name>/`
|-> init.cs #add how to initialize the objects
|-> custom.cs #add customizations in callback
|-> shutdown.cs #add shutdown or any method to call the event
to make some custom files you only need a code using:
`parent` -> default name of parent object defined as `Eo *`
`obj` -> default name of current object
`arg_<name>` -> replace <name> with functions arguments name
you can use custom.c (suite or class) to add specilized code, structs and callbacks
-- Use `*.cs` to Emono/CSharp generated code --
Some class or function test don't need a test in some Suite, you can disable test generated
of it with a blank file as following:
use lowercase and `_` as separator
`src/test/<testname>/<class_name>` #don't generate test for <class_name>
`src/test/<testname>/<class_name>/method_name` #don't generate test for <method_name>
`src/test/<testname>/<class_name>/<property_name>` #don't generate test for this property
`src/test/<testname>/<class_name>/<property_name>_get` #don't generate test for this property get
`src/test/<testname>/<class_name>/<property_name>_set` #don't generate test for this property set
Where to find more info
=======================
* read the Pyolian README file in EFL scripts
* read the eolian.py file (it declare the full eolian API)
* read the generator.py file (it's super simple)
* read the original [pyratemp docs](https://www.simple-is-better.org/template/pyratemp.html)
Note
====
This markdown file is mirrored in efl src tree (src/scripts/pyolian) and in
phab wiki (phab.enlightenment.org/w/pyolian). Don't forget to update the other
if you change one!

View File

View File

@ -0,0 +1,307 @@
#!/usr/bin/env python3
# encoding: utf-8
from enum import IntEnum, IntFlag
from testgen import name_helpers
class Function_List_Type(IntFlag):
OWN = 1
IMPLEMENTS = 2 # Overrides
EXTENSIONS = 4 # Interfaces/Mixins
INHERITED = 8 # Inherited but not overriden methods and classes
INHERITS_FULL = 4 & 8
class EKeys:
def __init__(self, ext):
self.ext = ext
self.dicttypes = {}
self.keywords = []
self.verbs = []
self.blacklist = ["efl_constructor"]
self.keyloads = ["init", "shutdown", "custom"]
self.implementsbl = ["construtor", "destructor", "finalize"]
self.funclist = Function_List_Type.OWN | Function_List_Type.IMPLEMENTS
def type_convert(self, eotype):
return eotype.name
def event_convert(self, event):
return event.c_macro
def print_arg(self, eoarg):
return "arg_{}".format(eoarg.name)
def format_name(self, func):
return self.name
class EMonoKeys(EKeys):
def __init__(self, ext):
super().__init__(ext)
self.funclist = (
Function_List_Type.OWN
| Function_List_Type.IMPLEMENTS
| Function_List_Type.EXTENSIONS
)
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_ref": "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": "Eina.Accessor",
"strbuf": "Eina.Strbuf",
"Efl.Class": "System.Type",
"rw_slice": "Eina.RwSlice",
"slice": "Eina.Slice",
}
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",
"apply",
"bind",
"cancel",
"copy",
"create",
"cut",
"delete",
"deselect",
"detach",
"do",
"gen",
"insert",
"iterate",
"join",
"leave",
"limit",
"paste",
"parse",
"prepend",
"process",
"query",
"refresh",
"remove",
"register",
"reject",
"release",
"reply",
"send",
"select",
"serialize",
"steal",
"sync",
"toggle",
"unbind",
"unregister"
]
self.blacklist = [
"efl_event_callback_array_priority_add",
"efl_event_callback_forwarder_priority_add",
"efl_player_playback_position_get",
"efl_text_font_source",
"efl_ui_focus_manager_focus_get",
"efl_ui_widget_focus",
"efl_ui_text_password",
"elm_interface_scrollable_repeat_events",
"elm_wdg_item_del",
"elm_wdg_item_focus",
"elm_interface_scrollable_mirrored_set",
"evas_obj_table_mirrored",
"edje_obj_load_error_get",
"efl_ui_focus_user_parent_get",
"efl_canvas_object_scale", # duplicated signature
"efl_access_parent_get",
"efl_access_name",
"efl_access_root_get",
"efl_access_type_get",
"efl_access_role_get",
"efl_access_action_description",
"efl_access_image_description",
"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",
"efl_constructor",
]
self.struct_blacklist = [
"Efl.Event_Description",
"Eina.Binbuf",
"Eina.Strbuf",
"Eina.Slice",
"Eina.Rw_Slice",
"Eina.Promise",
"Eina.Value",
"Eina.Value_Type",
"Eina.Future",
]
def escape_keyword(self, key):
key = "kw_{}".format(key) if key in self.keywords else key
return "{}Add".format(key) if key == "Finalize" else key
def direction_get(self, param):
direction = param.direction
if direction == direction.INOUT:
return "ref"
elif direction != direction.IN:
if param.type.name in ("slice", "rw_slice"):
return "ref"
else:
return "out"
elif (direction == direction.IN) and param.type.is_ptr:
if param.type.typedecl and (
param.type.typedecl.type == param.type.typedecl.type.STRUCT
):
return "ref" if param.type.name not in self.struct_blacklist else None
return None
def klass_name(self, eotype):
*namespaces, name = eotype.name.split(".")
namespaces = [self.escape_keyword(x.lower()) for x in namespaces]
is_interface = eotype.type == eotype.type.CLASS
k_name = ("I" if is_interface else "") + name
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, name_helpers.type_managed_name(eotype)
)
if new_type not in ("Eina.RwSlice", "Eina.Slice") and eotype.base_type:
# Stringshare is a special case where its C# type differs if inside or outside
# a container:
# - Non-contained stringshares are directly converted to strings.
# - Contained stringshares are kept as the container parameter as a tag to
# marshal the value correctly whem adding/removing items from the container.
if eotype.base_type.name == "stringshare":
base_type = "Eina.Stringshare"
else:
base_type = self.dicttypes.get(
eotype.base_type.name,
name_helpers.type_managed_name(eotype.base_type),
)
new_type = "{}<{}>".format(new_type, base_type)
return new_type
def event_convert(self, event):
return "{}Evt".format("".join([i.capitalize() for i in event.name.split(",")]))
def print_arg(self, eoarg):
r = super().print_arg(eoarg)
prefix = self.direction_get(eoarg) or None
return " ".join([prefix, r]) if prefix else r
def format_name(self, func):
names = func.comp.name.split("_")
if func.type == func.type.METHOD and names[-1] in self.verbs:
names.insert(0, names.pop())
fname = "".join([name.capitalize() for name in names])
if func.type == func.type.METHOD:
fname = self.escape_keyword(fname)
return fname
def GetKey(ext):
if ext == ".cs":
return EMonoKeys(ext)
return EKeys(ext)

View File

@ -0,0 +1,92 @@
#!/usr/bin/env python3
# encoding: utf-8
"""Helper module with naming rules for the C# binding."""
import os
import sys
# Hackish way of detecting pyolian...
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
def remove_underlines(name):
"""Removes underlines from name"""
return name.replace("_", "")
def managed_name(name):
"""Replaces underlines and capitalize first letter of each word"""
words = name.split("_")
return "".join(word[0].upper() + word[1:] for word in words)
def managed_namespaces(namespaces):
"""Converts an eolian list of namespaces into the managed namespace"""
return ".".join(remove_underlines(nsp) for nsp in namespaces)
def class_managed_name(cls):
"""Gets the full managed name of the given eolian class"""
ret = managed_namespaces(cls.namespaces)
if ret:
ret += "."
if cls.type in (eolian.Eolian_Class_Type.INTERFACE, eolian.Eolian_Class_Type.MIXIN):
ret += "I"
ret += remove_underlines(cls.short_name)
if ret == "Efl.Class":
return "System.Type"
return ret
def type_managed_name(type):
"""Gets the full managed name of a given type."""
if type.type == eolian.Eolian_Type_Type.CLASS:
return class_managed_name(type.class_)
ret = managed_namespaces(type.namespaces)
if ret:
ret += "."
ret += remove_underlines(type.short_name)
return ret
# Need to pass the class as it is not accessible from Event in Pyolian
def event_args_managed_name(event, cls):
"""Gets the full managed name of the event arguments struct"""
if event.type is None:
return "System.EventArgs"
ret = class_managed_name(cls)
return ret + managed_name(event.myname) + "EventArgs"
def event_managed_short_name(event):
"""Gets the managed short name of an event"""
return managed_name(event.name.replace(",", "_")) + "Event"
def enum_field_managed_name(field):
"""Gets the managed name of an Enum field"""
return managed_name(field.name)

View File

@ -0,0 +1,291 @@
import itertools
import os
from pyolian.eolian import Eolian_Function_Type, Eolian_Class_Type, Eolian_Object_Scope
from .ekeys import GetKey, Function_List_Type
from pyolian import eolian
from testgen import name_helpers
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("Error getting {}".format(attr))
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):
super().__init__(comp, os.path.join(path, comp.name), keys)
self.has_getter = (
comp.type in (Eolian_Function_Type.PROP_GET, Eolian_Function_Type.PROPERTY)
and comp.full_c_getter_name not in keys.blacklist
and comp.getter_scope == Eolian_Object_Scope.PUBLIC
and not os.path.isfile("{}_get".format(os.path.join(path, comp.name)))
)
self.has_setter = (
comp.type in (Eolian_Function_Type.PROP_SET, Eolian_Function_Type.PROPERTY)
and comp.full_c_setter_name not in keys.blacklist
and comp.setter_scope == Eolian_Object_Scope.PUBLIC
and not os.path.isfile("{}_set".format(os.path.join(path, comp.name)))
)
self.is_enum = (
lambda arg: arg.type
and arg.type.typedecl
and arg.type.typedecl.type == arg.type.typedecl.type.ENUM
)
self.is_number = lambda arg: arg.type and arg.type.builtin_type in (
arg.type.builtin_type.INT,
arg.type.builtin_type.UINT,
arg.type.builtin_type.LONG,
arg.type.builtin_type.ULONG,
arg.type.builtin_type.LLONG,
arg.type.builtin_type.ULLONG,
arg.type.builtin_type.INT8,
arg.type.builtin_type.UINT8,
arg.type.builtin_type.INT16,
arg.type.builtin_type.UINT16,
arg.type.builtin_type.INT32,
arg.type.builtin_type.UINT32,
arg.type.builtin_type.INT64,
arg.type.builtin_type.UINT64,
arg.type.builtin_type.INT128,
arg.type.builtin_type.UINT128,
)
@property
def getter_args(self):
return itertools.chain(self.getter_values, self.getter_keys)
@property
def setter_args(self):
return itertools.chain(self.setter_values, self.setter_keys)
@property
def format_name(self):
return self.keys.format_name(self)
class EventItem(ComItem):
def __init__(self, comp, path, keys):
self.myname = comp.name.replace(",", "_")
super().__init__(comp, os.path.join(path, self.myname), keys)
self.format_name = self.keys.event_convert(self)
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)
def mfilter(f):
if f.full_c_method_name in self.keys.blacklist:
return False
if os.path.isfile(os.path.join(self.path, f.name)):
return False
if f.type == Eolian_Function_Type.PROPERTY:
if f.getter_scope != Eolian_Object_Scope.PUBLIC:
scope = f.setter_scope
else:
scope = f.getter_scope
else:
scope = f.scope_get(f.type)
if scope != Eolian_Object_Scope.PUBLIC:
return False
return True
if self.keys.funclist & Function_List_Type.OWN:
self._methods = {
m.name: FuncItem(m, self.path, keys)
for m in self.comp.methods
if mfilter(m)
}
self._properties = {
p.name: FuncItem(p, self.path, keys)
for p in self.comp.properties
if mfilter(p)
}
self._events = {
e.name: EventItem(e, self.path, keys) for e in self.comp.events
}
else:
self._methods = {}
self._properties = {}
self._events = {}
if self.keys.funclist & Function_List_Type.IMPLEMENTS:
for imp in comp.implements:
if (
imp.namespace == self.name
or imp.short_name.lower() in self.keys.implementsbl
):
continue
f = imp.function
if not mfilter(f):
continue
if f.type == Eolian_Function_Type.METHOD:
if f.name in self._methods:
continue
self._methods[f.name] = FuncItem(f, self.path, keys)
elif f.type in (
Eolian_Function_Type.PROPERTY,
Eolian_Function_Type.PROP_GET,
Eolian_Function_Type.PROP_SET,
):
if f.name in self._properties:
continue
self._properties[f.name] = FuncItem(f, self.path, keys)
parents = []
if self.keys.funclist & Function_List_Type.INHERITS_FULL:
# Use inherits full to get inherited interfaces too
parents = self.comp.inherits_full
else:
if self.keys.funclist & Function_List_Type.EXTENSIONS:
parents = self.comp.extensions_hierarchy
if self.keys.funclist & Function_List_Type.INHERITED:
if parents:
parents = itertools.chain(self.comp.hierarchy, parents)
else:
parents = self.comp.hierarchy
for eoclass in parents:
for f in filter(mfilter, eoclass.methods):
if f.name in self._methods:
continue
self._methods[f.name] = FuncItem(f, self.path, keys)
for f in filter(mfilter, eoclass.properties):
if f.name in self._properties:
continue
self._properties[f.name] = FuncItem(f, self.path, keys)
@property
def properties(self):
return filter(lambda p: p.has_setter or p.has_getter, self._properties.values())
@property
def properties_get(self):
return filter(lambda p: p.has_getter, self._properties.values())
@property
def properties_set(self):
return filter(lambda p: p.has_setter, self._properties.values())
@property
def methods(self):
return self._methods.values()
@property
def events(self):
return self._events.values()
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 = "_".join([name, testname]) if testname else name
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):
if eotype.type == eolian.Eolian_Type_Type.CLASS:
return name_helpers.class_managed_name(eotype.class_)
if eotype.typedecl:
return name_helpers.type_managed_name(eotype)
return self.keys.type_convert(eotype)
def constructor_params(self, cls):
ret = []
constructors = itertools.chain(
cls.constructors, *[base.constructors for base in cls.inherits_full]
)
for constructor in constructors:
# Skip optional constructors for now
if constructor.is_optional:
continue
func = constructor.function
if func.type == eolian.Eolian_Function_Type.PROPERTY:
first_param = list(func.setter_values)[0]
else:
first_param = list(func.parameters)[0]
param_type = first_param.type
ret.append("default({})".format(name_helpers.type_managed_name(param_type)))
return (", " if ret else "") + ", ".join(ret)
def print_arg(self, eoarg):
return self.keys.print_arg(eoarg)
def intersect(self, a, b):
return list(set(a) & set(b))
def loadFiles(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 != Eolian_Class_Type.REGULAR:
continue
self.loadObj(eocls)
def loadObj(self, eocls):
cls = ClassItem(eocls, self.path, self.keys)
if not os.path.isfile(cls.path):
cls.myfullname = "{}_{}".format(self.fullname, cls.myname)
self.clslist.append(cls)
else:
print("removing {} Class from generated list".format(cls.name))

125
src/scripts/testgen/testgen.py Executable file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env python3
# encoding: utf-8
import os
import sys
import datetime
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
from testgen import name_helpers
# 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)
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
ctx["name_helpers"] = name_helpers
# 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="Eolian Test Generator.")
parser.add_argument(
"testname",
help="The Test Name used to find custom and template files. (REQUIRED)",
)
parser.add_argument(
"suitename", help="The Suite Name used to find custom files. (REQUIRED)"
)
parser.add_argument("filename", help="Generated test file destination. (REQUIRED)")
parser.add_argument("eofiles", nargs="*", help="The Eolian Files 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.loadFiles(eolian_db, args.eofiles)
t = Template(suite.template)
# try:
t.render(suite)
# except:
# print("ERROR RENDERING - Cannot create file: {}".format(suite.filename))

View File

@ -0,0 +1,187 @@
<!--(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_macro}$, 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.typedecl and arg.type.typedecl.type == arg.type.typedecl.type.STRUCT )-->{}<!--(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_param}$ arg_${arg.name}$ = NULL;
Eina_Free_Cb arg_${arg.name}$_free_cb = NULL;
<!--(else)-->
${arg.type.c_type_param}$ 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 in (arg.direction.OUT, arg.direction.INOUT))-->&<!--(end)-->arg_${arg.name}$
<!--(end)-->
<!--(end)-->
<!--(macro arg_self)-->
<!--(if not func.is_class)-->
obj
<!--(end)-->
<!--(end)-->
<!--(macro print_comma)-->
<!--(if i > 0 or not is_class)-->
,
<!--(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_macro}$ ****************/
${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}$(${arg_self(func=func)}$<!--(for i, arg in enumerate(func.parameters))-->${print_comma(i=i, is_class=func.is_class)}$${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.has_getter)-->
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_keys)-->, arg_${arg.name}$<!--(end)--><!--(for arg in func.getter_values)-->, &arg_${arg.name}$<!--(end)-->);
<!--(end)-->
${m_show(mshow=func.arg_get_shutdown)}$#!
${shutdown(mcls=cls.shutdown,mfunc=func.get_shutdown)}$
}
END_TEST
<!--(end)-->
<!--(if func.has_setter)-->
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_get)-->
tcase_add_test(tc, ${cls.myfullname}$_${func.full_c_getter_name}$);
<!--(end)-->
<!--(for func in cls.properties_set)-->
tcase_add_test(tc, ${cls.myfullname}$_${func.full_c_setter_name}$);
<!--(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;
}

View File

@ -0,0 +1,113 @@
<!--(macro m_show)-->
<!--(if mshow)-->
${mshow}$#!
<!--(end)-->
<!--(end)-->
<!--(macro def_obj)-->${name_helpers.class_managed_name(param.type.class_)}$ 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)-->
<!--(macro meth_target)-->
<!--(if func.is_class)-->${name_helpers.class_managed_name(cls)}$<!--(else)-->obj<!--(end)-->
<!--(end)-->
using System;
namespace TestSuite
{
<!--(if suite.custom)-->${suite.custom}$<!--(end)-->
<!--(for cls in suite)-->
/**************** TEST CASE ${cls.c_macro}$ ****************/
class Test${cls.name.replace('.','')}$
{
${name_helpers.class_managed_name(cls)}$ obj;
${m_show(mshow=cls.custom)}$#!
public void SetUp()
{
${m_show(mshow=suite.init)}$#!
<!--(if cls.init)-->
${cls.init}$
<!--(else)-->
obj = new ${name_helpers.class_managed_name(cls)}$(null${suite.constructor_params(cls)}$);
<!--(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)-->${meth_target(func=func, cls=cls)}$.${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.has_getter)-->
public void ${func.name}$_pget()
{
<!--(if func.getter_return_type or 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 not func.getter_return_type and len(list(func.getter_values)) == 1)-->
var arg_${list(func.getter_values)[0].name}$ = ${meth_target(func=func, cls=cls)}$.Get${func.format_name}$(${', '.join(['arg_{}'.format(param.name) for param in func.getter_keys])}$);
<!--(else)-->
<!--(if func.getter_return_type)-->var r = <!--(end)-->${meth_target(func=func, cls=cls)}$.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.has_setter)-->
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)}$#!
${meth_target(func=func, cls=cls)}$.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)-->
}

View File

@ -0,0 +1,3 @@
#include <Ecore.h>
#include <Ecore_Audio.h>
#include <Ecore_File.h>

View File

@ -0,0 +1,2 @@
int _rinit = ecore_audio_init();
ck_assert_int_eq(_rinit, 1);

View File

@ -0,0 +1 @@
arg_input = efl_add(ECORE_AUDIO_IN_CLASS, NULL);

View File

@ -0,0 +1 @@
arg_input = efl_add(ECORE_AUDIO_IN_CLASS, NULL);

View File

@ -0,0 +1,3 @@
#ifdef _WIN32
obj = efl_add_ref(ECORE_AUDIO_OUT_WASAPI_CLASS, parent);
fail_if(!obj, "ERROR: Cannot init Ecore.Audio.Out.Wasapi!\n")

View File

@ -0,0 +1,3 @@
#else
(void)parent;
#endif

View File

@ -0,0 +1,3 @@
#include <cairo/Ector_Cairo.h>
#include <software/Ector_Software.h>
#include <gl/Ector_GL.h>

View File

@ -0,0 +1 @@
fail_if(!ector_init(), "ERROR: Cannot init Ector!\n");

View File

@ -0,0 +1,2 @@
Ector_Surface *surface = efl_add(ECTOR_CAIRO_SOFTWARE_SURFACE_CLASS, NULL);
obj = efl_add_ref(ECTOR_RENDERER_CAIRO_SHAPE_CLASS, parent, ector_renderer_surface_set(efl_added, surface));

View File

@ -0,0 +1 @@
ector_shutdown();

View File

@ -0,0 +1 @@
#define EFL_CANVAS_LAYOUT_BETA

View File

@ -0,0 +1,2 @@
fail_if(!ecore_evas_init(), "ERROR: Cannot init Ecore Evas!\n");
fail_if(!edje_init(), "ERROR: Cannot init Edje!\n");

View File

@ -0,0 +1,2 @@
edje_shutdown();
ecore_evas_shutdown();

View File

@ -0,0 +1,9 @@
parent = evas_new();
evas_output_method_set(parent, evas_render_method_lookup("buffer"));
Evas_Engine_Info *einfo = evas_engine_info_get(parent);
evas_engine_info_set(parent, einfo);
evas_output_size_set(parent, 500, 500);
evas_output_viewport_set(parent, 0, 0, 500, 500);
(void)obj;

View File

@ -0,0 +1,9 @@
parent = evas_new();
evas_output_method_set(parent, evas_render_method_lookup("buffer"));
Evas_Engine_Info *einfo = evas_engine_info_get(parent);
evas_engine_info_set(parent, einfo);
evas_output_size_set(parent, 500, 500);
evas_output_viewport_set(parent, 0, 0, 500, 500);
(void)obj;

View File

@ -0,0 +1,8 @@
parent = evas_new();
evas_output_method_set(parent, evas_render_method_lookup("buffer"));
Evas_Engine_Info *einfo = evas_engine_info_get(parent);
evas_engine_info_set(parent, einfo);
evas_output_size_set(parent, 500, 500);
evas_output_viewport_set(parent, 0, 0, 500, 500);
(void)obj;

View File

@ -0,0 +1,8 @@
parent = evas_new();
evas_output_method_set(parent, evas_render_method_lookup("buffer"));
Evas_Engine_Info *einfo = evas_engine_info_get(parent);
evas_engine_info_set(parent, einfo);
evas_output_size_set(parent, 500, 500);
evas_output_viewport_set(parent, 0, 0, 500, 500);
(void)obj;

View File

@ -0,0 +1,8 @@
parent = evas_new();
evas_output_method_set(parent, evas_render_method_lookup("buffer"));
Evas_Engine_Info *einfo = evas_engine_info_get(parent);
evas_engine_info_set(parent, einfo);
evas_output_size_set(parent, 500, 500);
evas_output_viewport_set(parent, 0, 0, 500, 500);
(void)obj;

View File

@ -0,0 +1,8 @@
parent = evas_new();
evas_output_method_set(parent, evas_render_method_lookup("buffer"));
Evas_Engine_Info *einfo = evas_engine_info_get(parent);
evas_engine_info_set(parent, einfo);
evas_output_size_set(parent, 500, 500);
evas_output_viewport_set(parent, 0, 0, 500, 500);
(void)obj;

View File

@ -0,0 +1,2 @@
obj = efl_add(EFL_CANVAS_VIDEO_CLASS, parent, efl_canvas_object_legacy_ctor(efl_added));
fail_if(!obj, "Error: Canot get obj!\n");

View File

@ -0,0 +1,9 @@
int argc = 2;
char *argv[] = { "efl_ui_suite", "test" };
(void)parent;
_EFL_APP_VERSION_SET();
obj = efl_app_get();
efl_event_callback_add(obj, EFL_LOOP_EVENT_ARGUMENTS, efl_main, NULL);
fail_if(!ecore_init_ex(argc, argv));
__EFL_MAIN_CONSTRUCTOR;

View File

@ -0,0 +1,3 @@
efl_loop_exit_code_process(r);
__EFL_MAIN_DESTRUCTOR;
ecore_shutdown_ex();

View File

@ -0,0 +1,5 @@
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev)
{
efl_loop_quit(ev->object, EINA_VALUE_EMPTY);
}

View File

@ -0,0 +1 @@
arg_time = 0.5;

View File

@ -0,0 +1 @@
(void)parent;

View File

@ -0,0 +1 @@
#include <Eio.h>

View File

@ -0,0 +1 @@
fail_if(!eio_init(), "ERROR: Cannot init Eio!\n");

View File

@ -0,0 +1 @@
eio_shutdown();

View File

@ -0,0 +1 @@
fail_if(eldbus_init() < 0, "ERROR: Cannot init Eldbus!\n");

View File

@ -0,0 +1 @@
eldbus_shutdown();

View File

@ -0,0 +1,2 @@
#include "Emotion.h"
#include "Evas_Internal.h"

View File

@ -0,0 +1,3 @@
fail_if(!ecore_evas_init(), "ERROR: Cannot init ECore Evas!\n");
fail_if(!emotion_init(), "ERROR: Cannot init Emotion!\n");

View File

@ -0,0 +1,4 @@
// efl_unref(parent);
// ecore_evas_free(ee);
emotion_shutdown();
// ecore_evas_shutdown();

View File

@ -0,0 +1 @@
#include "efl_canvas_surface.h"