Skip to content

Commit

Permalink
#49: More tests; move default-return into Function
Browse files Browse the repository at this point in the history
  • Loading branch information
theHolgi committed Mar 21, 2024
1 parent 6e6c148 commit 0bd9481
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 20 deletions.
42 changes: 27 additions & 15 deletions hammocking/hammocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def _collect_arguments(params) -> str:

return ", ".join(arguments)

def render(self, name) -> str:
def render(self, name: str) -> str:
if self.t.kind == TypeKind.CONSTANTARRAY:
res = f"{name}[{self.t.get_array_size()}]"
element_type = RenderableType(self.t.get_array_element_type())
Expand All @@ -51,15 +51,12 @@ def render(self, name) -> str:
else:
return self.t.spelling + " " + name

@property
def is_basic(self) -> bool:
if self.t.kind == TypeKind.CONSTANTARRAY:
return False
return True

@property
def is_constant(self) -> bool:
return self.t.kind == TypeKind.CONSTANTARRAY or self.t.is_const_qualified()
if self.is_array:
return self.t.element_type.is_const_qualified()
else:
return self.t.is_const_qualified()

@property
def is_array(self) -> bool:
Expand All @@ -73,6 +70,16 @@ def is_array(self) -> bool:
def is_struct(self) -> bool:
fields = list(self.t.get_canonical().get_fields())
return len(fields) > 0

def initializer(self) -> str:
if self.is_struct:
return f"({self.spelling}){{0}}"
elif self.is_array:
return "{0}"
elif self.t.kind == TypeKind.VOID:
return "void"
else:
return f"({self.spelling})0"

@property
def spelling(self) -> str:
Expand Down Expand Up @@ -117,6 +124,7 @@ def __init__(self, c: Cursor) -> None:

@property
def type(self) -> str:
"""The variable type as string"""
return self._type.spelling

def get_definition(self, with_type: bool = True) -> str:
Expand All @@ -126,15 +134,12 @@ def get_definition(self, with_type: bool = True) -> str:
return self.name

def is_constant(self) -> bool:
"""Is constant qualified"""
return self._type.is_constant

def initializer(self) -> str:
if self._type.is_struct:
return f"({self._type.spelling}){{0}}"
elif self._type.is_array:
return "{0}"
else:
return f"({self._type.spelling})0"
"""C expression to represent the value "0" according to the variable type"""
return self._type.initializer()

def __repr__(self) -> str:
return f"<{self.get_definition()}>"
Expand Down Expand Up @@ -164,12 +169,18 @@ def _collect_arguments(self, with_types: bool) -> str:
return ", ".join(arguments)

def has_return_value(self) -> bool:
"""Does the function have a return value?"""
return self.type.t.kind != TypeKind.VOID

@property
def return_type(self) -> str:
"""The function return type as string"""
return self.type.spelling # rendering includes the name, which is not what the user wants here.

def default_return(self) -> str:
"""C expression to represent the value "0" according to the function return type"""
return self.type.initializer()

