我正在做的事情:
我正在使用cmocka为大型嵌入式项目运行单元测试。嵌入式项目是用arm-gcc-compiler
编译的。单元测试是使用嵌入代码片段和cmocka库使用普通gcc
编译的。
通常,cmocka建议使用-Wl,--wrap=functionName
标志来模拟(替换)一些不需要的子函数。这个效果很好。
问题:
在我的嵌入代码中有一个头文件(foo.h
),它包含一些函数(声明为内联)。其中一个函数包含arm-gcc-compiler
的一些汇编代码,当然gcc
无法编译这些代码。
奇怪的是,wrap
-标志似乎不适用于放置在头文件中的函数。
问题:
如何在头文件中模拟此函数?
我如何解决问题:
我想插入一些#idef
宏来排除前面提到的汇编部分。但这是不能做到的,因为这个文件属于许可的库,我不允许更改它的内容。
我可以将测试中的函数提取到其他文件中,这样就不需要再包含foo.h
了。但这会混淆嵌入的源代码结构。
问题的确切线路
确切的代码放在freeRtos的portmacro.h的第233行:
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))
令人遗憾的是,wrap标志似乎对函数无效它们被放置在头文件中。
这不是wrap
的错,函数已被编译器内联,因此链接器无法执行任何操作。
如何在头文件中模拟此函数?
一个选项是在将有问题的代码传递给gcc之前,使用sed
自动修补该代码。例如,更改
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).*)/1;/; /inline|INLINE/!d }'
regex相当草率,它依赖于这样一个事实,即头中的所有定义都将具有INLINE
标记,但在您的情况下可能已经足够了。
您可以将上面的命令嵌入到Makefile中,在临时文件夹中生成自定义标头,然后用-Ipath/to/temp/folder
标志覆盖默认标头。
我还没有使用cmocka,所以我不确定框架中是否已经有管理它的方法。
然而,cmock使用了一种方法,通过该方法,标头被复制到测试构建的include层次结构中更高的位置(并且只有测试构建,该位置甚至不被发布构建隐式包含)。
然后可以编辑此标头的副本,从而使函数声明简单地变为port void vPortRaiseBASEPRI( void );
。然后,当生成mock时,将为此生成mock(以及同一头中的其他函数声明),就像在任何其他情况下一样。因为正在生成hte mock,所以函数没有匹配的源代码定义(即.c
文件)并不重要。
请参阅https://dmitryfrank.com/articles/unit_testing_embedded_c_applications
我的类似问题以及我是如何在这里解决的:使用编译器特定关键字的单元测试C