一、LTP的框架理解分享
按照当前的发展与其说LTP是一个完整的测试框架不如说是 c、python、shell的测试框架的整合,即每种testcase都有不一样的支持环境,触发方法、编译方式(除了c,python和shell是脚本不需要编译只需要解释器)其他也不需要编译下面逐一介绍。
下面的内容就不分享原理了,只分享如何使用即如何添加用例和编译。
1、程序框架
%%文件结构如下:
ltp/
├── testcases/
│ ├── kernel/
│ │ ├── syscalls/
│ │ │ ├── my_new_test/ # 新建测试目录
│ │ │ │ ├── my_new_test.c # 测试源码
│ │ │ │ ├── Makefile # 本地Makefile
│ │ │ │ └── ... # 其他依赖文件
│ ├── include/ # 头文件目录
│ │ ├── tst_test.h
│ │ ├── lapi/
│ │ │ └── syscalls.h # 系统调用封装
│ ├── lib/ # 库文件
│ │ ├── libltp.a
│ │ └── ...
└── include/mk/ # 编译规则
└── testcases.mk
二,添加文件夹
一般我们添加的用例是我们各位公司的私有用例一般情况下在/testcase下新建文件夹,然后在里面建立文件夹再新建各个用例的文件夹。
/testcase文件夹的makefile如下:
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2009, Cisco Systems Inc.
# Ngie Cooper, July 2009
top_srcdir ?= .. //变量指下面的$(top_srcdir)/include/mk/env_pre.mk 刚好等于ltp下面include/mk/env_pre.mk
/*自动拥有 LTP 的标准变量
(CC, CFLAGS, LDFLAGS, top_srcdir, abs_srcdir…)
自动识别 SUBDIRS(除非你在 leaf,不使用的也不会干扰)
自动拥有过滤目录的机制(FILTER_OUT_DIRS)
自动拥有通用构建环境(如 debug flags、优化 flags)
*/
include $(top_srcdir)/include/mk/env_pre.mk //ltp下面include/mk/env_pre.mk
# XXX (garrcoop):
# kdump shouldn't be compiled by default, because it's runtime based and will
# crash the build host (the tests need to be fixed to just build, not run).
FILTER_OUT_DIRS := kdump //排除命令,设置后make会跳过这个文件夹
/*这个作用大概是如WITH_OPEN_POSIX_TESTSUITE值是yes 就屏蔽编译open_posix_testsuite
ifneq ($(WITH_OPEN_POSIX_TESTSUITE),yes)
FILTER_OUT_DIRS += open_posix_testsuite
endif
/*自动扫描子目录
根据 FILTER_OUT_DIRS / 开关 生成 SUBDIRS
递归调用 make 构建所有 SUBDIRS */
include $(top_srcdir)/include/mk/generic_trunk_target.mk
如上可以看出要再一个文件夹下面增加多个文件夹,这个文件夹的makeflie下必须要有
top_srcdir:在这个文件夹下make 必须要告诉make在ltp文件夹下面的那个位(返回上层执行几次可以到达/ltp层)
include $(top_srcdir)/include/mk/env_pre.mk :包含默认make配置
include $(top_srcdir)/include/mk/generic_trunk_target.mk :递归文件夹及编译的规则
三,添加用例
1、c语言的子框架
上诉知道添加文件夹,现在分享如何让添加用例
%% c类型testcase
在/ltp/testcases/kernel/syscalls/创建文件
cd ltp/testcases/kernel/syscalls/
mkdir my_c_test
cd my_c_test
c类型testcase 模板:
// testcases/kernel/syscalls/my_read_test/my_c_test.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "tst_test.h" // LTP测试框架头文件
#include "lapi/syscalls.h" // 系统调用封装
#define TEST_FILE "testfile.txt"
#define BUFFER_SIZE 1024
static void run(void)
{
int fd;
char buffer[BUFFER_SIZE];
ssize_t ret;
// 创建测试文件
fd = SAFE_OPEN(TEST_FILE, O_CREAT | O_RDWR, 0644);
SAFE_WRITE(1, fd, "Hello LTP Test", 14);
SAFE_LSEEK(fd, 0, SEEK_SET);
// 测试read系统调用
ret = SAFE_READ(0, fd, buffer, sizeof(buffer));
if (ret == 14) {
tst_res(TPASS, "read() returned correct byte count: %zd", ret);
} else {
tst_res(TFAIL, "read() returned wrong byte count: %zd", ret);
}
SAFE_CLOSE(fd);
SAFE_UNLINK(TEST_FILE);
}
static struct tst_test test = {
.tid = "my_read_test",
.tcnt = 1,
.needs_tmpdir = 1, // 需要临时目录
.test_all = run,
};
%%最底层文件的makefile
关于makefile其实有两种情况,一般c代码编译的结构有5种,分别是:
用户态执行文件,无固定后缀一般无后缀
静态库, .a后缀
共享库/动态库 .so
内核模块 .ko
中间目标对象文件 .o
LTP-DDT的c语言case基本两种情况编译成用户态执行文件和内核模块两种的编译方式不一样。
%%%%用户态执行文件:
他扮演的是测试应用的角色通过系统调用测试
用户态的程序运行依赖的是系统库,链接阶段链接的是glibc库。
program.c
│
▼
gcc(预处理:#include 展开)
│
▼
gcc(编译成汇编 .s)
│
▼
as(汇编成 .o)
│
▼
ld(链接 glibc / crt0 / 动态链接器)
│
▼
生成 ELF 可执行文件 program
c语言用例如上面”c类型testcase 模板“章节中他的头文件都是linux系统的库文件和LTP相关的头文件:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "tst_test.h" // LTP测试框架头文件
#include "lapi/syscalls.h"
底层makefile如下:
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) International Business Machines Corp., 2001
top_srcdir ?= ../../../..
include $(top_srcdir)/include/mk/testcases.mk
include $(top_srcdir)/include/mk/generic_leaf_target.mk
generic_leaf_target.mk的文件已经包含了相关的规则,如下一部分:
TARGETS := $(patsubst %.c,%,$(notdir $(wildcard *.c)))
MAKE_TARGETS += $(TARGETS)
%: %.c
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) //cc等由testcases.mk定义
他会找到当前文件夹的.c文件运行“ gcc -o 文件名.c ”
或者如下:
top_srcdir ?= ../../..
include $(top_srcdir)/include/mk/env_pre.mk
INSTALL_DIR := testcases/bin/ddt //就是会放在被测设备的LTP文件夹的位子,方便LTP查找
FILTER_OUT_DIRS := //定义要排除的子目录,输入文件夹名字则跳过该文件夹编译
# We don't want to copy over the Makefile
UNWANTED_FILES := Makefile //定义不拷贝到安装目录的文件
INSTALL_MODE := 00755 //安装目录的文件权限0075 即读、写、执行 即输出文件的权限。
/*c的编译器标识简单理解为参数 -g:生成调试信息 -Wall:启用所有警告 -D__EXPORTED_HEADERS__:定义预处理器宏,通常用于内核头文件*/
CFLAGS = -g -Wall -D__EXPORTED_HEADERS__
LDFLAGS += -g //添加链接器标志 += 表示在原有基础上追加(来自env_pre.mk的设置) -g:在链接时包含调试信息
/*含义:设置头文件搜索路径(这个定义被下面的覆盖了)
-I $(KERNEL_USR_INC):内核用户空间头文件路径,可以编译器当前shell设置如exprot KERNEL_USR_INC=/opt/kernel/incloud/
-I src/interface/common:通用接口头文件
-I ../utils/user:用户空间工具头文件
*/
INCLUDES = -I $(KERNEL_USR_INC) -I src/interface/common -I ../utils/user
#Path of header files to be included
INCLUDES = -I src/parser \
-I src/interface/common \
-I ../utils/user
#List of source files- Update this on adding a new C file
/* 含义:定义所有要编译的C源文件
使用反斜杠 \ 进行多行书写
包含 watchdog 定时器测试的各种组件:
文件API工具函数
WDT通用接口
WDT解析器
IOCTL测试用例
打开关闭测试用例
写入测试用例
*/
SOURCES := \
../utils/user/st_fileapi.c \
src/interface/common/st_wdt_common.c \
src/parser/st_wdt_parser.c \
src/testcases/st_wdt_ioctl.c \
src/testcases/st_wdt_open_close.c \
src/testcases/st_wdt_write.c
MAKE_TARGETS := wdt_tests //定义要构建的目标名称
/* 含义:定义如何构建 wdt_tests 目标
$(CC):C编译器(从环境变量或env_pre.mk中获取)
$(CFLAGS):编译标志,编译器的参数
$(LDFLAGS):链接标志 ,链接器的参数
-o wdt_tests:指定输出文件名
${INCLUDES}:头文件路径
${SOURCES}:所有源文件
注意:这里一次性编译所有源文件生成单个可执行文件
*/
wdt_tests:
$(CC) $(CFLAGS) $(LDFLAGS) -o wdt_tests ${INCLUDES} ${SOURCES}
include $(top_srcdir)/include/mk/generic_leaf_target.mk
如上包含include $(top_srcdir)/include/mk/env_pre.mk使用基础的配置,但是编译规则自定义,有些厂商对应用的编译有不一样的要求。
%%%%内核模块:
当需要
内核模块的执行依赖于内核文件,链接阶段是链接的是导出函数的符号,链接的是内核。
编译过程如下
mymodule.c
│
▼
Kbuild Makefile(obj-m += mymodule.o)
│
▼
make -C /lib/modules/$(uname -r)/build M=$(PWD) modules
│
▼
1) 预处理(cpp)
2) 编译成汇编(gcc -S)
3) 汇编成 .o(as)
4) Kbuild 链接,生成 mymodule.o(不同于 gcc 默认规则)
5) modpost 分析符号,生成 mymodule.mod.c
6) 编译 mymodule.mod.c → mymodule.mod.o
7) ld 链接成 mymodule.ko
8) depmod 生成模块依赖(*.mod)
如上第二章第二点“适配内核”的代码,他包含linux内核的相关文件
#include <linux/module.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <asm/gpio.h>
/TLP/testcase/ddt/gpio_test_suite/ makefile 如下:
# MakeFile function :: MakeFile for compiling GPIO kernel module
LDFLAGS += -g -static
INCLUDES = -I ../utils/user -I $(KERNEL_DIR)/include
EXTRA_CFLAGS := -I$(src)/../../../utils/kernel //扩展的头文件地址
#List of source files for kernel module - Update this on adding a new C file
SOURCES_KERNEL := \
testcases/gpio_test.o \
obj-m += gpio_test.o ../../../utils/kernel/kSt_timer.o//会在本文件夹搜索相同名字的.c文件
gpio_test-objs := $(SOURCES_KERNEL) //显式指定代码地址即输出gpio_test.o的地址,他会自动识别对应名字的源码
MAKE_TARGETS := modules
modules:
$(MAKE) -C $(KERNEL_DIR) ARCH=arm SUBDIRS=$(PWD) modules// KERNEL_DIR是linux内核代码地址
cp gpio_test.ko ../../
cp ../../../utils/kernel/kSt_timer.ko ../../
clean:
rm -f */*.o
rm -f ../../*.ko
rm -f *.o
rm -f .*.cmd
rm -rf .tmp_versions/
rm -f *.order
rm -f *.c
rm -f *.symvers
rm -f ./testcases/*.o*
rm -f ./testcases/.*.cmd
rm -f ../../../utils/kernel/*.ko
rm -f ../../../utils/kernel/*.o
rm -f ../../../utils/kernel/*.mod.*
rm -f ../../../utils/kernel/.*.cmd
rm -f *.o
rm -f *.*
rm -rf ../../../utils/kernel/.kSt_timer.ko.cmd
如上主要设置obj-m 、KERNEL_DIR、$(MAKE) -C (KERNELDIR)ARCH=armSUBDIRS=(KERNEL_DIR) ARCH=arm SUBDIRS=(KERNELDIR)ARCH=armSUBDIRS=(PWD) modules 地址。注意不需要特意的添加头文件地址,KERNEL_DIR会自动识别下面的incloud地址。
2、shell脚本子框架
如下shell的模板:
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
# 需要的命令
TST_NEEDS_CMDS="ls grep"
# 自动创建临时目录
TST_NEEDS_TMPDIR=1
# 运行入口
TST_SETUP=setup
TST_TESTFUNC=run_test
TST_CLEANUP=cleanup
TST_CNT=1
. tst_test.sh #导入tst_test.sh
setup() {
tst_res TINFO "Setup environment"
echo "hello ltp" > test.txt
}
run_test() {
if grep "hello" test.txt >/dev/null; then
tst_res TPASS "Found expected string"
else
tst_res TFAIL "String not found"
fi
}
cleanup() {
tst_res TINFO "Cleaning up"
}
如上脚本导入了tst_test.sh
makefile 如下:
top_srcdir ?= ../../../..
include $(top_srcdir)/include/mk/env_pre.mk
INSTALL_DIR := testcases/bin/ddt/$(notdir $(CURDIR:%/=%))
FILTER_OUT_DIRS :=
# We don't want to copy over the Makefile
UNWANTED_FILES := Makefile
INSTALL_MODE := 00755
INSTALL_TARGETS := $(filter-out $(UNWANTED_FILES),$(notdir $(patsubst $(abs_srcdir)/%,%,$(wildcard $(abs_srcdir)/*))))
MAKE_TARGETS :=
include $(top_srcdir)/include/mk/generic_leaf_target.mk
如上makefile 内容可以看出 shell的LTP 功能只不过是将 脚本整理打包到对应的文件夹设置对应权限。
如下runltp的strup函数:
setup()
{
cd `dirname $0` || \
{
echo "FATAL: unable to change directory to $(dirname $0)"
exit 1
}
export LTPROOT=${PWD}
export TMPBASE="/tmp"
export PATH=$( find ${LTPROOT}/testcases/bin/ddt -type d -exec printf {}":" \; )"${PATH}:${LTPROOT}/testcases/bin"
export LTP_DEV_FS_TYPE="ext2"
[ -d "$LTPROOT/testcases/bin" ] ||
{
echo "FATAL: LTP not installed correctly"
echo "INFO: Follow directions in INSTALL!"
exit 1
}
[ -e "$LTPROOT/bin/ltp-pan" ] ||
{
echo "FATAL: Test suite driver 'ltp-pan' not found"
echo "INFO: Follow directions in INSTALL!"
exit 1
}
}
这里 export PATH=$( find LTPROOT/testcases/bin/ddt−typed−execprintf":" )"{LTPROOT}/testcases/bin/ddt -type d -exec printf {}":" \; )"LTPROOT/testcases/bin/ddt−typed−execprintf":")"{PATH}😒{LTPROOT}/testcases/bin" 这里设置path ,然后generic_leaf_target.mk 会把/testcase/lib/里面的文件复制到输出目录的/testcase/bin/下面,这样执行脚本就可以找对应的库脚本。
总结
如上就是添加用例相关分享
4081

被折叠的 条评论
为什么被折叠?