def get_call(self) -> str:
"""
Return a piece of C code to call the function
Expand All @@ -180,11 +191,12 @@ def get_call(self) -> str:
return f"{self.name}({self._collect_arguments(False)})"

def get_param_types(self) -> str:
"""Return the function type parameters as a list of types"""
param_types = ", ".join(f"{param.type}" for param in self.params)
return f"{param_types}"

def __repr__(self) -> str:
return f"<{self.type} {self.name} ()>"
return f"<{self.type} {self.name} ({self.get_param_types()})>"


class MockupWriter:
Expand Down
2 changes: 1 addition & 1 deletion hammocking/templates/gmock/mockup.cc.j2
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extern "C" {
if(0 != mockup_global_ptr)
return mockup_global_ptr->{{function.get_call()}};
else
return ({{function.return_type}})0;
return {{function.default_return()}};
{% else %}
if(0 != mockup_global_ptr)
mockup_global_ptr->{{function.get_call()}};
Expand Down
2 changes: 1 addition & 1 deletion hammocking/templates/plain_c/mockup.c.j2
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

{{function.get_signature()}}{
{% if function.has_return_value() %}
return ({{function.return_type}})0;
return {{function.default_return()}};
{% endif %}
} /* {{function.name}} */
{% endfor %}
146 changes: 143 additions & 3 deletions tests/hammocking_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,47 @@ def is_var_or_func(c: Cursor) -> bool:

class TestVariable:
def test_simple(self):
"Basic type"
v = Variable(clang_parse("char x"))
assert v.name == "x"
assert v.is_constant() == False
assert v.get_definition() == "char x"
assert v.initializer() == "(char)0"

def test_array(self):
"Array type"
w = Variable(clang_parse("int my_array[2]"))
assert w.name == "my_array"
assert w.is_constant() == False
assert w.get_definition() == "int my_array[2]"
assert w.initializer() == "{0}"

def test_unlimited_array(self):
"Unlimited array type"
w = Variable(clang_parse("int my_array[]"))
assert w.name == "my_array"
assert w.is_constant() == False
assert w.get_definition() == "int my_array[]"
assert w.initializer() == "{0}" # Cannot be initialized, but hey...

def test_constant(self):
"Basic constant"
w = Variable(clang_parse("const int y;"))
assert w.name == "y"
assert w.is_constant() == True
assert w.get_definition() == "const int y"
assert w.initializer() == "(const int)0"

def test_constant_array(self):
"Basic constant array"
w = Variable(clang_parse("const int y[3];"))
assert w.name == "y"
assert w.is_constant() == True
assert w.get_definition() == "const int y[3]"
assert w.initializer() == "{0}"

def test_constant_struct(self):
"Constant structure"
w = Variable(clang_parse("""
typedef struct { int a; int b; } y_t;
extern const y_t y;"""))
Expand All @@ -59,17 +71,52 @@ def test_constant_struct(self):
assert w.get_definition() == "const y_t y"
assert w.initializer() == "(const y_t){0}"

def test_ptr_int(self):
"Pointer to integer"
w = Variable(clang_parse("""int *ptr;"""))
assert w.name == "ptr"
assert w.is_constant() == False
assert w.get_definition() == "int * ptr"
assert w.initializer() == "(int *)0"

def test_ptr_func(self):
"Pointer to function"
w = Variable(clang_parse("""int (*func)(int,int)"""))
assert w.name == "func"
assert w.is_constant() == False
assert w.get_definition() == "int (*func)(int,int)"
assert w.initializer() == "(int (*)(int, int))0"

def test_constant_ptr(self):
"Constant pointer"
w = Variable(clang_parse("""const int *y;"""))
assert w.name == "y"
assert w.is_constant() == False
assert w.get_definition() == "const int * y"
assert w.initializer() == "(const int *)0"

def test_ptr_to_constant(self):
"Pointer to constant"
w = Variable(clang_parse("""int *const y;"""))
assert w.name == "y"
assert w.is_constant() == True
assert w.get_definition() == "int *const y"
assert w.initializer() == "(int *const)0"


class TestFunction:
def test_void_void(self):
"returns void / void parameters"
f = Function(clang_parse("void func(void);"))
assert f.name == "func"
assert f.get_signature() == "void func()"
assert f.get_call() == "func()"
assert f.get_param_types() == ""
assert f.has_return_value() == False
assert f.default_return() == "void" # "return void" is a valid way to exit a void function

def test_void_int(self):
"Integer parameter"
f = Function(clang_parse("void set(int a);"))
assert f.name == "set"
assert f.get_signature() == "void set(int a)"
Expand All @@ -78,64 +125,157 @@ def test_void_int(self):
assert f.has_return_value() == False

def test_int_void(self):
"Integer return type"
f = Function(clang_parse("int get(void);"))
assert f.name == "get"
assert f.get_signature() == "int get()"
assert f.get_call() == "get()"
assert f.get_param_types() == ""
assert f.has_return_value() == True
assert f.default_return() == "(int)0"

def test_typedef_int(self):
"Integer/typedef return type"
f = Function(clang_parse("typedef int some_type; some_type get(void);"))
assert f.name == "get"
assert f.get_signature() == "some_type get()"
assert f.get_call() == "get()"
assert f.get_param_types() == ""
assert f.has_return_value() == True
assert f.default_return() == "(some_type)0"

def test_void_int_double(self):
"Integer and double parameters"
f = Function(clang_parse("void set(int a, double b);"))
assert f.name == "set"
assert f.get_signature() == "void set(int a, double b)"
assert f.get_call() == "set(a, b)"
assert f.get_param_types() == "int, double"
# assert f.has_return_value() == False
assert f.has_return_value() == False

def test_function_with_unnamed_arguments(self):
"Unnamed arguments"
f = Function(clang_parse("float my_func(float, float);"))
assert f.name == "my_func"
assert f.get_signature() == "float my_func(float unnamed1, float unnamed2)"
assert f.get_call() == "my_func(unnamed1, unnamed2)"
assert f.get_param_types() == "float, float"
assert f.default_return() == "(float)0"

def test_variadic_function(self):
"Variadic function"
f = Function(clang_parse("int printf_func(const char* fmt, ...);"))
assert f.name == "printf_func"
assert f.get_signature() == "int printf_func(const char * fmt, ...)"
assert f.get_call() == "printf_func(fmt)" # TODO
assert f.get_param_types() == "const char *" # ?

def test_array_param(self):
"Takes an array parameter"
f = Function(clang_parse("void x(int arg[]);"))
assert f.name == "x"
assert f.get_signature() == "void x(int arg[])"
assert f.get_call() == "x(arg)"
assert f.get_param_types() == "int[]"

def test_funcptr_param(self):
"Takes a function-pointer parameter"
f = Function(clang_parse("void x(int (*cb)(void));"))
assert f.name == "x"
assert f.get_signature() == "void x(int (*cb)())"
assert f.get_call() == "x(cb)"
assert f.get_param_types() == "int (*)(void)"

def test_blank_func(self):
"Blank (nonproto) function"
f = Function(clang_parse("void x();"))
assert f.name == "x"
assert f.get_signature() == "void x()"
assert f.get_call() == "x()"
assert f.get_param_types() == ""


def test_ptr_return(self):
def test_const_ptr_return(self):
"const-Pointer const-value return type"
f = Function(clang_parse("const char* const x(void);"))
assert f.name == "x"
assert f.return_type == "const char *const"
assert f.get_signature() == "const char *const x()"
assert f.get_call() == "x()"
assert f.get_param_types() == ""
assert f.default_return() == "(const char *const)0"

def test_struct_param_func(self):
"Structure type parameter"
f = Function(clang_parse("""
typedef struct { int a; int b; } x_t;
extern void f(x_t x);"""))
assert f.name == "f"
assert f.return_type == "void"
assert f.get_signature() == "void f(x_t x)"
assert f.get_call() == "f(x)"
assert f.get_param_types() == "x_t"
assert f.has_return_value() == False

def test_struct_type_return_func(self):
"Structure/typedef return type"
f = Function(clang_parse("""
typedef struct { int a; int b; } x_t;
extern x_t f();"""))
assert f.name == "f"
assert f.return_type == "x_t"
assert f.get_signature() == "x_t f()"
assert f.get_call() == "f()"
assert f.get_param_types() == ""
assert f.default_return() == "(x_t){0}"

def test_named_struct_return_func(self):
"Structure return type"
f = Function(clang_parse("""
struct x_s { int a; int b; };
struct x_s f();
"""))
assert f.name == "f"
assert f.return_type == "struct x_s"
assert f.get_signature() == "struct x_s f()"
assert f.get_call() == "f()"
assert f.get_param_types() == ""
assert f.default_return() == "(struct x_s){0}"

def test_enum_param(self):
"enum/typedef parameter"
f = Function(clang_parse("""
typedef enum { FIRST; SECOND } e_t;
void f(e_t param);
"""))
assert f.name == "f"
assert f.return_type == "void"
assert f.get_signature() == "void f(e_t param)"
assert f.get_call() == "f(param)"
assert f.get_param_types() == "e_t"

def test_named_enum_param(self):
"named enum parameter"
f = Function(clang_parse("""
enum some_enum { FIRST; SECOND };
void f(enum some_enum param);
"""))
assert f.name == "f"
assert f.return_type == "void"
assert f.get_signature() == "void f(enum some_enum param)"
assert f.get_call() == "f(param)"
assert f.get_param_types() == "enum some_enum"

def test_named_enum_return(self):
"named enum return"
f = Function(clang_parse("""
enum some_enum { FIRST; SECOND };
enum some_enum get_enum(void);
"""))
assert f.name == "get_enum"
assert f.return_type == "enum some_enum"
assert f.get_signature() == "enum some_enum get_enum()"
assert f.get_call() == "get_enum()"
assert f.default_return() == "(enum some_enum)0"


class TestMockupWriter:
Expand Down

0 comments on commit 0bd9481

Please sign in to comment.