如何模拟头文件中的函数?

How to mock functions in headerfiles?

我在做什么:

我正在使用 cmocka 运行 对大型嵌入式项目进行单元测试。 嵌入式项目是用arm-gcc-compiler编译的。 单元测试是使用嵌入式代码片段和 cmocka 库使用正常 gcc 编译的。

通常cmocka推荐使用-Wl,--wrap=functionName标志来mock(替换)一些不需要的子函数。这个效果不错。

问题:

好吧,在我的嵌入式代码中有一个头文件 (foo.h),其中包含一些函数(声明为内联)。其中一个函数包含 arm-gcc-compiler 的一些汇编代码,当然 gcc.

无法编译这些代码

愚蠢的是,wrap-flag 似乎不适用于放置在头文件中的函数。

问题:

如何在头文件中模拟这个函数?

我是如何尝试解决问题的:

我想插入一些 #idef 宏来排除提到的汇编器部分。但这是不可能的,因为这个文件属于一个许可的库,我不能更改它的内容。

我可以将我的待测函数提取到其他文件中,这样 foo.h 就不再需要包含了。但这会混淆嵌入式源代码结构。

问题的确切行数

确切的代码放在第233行的freeRtos的portmacro.h中:

portFORCE_INLINE static void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI;

    __asm volatile
    (
        "   mov %0, %1                                              \n" \
        "   msr basepri, %0                                         \n" \
        "   isb                                                     \n" \
        "   dsb                                                     \n" \
        :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
    );
}

其中 portFORCE_INLINE 定义为:

#define portFORCE_INLINE inline __attribute__(( always_inline))

Stupidly the wrap-flag seems not to work on functions which are placed in header files.

这不是 wrap 的错,该函数已被编译器内联,因此链接器无能为力。

How to mock this function in the headerfile away?

一种选择是使用 sed 自动修补有问题的代码,然后再将其传递给 gcc。例如。改变

portFORCE_INLINE static void vPortRaiseBASEPRI( void )
{
  uint32_t ulNewBASEPRI;
  ...
}

portFORCE_INLINE static void vPortRaiseBASEPRI_2( void )
{
  uint32_t ulNewBASEPRI;
  ...
}

从你的例子到

portFORCE_INLINE static void vPortRaiseBASEPRI( void );

portFORCE_INLINE static void vPortRaiseBASEPRI_2( void );

cat tmp.c | sed '/inline\|INLINE/,/^}$/{ s/^\(.*\(inline\|INLINE\).*\)/;/; /inline\|INLINE/!d }'

正则表达式非常草率,它依赖于 header 中的所有定义都将具有 INLINE 标记这一事实,但在您的情况下应该足够了。

您可以将以上命令嵌入到您的 Makefile 中以在临时文件夹中生成自定义 header,然后使用 -Ipath/to/temp/folder 标志覆盖默认 header。

我没有使用过 cmocka,所以我不确定框架内是否已经有管理它的方法。

但是,cmock 使用一种方法,其中 header 被复制到测试构建的包含层次结构中更高的位置(并且只有测试构建,该位置甚至不包含在发布版本中。

然后可以编辑此 header 的副本,使函数声明简单地变为 port void vPortRaiseBASEPRI( void );。然后当生成模拟时,就像在任何其他情况下一样,为此(以及同一 header 中的其他函数声明)生成模拟。因为正在生成 hte 模拟,所以没有匹配的函数源代码定义(即 .c 文件)并不重要。

请参阅 https://dmitryfrank.com/articles/unit_testing_embedded_c_applications

中的 "Dealing with compiler-specific stuff" 部分

还有我的类似问题以及我是如何解决的:Unit test C with compiler specific keywords