0


【嵌入式单元测试】C语言单元测试框架搭建

cmocka

单元测试框架是一个软件包,它能够让开发者比较方便的表达产品代码需要表现出什么样的行为。单元测试框架提供了一个自动化单元测试的解决方案,让开发者把更多的精力放在测试用例的设计的编写上,而不用花精力考虑如何对测试用例进行组织。

cmocka

是一个优雅的C语言单元测试框架,支持模拟对象。它只需要标准的C库,适用于各种计算平台(Linux、windows,以及嵌入式)。
理论上来说,

cmocka

可以支持任何使用标准C库的交叉编译器。

本文将介绍如何在嵌入式环境(交叉编译)搭建

cmocka

单元测试环境,以及

cmocka

的简单使用示例。

cmocka交叉编译

源码下载

目前最新的1.1.5版本,对于嵌入式环境,我们需要下载源码进行交叉编译

cmocka1.1源码下载地址

这里以

cmocka-1.1.5.tar.xz

为例。

编译准备

将上述源码在linux环境中解压,并在源码同级目录新建编译目录

build_dir

在这里插入图片描述

cmocka-1.1.5

内容如下:

在这里插入图片描述

源码修改

进入

cmocka-1.1.5

源码目录,修改顶层

CMakeLists.txt

,将如下行注释掉。

doc

组件需要特定的库支持,嵌入式环境一般没有这个库,而且这个只是生成代码注释,对功能没有影响,所以将其注释。

# add_subdirectory(doc)

指定编译器

接下来在

build_dir

目录的同级目录新建配置文件

arm64_setup.cmake

(文件名随意)
在这里插入图片描述
文件内容如下:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm64)

set(tools /mnt/opt/compile_tools/bin/)
set(CMAKE_C_COMPILER ${tools}/aarch64-openwrt-linux-gcc)
set(CMAKE_CXX_COMPILER ${tools}/aarch64-openwrt-linux-g++)
  • CMAKE_SYSTEM_NAME 指定嵌入式系统类型
  • CMAKE_SYSTEM_PROCESSOR 指定嵌入式平台
  • tools 交叉编译器路径(以实际路径为准)
  • CMAKE_C_COMPILER C交叉编译器
  • CMAKE_CXX_COMPILER C++交叉编译器

编译

编译需要进入

build_dir

目录,先执行如下命令生成必要的配置和makefile文件

$ cd build_dir
$ cmake -DCMAKE_TOOLCHAIN_FILE=../arm64_setup.cmake -DBUILD_STATIC_LIB=ON ../cmocka-1.1.5/
  • -DCMAKE_TOOLCHAIN_FILE是指定刚刚创建的编译器配置文件
  • -DBUILD_STATIC_LIB=ON 是编译生成静态库,去掉这句只会生成动态库
  • ../cmocka-1.1.5/指定cmocka源码目录

如果上述步骤没有错误,那么在

build_dir

应该会生成若干目录和文件,其中就包括makefile,接下来执行

make

编译即可。

$ make

出现下列提示表示编译成功:

Scanning dependencies of target cmocka
[4%] Building C object src/CMakeFiles/cmocka.dir/cmocka.c.o
......[100%] Linking C executable test_uptime
[100%] Built target test_uptime

如果需要

clean

,直接在

build_dir

目录执行

make clean

是不行的,因为

Cmake

不支持。最简单的方式就是把

build_dir

目录手动清空即可。

编译完成后查看

build_dir/src

,会生成我们需要的动态库或者静态库.
使用

file

指令可以看到这个动态库是

ARM aarch64

平台的,表明我们交叉编译成功了。

$ build_dir/src$ ls
CMakeFiles           libcmocka.so    libcmocka.so.0.7.0  Makefile
cmake_install.cmake  libcmocka.so.0  libcmocka-static.a

$ file libcmocka.so.0.7.0 
libcmocka.so.0.7.0: ELF 64-bit LSB shared object, ARM aarch64, version 1(SYSV), dynamically linked, with debug_info, not stripped

另外,

build_dir/example

目录下也会生成一些示例

demo

,可以直接在开发板上运行。

