为C API头文件自动生成FFF伪定义

autofff的Python项目详细描述


自动fffBuild StatusPyPI versionLicense: MIT

为C API头文件自动生成FFF假定义。

将脚本合并到您的正常构建环境(如make)中,并自动生成带有伪造函数定义的测试头,以便与FFF自己的gtest或其他单元测试框架一起使用。

伪装背后的想法

特别是在嵌入式世界中,针对实际的目标平台运行(单元)测试通常是不可行的,因为您正在执行测试的体系结构和您正在为其编写代码的目标平台通常是不同的。

这就是伪造特定平台api可能会帮助您的地方。不要调用实际的目标平台api,而是将正在测试的代码(cut)与所述api的伪造版本相链接。现在,每当您的cut尝试访问平台api时,它都会调用假实现,您可以在测试用例的设置阶段轻松地对其进行配置。

FFF(伪函数框架)是一个框架,设计用于轻松创建api函数声明的伪定义,允许您配置返回值并检查在测试运行时调用的调用和参数历史记录。

在嵌入式C中伪造API的问题通常是使用动态链接的不可行性,以及C缺乏诸如“反射”之类的技术来在运行时操作剪切。这使得编写假定义的过程变得乏味、劳动密集和容易出错。

介绍AutoFFF,试图自动编写所谓的测试头(包括伪造定义的头)的过程。

假装的两种哲学

当你写假货的时候,你会注意到有两种方法来展示你的假货。

  1. 禁止原始api头
    这种策略bans通过定义api报头include guard来禁止原始报头,使得不可能包含原始函数、变量和类型声明。这给了您在测试头中的最终自由,但也意味着您必须手动声明api用户可能期望的任何类型、函数和变量。它还允许您控制include层次结构,并可能跳过一些与测试运行程序的体系结构不兼容的头文件。一般来说,这种方法通常涉及大量的复制和粘贴,因此更容易出现“代码腐烂”。如果您正在寻找一种易于维护的管理测试头的方法,则不是最佳策略。
  2. wrapping原始api头
    与禁止方法相反,wrapping策略直接包含原始api头,从而导入任何类型、变量和函数声明。此外,include层次结构将从原始结构中接管。唯一要添加到测试头中的是伪定义。显然,这个方法在测试头中给了您更少的自由,但是通常更短,并且随着时间的推移,不太容易出现“rot”

显然,哪种方法更适合自动化。因此autofff遵循wrapping编写测试头的方法,这在大多数情况下应该足够好。

最后必须说明的是,这两种哲学本身是很好地结合在一起的。

安装

使用pipPyPi存储库下载并安装autofff

py -3.6 -m pip install autofff

或从源安装:

py -3.6 -m pip install .

用法

作为模块

py -3.6 -m autofff \
    ./examples/driver.h \
    -O ./output/driver_th.h \
    -I ./examples \
    -F ./dependencies/pycparser/utils/fake_libc_include

使用提供的makefile

要运行生成和运行测试,只需执行:

make run_tests

您还可以让makefile为您安装autofff

make install_autofff

运行“生成假货”示例

make -f examples/generate-via-makefile/generate_fakes.mk CRAWL_PATHS=examples

作为一个python包

importautofffimportos.pathtargetHeader=input("Enter the path of the header you would like to scan: ")outputHeader=input("Enter the path of the target header file which shall be generated: ")fakes='./autofff/dependencies/pycparser/utils/fake_libc_include'scnr=autofff.GCCScanner(targetHeader,fakes)# Create GCC code scannerresult=scnr.scan()# Scan for function declarations and definitionsgen=autofff.SimpleFakeGenerator(os.path.splitext(os.path.basename(outputHeader))[0],targetHeader)# Create new generator with name output-header and path to target-headerifnotos.path.exists(os.path.dirname(outputHeader)):dirname=os.path.dirname(outputHeader)os.makedirs(dirname)withopen(outputHeader,"w")asfs:gen.generate(result,fs)# Generate fff-fakes from scanner-result

通用电气的假货如何生成

生成的测试头的格式显然取决于使用的FakeGenerator的细节。

  1. BareFakeGenerator只生成FAKE_VALUE_-和FAKE_VOID_FUNC宏,而不生成任何修饰,如include-guards或header-includes。如果要在顶部添加自己的文件(基于shell)处理,请使用此生成器。
  2. SimpleFakeGenerator将生成一个“最小可行测试头”,这意味着结果应该可以编译,而不需要太多的工作。

在头定义函数中

在某些api头中,函数可以在头中定义。这将导致在试图伪造此函数时出现问题,因为通过包含头,函数定义将复制到每个翻译单元中。如果我们试图以通常的方式应用一个伪定义,我们将得到一个“函数的重新定义x错误。

autofff实现了一个解决方案,以避免此重新定义错误并允许伪造原始函数。这个解决方法只是由一些定义组成,这些定义将把对原始in头定义的任何调用重新路由到我们伪造的头定义。为此,需要在指示对所考虑的函数的任何函数调用之前包含测试头(并因此预处理),即在剪切之前必须包含测试头。在预处理解决方案之前处理的任何函数调用都将使此函数调用的目标指向原始的in头定义。

实际上,解决方法如下:

/* api.h */#ifndef API_HEADER_H_#define API_HEADER_H_constchar*foo(void){return"Definitions inside headers are great!";}#endif
/* api_th.h */#ifndef TEST_HEADER_H_#define TEST_HEADER_H_#include"fff.h"#include"api.h"/* Re-route any call to 'foo' to 'foo_fff' (our fake definition). */#define foo foo_fff/* By re-routing the '_fake' and '_reset' type and function the workaround becomes invisible in the test-case. */#define foo_fake Foo_fff_fake#define foo_reset Foo_fff_reset/* Create the fake definition using the now re-routed 'foo'-symbol. */FAKE_VOID_FUNC(foo);#endif
/* cut.c - code-under-test */#include"api.h"#include<stdio.h>constchar*bar(void){constchar*str=foo();returnstr;}
/* test.c */#include"fff.h"#include"api_th.h" /* fakes + workaround */#include"cut.c"setup(void){RESET_FAKE(foo);}TEST_F(foo,ReturnBar_Success){constchar*expected_retval="Definitions inside headers make faking difficult!";foo_fake.return_val=expected_retvalconstchar*str=bar();ASSERT_STREQ(expected_retval,str);}

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java画布矩形位置   Java类文件未在Android Studio上编译或运行   Java“null”值从未分配给我的Runnable   java Jsp使用URL“保存或打开”访问pdf窗口在单击时显示错误   组件之间的Java swing通信   属性值的java正则表达式   java无法从myFile加载MainClass清单属性。震击器错误   Java内存碎片和大型阵列的分配   java是NullPointerException的一个问题   java如何使用鳄梨酱进行sftp的二次开发   新手程序员需要建议:“字符串索引超出范围”Java   基于页面的java Birt组头   使用集合时出错。使用ArrayList和类排序()。(爪哇)   在foursquare中使用多个单词进行带查询的java场馆搜索   有了新的Java14记录功能,是否可以为同一记录创建多个构造函数?   java创建自己的文件扩展名   java组织。阿帕奇。xml。安全性在Spring Boot下不工作   java谷歌地图标记标题安卓   java为什么选项卡小部件在安卓中位于内容之上?