Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feature/41-durable…
Browse files Browse the repository at this point in the history
…_mockup
  • Loading branch information
theHolgi committed Aug 19, 2023
2 parents a508416 + 1b4358e commit e9fb874
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 6 deletions.
4 changes: 3 additions & 1 deletion doc/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Let's call hammocking without any arguments:
.. code-block:: shell
$ python -m hammocking
usage: hammocking [-h] (--symbols SYMBOLS [SYMBOLS ...] | --plink PLINK) --outdir OUTDIR --sources SOURCES [SOURCES ...]
usage: hammocking [-h] (--symbols SYMBOLS [SYMBOLS ...] | --plink PLINK) --outdir OUTDIR --sources SOURCES [SOURCES ...] [--except EXCLUDES ...]
hammocking: error: the following arguments are required: --outdir/-o, --sources
hammocking needs ...
Expand All @@ -22,6 +22,8 @@ hammocking needs ...
* --symbols*: comma seperated list of symbol names which are to mock or
* *--plink*: path to the object file which contains the unresolved symbols to mock
* *--outdir*: An existing directory where to write code files containing mockup code.
* *--except*: if a symbol is found in a header of these directories, it will not be mocked. Use this to exclude symbols from mocking that will be provided by libraries in the linking process.
(Defaults to ``/usr/include`` for system headers)

One compilation unit
--------------------
Expand Down
31 changes: 26 additions & 5 deletions hammocking/hammocking.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import re
from argparse import ArgumentParser
from pathlib import Path
from typing import List, Union, Tuple
from typing import List, Union, Tuple, Iterator, Iterable
from clang.cindex import Index, TranslationUnit, Cursor, CursorKind, Config
from jinja2 import Environment, FileSystemLoader
import logging
Expand Down Expand Up @@ -82,12 +82,12 @@ def add_header(self, name: str) -> None:

def add_variable(self, type: str, name: str, size: int = 0) -> None:
"""Add a variable definition"""
print(f"INFO: HammocKing: Create mockup for variable {name}")
logging.info(f"HammocKing: Create mockup for variable {name}")
self.variables.append(Variable(type, name, size))

def add_function(self, type: str, name: str, params: List[Tuple[str, str]] = []) -> None:
"""Add a variable definition"""
print(f"INFO: HammocKing: Create mockup for function {name}")
logging.info(f"Create mockup for function {name}")
self.functions.append(Function(type, name, [Variable(param[0], param[1]) for param in params]))