$ build_dir/example$ ls
allocate_module_test  CMakeFiles           Makefile
assert_macro_test     cmake_install.cmake  mock
assert_module_test    CTestTestfile.cmake  simple_test
$ file simple_test 
simple_test: ELF 64-bit LSB executable, ARM aarch64, version 1(SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped

有了

libcmocka

,在加上源码中的

cmocka.h

,我就可以利用

cmocka

提供的API编译我们自己的单元测试代码了。

cmocka.h

位于

cmocka

源码的

include

目录

cmocka使用示例

测试代码如下:

$ tree hello/ 
hello/
├── inc
│   └── cmocka.h
├── libs
│   ├── libcmocka.so
│   └── libcmocka-static.a
├── makefile
└── src
    └── hello_cmocka.c
  • libs文件夹主要存放库文件,推荐使用libcmocka静态库,使用静态库的好处是编译出的二进制文件在开发板上可以直接运行,如果是动态库还需要开发板上也安装这个动态库,静态库的缺点就是编译产物体积较大。

示例

makefile

文件如下:

#source file
SOURCE  +=$(wildcard ./src/*.c)
OBJS    :=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))

TARGET  := hello_cmocka

#compile and lib parameter
CC      := aarch64-openwrt-linux-gcc
LIBS    := -lcmocka-static
LDFLAGS := -L./libs  
DEFINES :=
INCLUDE := -I./inc/ 
CFLAGS  := -g -Wno-unused-variable -O3 $(DEFINES)$(INCLUDE)
CXXFLAGS:=$(CFLAGS) 
  
.PHONY: 

all :$(TARGET)
objs :$(OBJS)
             
clean :rm -fr ./src/*.o
    rm -fr $(TARGET)$(TARGET):$(OBJS)$(CC)$(CXXFLAGS) -o $@$(OBJS)$(LDFLAGS)$(LIBS)

示例

hello_cmocka.c

如下:

#include<stdarg.h>#include<stdio.h>#include<setjmp.h>#include<stddef.h>#include<stdint.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<sys/ioctl.h>#include<cmocka.h>staticintadd(int a ,int b){return a+b;}staticchar*print_string(int num){switch(num){case1:return"CASE1";case2:return"CASE2";default:return"NOT SUPPORT";}return"NOT SUPPORT";}staticvoidtest_demo1(void**state){int ret =add(3,2);assert_int_equal(ret,5);(void) state;}staticvoidtest_demo2(void**state){int ret =add(3,2);assert_int_equal(ret,0);(void) state;}staticvoidtest_demo3(void**state){char*p =print_string(1);assert_string_equal(p,"CASE1");(void) state;}intmain(void){conststructCMUnitTest tests[]={cmocka_unit_test(test_demo1),cmocka_unit_test(test_demo2),cmocka_unit_test(test_demo3),};returncmocka_run_group_tests(tests,NULL,NULL);}

运行结果如下:

# ./hello_cmocka
[==========] Running 3test(s).[ RUN      ] test_demo1
[       OK ] test_demo1
[ RUN      ] test_demo2
[  ERROR   ]---0x5!=0[   LINE   ]--- src/hello_cmocka.c:46: error: Failure![  FAILED  ] test_demo2
[ RUN      ] test_demo3
[       OK ] test_demo3
[==========]3test(s) run.[  PASSED  ]2test(s).[  FAILED  ]1test(s), listed below:[  FAILED  ] test_demo2

 1 FAILED TEST(S)

提示

test_demo2

测试失败,原因是

0x5 != 0

,

test_demo2

的预期结果是0,实际返回5,不符合预期,故测试失败。

assert_int_equal()

是判断int类型结果,

assert_string_equal()

是判断

const char *

类型的结果。除此之外,

cmocka

还提供了更多其他的测试API,请参考

cmocka.h

.

常见问题

  1. 编译错误
/cmocka-1.1.5/include/cmocka.h:132:28: error: conflicting types for'uintptr_t'
typedef unsigned int uintptr_t;
/mnt/opt/include/bits/alltypes.h:109:24: note: previous declaration of 'uintptr_t' was here
 typedef unsigned _Addr uintptr_t;
cmocka

源码中

uintptr_t

定义和我们的交叉编译器中的定义冲突了,这里只需要暂时把编译器中的定义注释掉,编译完

cmocka

再改回来即可。

  1. 其他编译问题
cmocka

仅使用了标准C库,它的跨平台兼容性很好。因此,对于绝大多数的交叉编译器应该都是支持的(除非你的编译器版本很低,对C库的支持不全)。
因此,交叉编译

cmocka

源码中遇到的编译问题基本上都是C代码问题,与平台无关。

参考

  • cmocka官网
  • cmocka API介绍

本文转载自: https://blog.csdn.net/qq_24835087/article/details/125803037
版权归原作者 知否,知否 所有, 如有侵权,请联系我们删除。

“【嵌入式单元测试】C语言单元测试框架搭建”的评论:

还没有评论