{ "info": { "author": "Andreas Baulig", "author_email": "free.geronimo@hotmail.de", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6" ], "description": "# AutoFFF [![Build Status](https://travis-ci.org/FreeGeronimo/autofff.svg?branch=master)](https://travis-ci.org/FreeGeronimo/autofff) [![PyPI version](https://badge.fury.io/py/autofff.svg)](https://badge.fury.io/py/autofff) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n\nAuto-generate [FFF](https://github.com/meekrosoft/fff) fake definitions for C API header files.\n\nIncorporate the script into your normal build environment (like _make_) and automatically generate test-headers with faked function definitions ready-to-use with [FFF](https://github.com/meekrosoft/fff)'s own _gtest_ or some other unit-testing-framework.\n\n## The Idea Behind Faking\n\nEspecially in the embedded world, running your (unit-)tests against the actual target platform often isn't feasible, as the architecture you're executing the test on and your target platform you're writing the code for are generally not the same.\n\nThis is where faking a specific platform API might help you. Instead of calling the actual target platform API, you link your code-under-test (CuT) against a faked version of said API. Now whenever your CuT tries to access the platform API it is instead calling the fake-implementation which you can easily configure in your test-cases' setup phase.\n\n[FFF](https://github.com/meekrosoft/fff) (fake-functions-framework) is a framework designed to easily create faked definitions of your API function-declarations, allowing you to configure return values and inspect call and argument histories that were called during the tests' runtime.\n\nThe problem with faking an API in embedded C is usually the infeasibility of using dynamic linking and C's lack of techniques like 'reflection' to manipulate your CuT during runtime. This makes the process of writing fake definitions a tedious, labor intensive and error prone matter.\n\nIntroducing [*AutoFFF*](https://github.com/FreeGeronimo/autofff), an attempt at automating the process of writing so called test-headers (headers which include the faked definitions).\n\n### Two Philosophies of Faking\n\nWhen writing fakes you will notice that there are two approaches of laying out your fake.\n\n1. **Banning** the original API header\\\n This strategy *bans* the original header by defining the API headers include guard, making it impossible to include the original function, variable and type declarations. This gives you ultimate freedom in the test-header, but also means that you will have to manually declare any types, functions and variables the API-user might expect. It also allows you to control the include hierarchy and maybe skip some headers which aren't compatible with your test-runner's architecture. In general this approach usually involves a lot of copy&pasting and is therefore more prone to *\"code rot\"*. Not the optimal strategy if you're looking for an easy-to-maintain way of managing test-headers.\n1. **Wrapping** the original API header\\\n Conversely to the banning method the *wrapping* strategy directly includes the original API header, and thereby imports any type, variable and function declarations. Also the include hierarchy is taken over from the original. The only thing to add into the test-header are the fake definitions. This method evidently grants you less freedom in the test-header, but usually is much shorter and slightly less prone to *\"rot\"* over time.\n\nIt should become obvious which method is better suited for automation. Therefore *AutoFFF* follows the *wrapping* approach of writing test-headers, which for most cases should be good enough.\n\nFinally it must be stated, that these two philosophies seldomly mix well.\n\n## Installation\n\nUse `pip` to download and install *AutoFFF* from the [PyPi](https://pypi.org/project/autofff/) repositories:\n\n```shell\npy -3.6 -m pip install autofff\n```\n\nOr install from source:\n\n```shell\npy -3.6 -m pip install .\n```\n\n## Usage\n\n### As a Module\n\n```shell\npy -3.6 -m autofff \\\n ./examples/driver.h \\\n -O ./output/driver_th.h \\\n -I ./examples \\\n -F ./dependencies/pycparser/utils/fake_libc_include\n```\n\n### Using the provided Makefile\n\nTo run build and run the tests, simply execute:\n\n```shell\nmake run_tests\n```\n\nYou can also let the makefile do the installation of *AutoFFF* for you.\n\n```shell\nmake install_autofff\n```\n\n### Running the 'Generate Fakes' Example\n\n```shell\nmake -f examples/generate-via-makefile/generate_fakes.mk CRAWL_PATHS=examples\n```\n\n### As a Python Package\n\n```python\nimport autofff\n\nimport os.path\n\ntargetHeader = input(\"Enter the path of the header you would like to scan: \")\noutputHeader = input(\"Enter the path of the target header file which shall be generated: \")\nfakes = './autofff/dependencies/pycparser/utils/fake_libc_include'\n\nscnr = autofff.GCCScanner(targetHeader, fakes) # Create GCC code scanner\nresult = scnr.scan() # Scan for function declarations and definitions\n\ngen = autofff.SimpleFakeGenerator(os.path.splitext(os.path.basename(outputHeader))[0], targetHeader) # Create new generator with name output-header and path to target-header\n\nif not os.path.exists(os.path.dirname(outputHeader)):\n dirname = os.path.dirname(outputHeader)\n os.makedirs(dirname)\n\nwith open(outputHeader, \"w\") as fs:\n gen.generate(result, fs) # Generate fff-fakes from scanner-result\n```\n\n## How Fakes Are Generated\n\nThe format of the generated test-header obviously depends on the specifics of the `FakeGenerator` being used.\n\n1. The `BareFakeGenerator` will only generate the `FAKE_VALUE_`- and `FAKE_VOID_FUNC` macros without any decorations, like include guards or header includes. Use this generator if you want to add your own file (shell-based) processing on top.\n2. The `SimpleFakeGenerator` will generate a \"minimum viable test header\", meaning the result should be compilable without too much effort.\n\n### In-Header Defined Functions\n\nIn some API headers functions may be defined within the header. This will cause issues when trying to fake this function, because by including the header the function definition is copied into each translation unit. If we try to apply a fake definition the usual way, we will end up with a _\"redefinition of function *x*\"_ error.\n\n*AutoFFF* implements a workaround to avoid this redefinition error and allowing to fake the original function. This workaround simply consists of some defines which will re-route any call to the original in-header definition to our faked one. For this to work it is required that the test-header is included (and thereby pre-processed) _before_ any function call to the function under consideration is instructed, i.e. the test-header must be included _before_ the CuT. Any function call that is processed before the workaround is being pre-processed will leave this function call targeted towards the original in-header definition.\n\nIn practice the workaround looks like this:\n\n```c\n/* api.h */\n#ifndef API_HEADER_H_\n#define API_HEADER_H_\n\nconst char* foo(void)\n{\n return \"Definitions inside headers are great!\";\n}\n\n#endif\n```\n\n```c\n/* api_th.h */\n#ifndef TEST_HEADER_H_\n#define TEST_HEADER_H_\n\n#include \"fff.h\"\n#include \"api.h\"\n\n/* Re-route any call to 'foo' to 'foo_fff' (our fake definition). */\n#define foo foo_fff\n/* By re-routing the '_fake' and '_reset' type and function the workaround becomes invisible in the test-case. */\n#define foo_fake Foo_fff_fake\n#define foo_reset Foo_fff_reset\n/* Create the fake definition using the now re-routed 'foo'-symbol. */\nFAKE_VOID_FUNC(foo);\n\n#endif\n```\n\n```c\n/* cut.c - code-under-test */\n#include \"api.h\"\n#include \n\nconst char* bar(void)\n{\n const char* str = foo();\n return str;\n}\n```\n\n```c\n/* test.c */\n#include \"fff.h\"\n#include \"api_th.h\" /* fakes + workaround */\n#include \"cut.c\"\n\nsetup(void)\n{\n RESET_FAKE(foo);\n}\n\nTEST_F(foo, ReturnBar_Success)\n{\n const char* expected_retval = \"Definitions inside headers make faking difficult!\";\n foo_fake.return_val = expected_retval\n\n const char* str = bar();\n\n ASSERT_STREQ(expected_retval, str);\n}\n```\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/FreeGeronimo/autofff", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "autofff", "package_url": "https://pypi.org/project/autofff/", "platform": "", "project_url": "https://pypi.org/project/autofff/", "project_urls": { "Homepage": "https://github.com/FreeGeronimo/autofff" }, "release_url": "https://pypi.org/project/autofff/0.3/", "requires_dist": [ "pycparser (>=2.19)", "overrides (>=1.9)", "configobj (>=5.0.6)", "validator (>=2.0.6)" ], "requires_python": "", "summary": "Auto-generate FFF fake definitions for C API header files", "version": "0.3" }, "last_serial": 4489345, "releases": { "0.1-1": [ { "comment_text": "", "digests": { "md5": "e05410e7d8c081f462ebfadbfbe241f1", "sha256": "c0c6f20f425a8a7627eb6a8718e25ecfae89ccb8c005d5d4f7cd619908176ade" }, "downloads": -1, "filename": "autofff-0.1.post1-py3-none-any.whl", "has_sig": false, "md5_digest": "e05410e7d8c081f462ebfadbfbe241f1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10598, "upload_time": "2018-09-29T15:06:25", "url": "https://files.pythonhosted.org/packages/5a/95/5603a45d3ad3272d82383d380643e1d6d93eee8209d7da788c4fe8975e62/autofff-0.1.post1-py3-none-any.whl" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "31e7217c9d255762e424a881b3d378d1", "sha256": "289cf848a7f7957b3a111e3088d607f21cb5d831448f15b6db41cd9fdf3d24f8" }, "downloads": -1, "filename": "autofff-0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "31e7217c9d255762e424a881b3d378d1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12020, "upload_time": "2018-09-29T16:09:37", "url": "https://files.pythonhosted.org/packages/71/4c/4f091cfcb497e739dd80113866f53e77bf2c47c05a79854eb516c1e3de73/autofff-0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c99dfa81f1838649e744556dbb7f0283", "sha256": "0da8d93766a345521b78190c76cdc219faa2cbaf0a0bfcedfe6a7aa67ca180cd" }, "downloads": -1, "filename": "autofff-0.2.tar.gz", "has_sig": false, "md5_digest": "c99dfa81f1838649e744556dbb7f0283", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9733, "upload_time": "2018-09-29T16:09:39", "url": "https://files.pythonhosted.org/packages/e9/94/3c6e94b367957fb4bbc0f414f918294c83824ed2dccc9edf8411b03886f7/autofff-0.2.tar.gz" } ], "0.3": [ { "comment_text": "", "digests": { "md5": "2414335b47374d3678620f2b6324139c", "sha256": "f96ede62aa2008b8151ba8fd03cf3065b659ce03ef9d47a7bbaae005d3c79e8f" }, "downloads": -1, "filename": "autofff-0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "2414335b47374d3678620f2b6324139c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12547, "upload_time": "2018-11-15T11:38:59", "url": "https://files.pythonhosted.org/packages/fa/93/699704c1a277bd51e6f9c0577fa6ed585ec674f72a6533aa6ff61675ec9f/autofff-0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9050005f57f585b62016f2456487385e", "sha256": "2f8c31784bc4f82de554a46abdcc56438f5697eda259da1d54090abf3f852c27" }, "downloads": -1, "filename": "autofff-0.3.tar.gz", "has_sig": false, "md5_digest": "9050005f57f585b62016f2456487385e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10966, "upload_time": "2018-11-15T11:39:01", "url": "https://files.pythonhosted.org/packages/97/66/e64546876839a02f8319550ce43b47fb31985c9beb999a9a7c88f8ec66e7/autofff-0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "2414335b47374d3678620f2b6324139c", "sha256": "f96ede62aa2008b8151ba8fd03cf3065b659ce03ef9d47a7bbaae005d3c79e8f" }, "downloads": -1, "filename": "autofff-0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "2414335b47374d3678620f2b6324139c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12547, "upload_time": "2018-11-15T11:38:59", "url": "https://files.pythonhosted.org/packages/fa/93/699704c1a277bd51e6f9c0577fa6ed585ec674f72a6533aa6ff61675ec9f/autofff-0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9050005f57f585b62016f2456487385e", "sha256": "2f8c31784bc4f82de554a46abdcc56438f5697eda259da1d54090abf3f852c27" }, "downloads": -1, "filename": "autofff-0.3.tar.gz", "has_sig": false, "md5_digest": "9050005f57f585b62016f2456487385e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10966, "upload_time": "2018-11-15T11:39:01", "url": "https://files.pythonhosted.org/packages/97/66/e64546876839a02f8319550ce43b47fb31985c9beb999a9a7c88f8ec66e7/autofff-0.3.tar.gz" } ] }