def get_mockup(self, file: str) -> str:
Expand Down Expand Up @@ -120,6 +120,10 @@ def __init__(self, symbols: List[str], cmd_args: List[str] = [], mockup_style="g
self.symbols = symbols
self.cmd_args = cmd_args
self.writer = MockupWriter(mockup_style, suffix)
self.exclude_pathes = []

def add_excludes(self, pathes: Iterable[str]) -> None:
self.exclude_pathes.extend(pathes)

def read(self, sources: List[Path]) -> None:
for source in sources:
Expand All @@ -128,6 +132,18 @@ def read(self, sources: List[Path]) -> None:
self.logger.debug(f"Parsing {source}")
self.parse(source)

@staticmethod
def iter_children(cursor: Cursor) -> Iterator[Cursor]:
"""
Iterate the direct children of the cursor (usually called with a translation unit), but dive into namepsaces like extern "C" {
"""
for child in cursor.get_children():
if child.spelling:
yield child
elif child.kind == CursorKind.UNEXPOSED_DECL: # if cursor is 'extern "C" {', loop inside
for subchild in Hammock.iter_children(child):
yield subchild

def parse(self, input: Union[Path, str]) -> None:
parseOpts = {
"args": self.cmd_args,
Expand All @@ -149,7 +165,7 @@ def parse(self, input: Union[Path, str]) -> None:
translation_unit = Index.create(excludeDecls=True).parse(**parseOpts)
self.logger.debug(f"Parse diagnostics: {list(translation_unit.diagnostics)}")
self.logger.debug(f"Command arguments: {parseOpts['args']}")
for child in translation_unit.cursor.get_children():
for child in self.iter_children(translation_unit.cursor):
if child.spelling in self.symbols:
in_header = child.location.file.name != translation_unit.spelling
if in_header: # We found it in the Source itself. Better not include the whole source!
Expand Down Expand Up @@ -180,6 +196,7 @@ def done(self) -> bool:

class NmWrapper:
regex = r"\s*U\s+((?!_|llvm_)\S*)"
nmpath = "llvm-nm"

def __init__(self, plink: Path):
self.plink = plink
Expand All @@ -191,7 +208,7 @@ def get_undefined_symbols(self) -> List[str]:

def __process(self):
with Popen(
["llvm-nm", "--undefined-only", self.plink],
[NmWrapper.nmpath, "--undefined-only", self.plink],
stdout=PIPE,
stderr=PIPE,
bufsize=1,
Expand All @@ -217,14 +234,18 @@ def main(pargv):

arg.add_argument("--style", "-t", help="Mockup style to output", required=False, default="gmock")
arg.add_argument("--suffix", help="Suffix to be added to the generated files", required=False)
arg.add_argument("--except", help="Path prefixes that should not be mocked", nargs="*", dest="excludes", default=["/usr/include"])
args, cmd_args = arg.parse_known_args(args=pargv)

if args.debug:
logging.basicConfig(level=logging.DEBUG)
if not args.symbols:
args.symbols = NmWrapper(args.plink).get_undefined_symbols()

logging.debug("Extra arguments: %s" % cmd_args)

h = Hammock(symbols=args.symbols, cmd_args=cmd_args, mockup_style=args.style, suffix=args.suffix)
h.add_excludes(args.excludes)
h.read(args.sources)
h.write(args.outdir)

Expand Down
2 changes: 2 additions & 0 deletions hammocking/templates/gmock/mockup.cc.j2
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mock_ptr_t mockup_global_ptr = nullptr;
{{ variable.get_definition() }};
{% endfor %}

extern "C" {
{% for function in functions %}

{{function.get_signature()}}{
Expand All @@ -20,3 +21,4 @@ mock_ptr_t mockup_global_ptr = nullptr;
{% endif %}
} /* {{function.name}} */
{% endfor %}
}
2 changes: 2 additions & 0 deletions tests/data/gmock_test/test_add_function_get/mockup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
mock_ptr_t mockup_global_ptr = nullptr;


extern "C" {

int a_get_y2(){
if(mockup_global_ptr)
return mockup_global_ptr->a_get_y2();
else
return (int)0;
} /* a_get_y2 */
}
2 changes: 2 additions & 0 deletions tests/data/gmock_test/test_add_function_set_one_arg/mockup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
mock_ptr_t mockup_global_ptr = nullptr;


extern "C" {

void set_some_int(int some_value){
if(mockup_global_ptr)
mockup_global_ptr->set_some_int(some_value);
} /* set_some_int */
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
mock_ptr_t mockup_global_ptr = nullptr;


extern "C" {

float my_func(float unnamed1){
if(mockup_global_ptr)
return mockup_global_ptr->my_func(unnamed1);
else
return (float)0;
} /* my_func */
}
2 changes: 2 additions & 0 deletions tests/data/gmock_test/test_empty_templates/mockup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
mock_ptr_t mockup_global_ptr = nullptr;


extern "C" {
}
2 changes: 2 additions & 0 deletions tests/data/gmock_test/test_mini_c_gmock/mockup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ int a_y1;
a_y4_t a_y4;
int c_u1;

extern "C" {

int a_get_y2(){
if(mockup_global_ptr)
Expand Down Expand Up @@ -54,3 +55,4 @@ void c_set_u6(c_u6_t u6){
if(mockup_global_ptr)
mockup_global_ptr->c_set_u6(u6);
} /* c_set_u6 */
}
5 changes: 5 additions & 0 deletions tests/data/mini_c_test/b.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include "a.h"
#include "c.h"

// This function is not found in a header, but only here.
extern int local_extern(int x);

void b_init(void){
}

Expand All @@ -29,4 +32,6 @@ void b_step(void){
int b6;
a_get_y6(&b6);
c_set_u6((c_u6_t)b6);

local_extern(2);
}
15 changes: 15 additions & 0 deletions tests/hammocking_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class class_mockup {
int x;
float y;
extern "C" {
}
"""
)

Expand Down Expand Up @@ -317,6 +319,19 @@ def test_variable_and_function_with_config_guards(self):
assert len(mock.writer.functions) == 1, "Mockup shall have a function"
assert mock.writer.functions[0].get_signature() == "void foo()", "Function shall be created in the mockup"

def test_extern_c_variable(self):
"""Mock a variable that is inside an "extern C" section"""
mock = Hammock(["foo"])
mock.parse("""
extern "C" {
extern void foo();
}
"""
)
assert mock.done, "Should be done now"
assert len(mock.writer.functions) == 1, "Mockup shall have a function"
assert mock.writer.functions[0].get_signature() == "void foo()", "Function shall be created in the mockup"

def test_variable_array(self):
"""Mock an int array"""
mock = Hammock(["my_array"])
Expand Down

0 comments on commit e9fb874

Please sign in to comment.