mugen是openEuler社区开放的测试框架,提供公共配置和方法以便社区开发者进行测试代码的编写和执行
- 优化了mugen框架的入口函数
- 当前版本添加了对测试套路径和测试用例环境的添加
- 新增python公共函数
bash dep_install.sh
- 命令执行:
bash mugen.sh -c --ip $ip --password $passwd --user $user --port $port
- 参数说明:
- ip:测试机的ip地址
- user:测试机的登录用户,默认为root
- password: 测试机的登录密码
- port:测试机ssh登陆端口,默认为22
- 环境变量文件:./conf/env.json
{
"NODE": [
{
"ID": 1,
"LOCALTION": "local",
"MACHINE": "physical",
"FRAME": "aarch64",
"NIC": "eth0",
"MAC": "55:54:00:c8:a9:21",
"IPV4": "192.168.0.10",
"USER": "root",
"PASSWORD": "openEuler12#$",
"SSH_PORT": 22,
"BMC_IP": "",
"BMC_USER": "",
"BMC_PASSWORD": ""
},
{
"ID": 2,
"LOCALTION": "remote",
"MACHINE": "kvm",
"FRAME": "aarch64",
"NIC": "eth0",
"MAC": "55:54:00:c8:a9:22",
"IPV4": "192.168.0.11",
"USER": "root",
"PASSWORD": "openEuler12#$",
"SSH_PORT": 22,
"HOST_IP": "",
"HOST_USER": "",
"HOST_PASSWORD": ""
"HOST_SSH_PORT": ""
}
]
}
- 在用例中的使用:NODE${id}_${LOCALTION}
- 执行所有用例
bash mugen.sh -a
- 执行指定测试套
bash mugen.sh -f testsuite
- 执行单条用例
bash mugen.sh -f testsuite -r testcase
- 日志输出shell脚本的执行过程
bash mugen.sh -a -x
bash mugen.sh -f testsuite -x
bash mugen.sh -f testsuite -r testcase -x
- 根据模板编写用例脚本(推荐)
source ${OET_PATH}/libs/locallibs/common_lib.sh
# 需要预加载的数据、参数配置
function config_params() {
LOG_INFO "Start to config params of the case."
LOG_INFO "No params need to config."
LOG_INFO "End to config params of the case."
}
# 测试对象、测试需要的工具等安装准备
function pre_test() {
LOG_INFO "Start to prepare the test environment."
LOG_INFO "No pkgs need to install."
LOG_INFO "End to prepare the test environment."
}
# 测试点的执行
function run_test() {
LOG_INFO "Start to run test."
# 测试命令:ls
ls -CZl -all
CHECK_RESULT 0
# 测试/目录下是否存在proc|usr|roor|var|sys|etc|boot|dev目录
CHECK_RESULT "$(ls / | grep -cE 'proc|usr|roor|var|sys|etc|boot|dev')" 7
LOG_INFO "End to run test."
}
# 后置处理,恢复测试环境
function post_test() {
LOG_INFO "Start to restore the test environment."
LOG_INFO "Nothing to do."
LOG_INFO "End to restore the test environment."
}
main "$@"
- 单纯的shell脚本或python脚本,通过脚本执行的返回码判断用例是否成功。
- 可参考样例:testsuite测试套下用例oe_test_casename_02和oe_test_casename_03
- 文件名为测试套名,文件后缀.json
- 文件内容说明:
- 如何加载到用例中,以供调用
# bash
source ${OET_PATH}/libs/locallibs/common_lib.sh
# python
import os, sys, subprocess
LIBS_PATH = os.environ.get("OET_PATH") + "/libs/locallibs"
sys.path.append(LIBS_PATH)
import xxx
- 公共函数
- 日志打印
# bash LOG_INFO "$message" LOG_WARN "$message" LOG_DEBUG "$message" LOG_ERROR "$message" # python import mugen_log mugen_log.logging(level, message) # level:INFO,WARN,DEBUG,ERROR;message:日志输出
- 结果检查
# bash CHECK_RESULT $1 $2 $3 $4 # $1:测试点的返回结果 # $2:预期结果 # $3:对比模式,0:返回结果和预期结果相对;1:返回结果和预期结果不等 # $4:发现问题,日志输出
- rpm包安装卸载
# bash ## func 1 DNF_INSTALL "vim bc" "$node_id" DNF_REMOVE "$node_id" "jq" "$mode" # mode:默认为0,会删除用例中安装的包,当为非0时,则只卸载当软件包 # python import rpm_manage tpmfile = rpm_manage.rpm_install(pkgs, node, tmpfile) rpm_manage.rpm_remove(node, pkgs, tmpfile)
- 远程命令执行
# bash ## func 1 SSH_CMD "$cmd" $remote_ip $remote_password $remote_user $time_out $remote_port` ## func 2 P_SSH_CMD --cmd $cmd --node $node(远端节点号) P_SSH_CMD --cmd $cmd --ip $remote_ip --password $remote_password --port $remote_port --user $remote_user --timeout $timeout # python conn = ssh_cmd.pssh_conn(remote_ip, remote_password,remote_port,remote_user,remote_timeout) exitcode, output = ssh_cmd.pssh_cmd(conn, cmd) # port:默认为22 # user:默认为root # timeout: 默认不限制
- 目录文件传输
# bash ## func 1 ### 本地文件传输到远端 `SSH_SCP $local_path/$file $REMOTE_USER@$REMOTE_IP:$remote_path "$REMOTE_PASSWD"` ### 远端文件传输到本地 `SSH_SCP $REMOTE_USER@$REMOTE_IP:$remote_path/$file $local_path "$REMOTE_PASSWD"` ## func 2 ### 目录传输 SFTP get/put --localdir $localdir --remotedir $remotedir ### 文件传输 SFTP get/put --localdir $localdir --remotedir $remotedir --localfile/remotefile $localfile/$remotefile # python ### 目录传输 import ssh_cmd, sftp conn = ssh_cmd.pssh_conn(remote_ip, remote_password,remote_port,remote_user,remote_timeout) psftp_get(conn,remote_dir, local_dir) psftp_put(local_dir=local_dir, remote_dir=remote_dir) ### 文件传输 import ssh_cmd, sftp psftp_get(remote_dir, remote_file, local_dir) psftp_put(local_dir=local_dir, local_file=local_file, remote_dir=remote_dir) # get:从远端获取 # put:传输到远端 # localdir: 默认为当前目录 # remotedir:默认为远端根目录
- 获取空闲端口
# bash GET_FREE_PORT $ip # python import free_port free_port.find_free_port(ip)
- 检查端口是否被占用
# bash IS_FREE_PORT $port $ip # python import free_port free_port.is_free_port(port, ip)
- 获取测试网卡
# bash TEST_NIC $node_id # python import get_test_device get_test_nic(node_id) # node_id:默认为1号节点
- 获取测试磁盘
# bash TEST_DISK $node_id # python import get_test_device get_test_disk(node_id) # node_id:默认为1号节点
- 睡眠等待
# bash SLEEP_WAIT $wait_time $cmd # python import sleep_wait sleep_wait.sleep_wait(wait_time,cmd)
- 远端重启等待
# bash REMOTE_REBOOT_WAIT $node_id $wait_time
- 设置OET_PATH(mugen框架路径环境变量)之后,就可以在用例所在目录直接执行用例脚本
- 默认超时30m
- 如果某条用例执行超过30m,在用例对TIMEOUT重新赋值
- 远程后台执行命令时,用例执行卡住,导致用例超时失败
- 原因:ssh远程执行命令不会自动退出,会一直等待命令的控制台标准输出,直至命令运行信号结束
- 解决方案:可以将标准输出与标准错误输出重定向到/dev/null,如此ssh就不会一直等待
cmd > /dev/nul 2>&1 &
在对 RISC-V 等非 x86-64 处理器架构上的软件包进行测试时,可以使用 qemu_test.py
脚本调用 QEMU 进行测试。
下载待测试的系统的镜像(.qcow2.zst
文件,需要使用 unzstd
命令解压为 .qcow2
文件)、引导加载程序(fw_payload_oe_uboot_*.bin
)和启动脚本(start_vm.sh
),保存到同一个目录中。
用启动脚本启动虚拟机,使用 Git 克隆 mugen 的 repo 到虚拟机下的一个目录(如 /root/mugen
),再使用 dep_install.sh
脚本安装依赖。如果有需要,可以根据具体情况对虚拟机进行一些修改,比如进行需要网络的测试时需要安装 lshw
包。
将虚拟机准备完成后,使用 poweroff
命令关闭虚拟机,记下 .qcow2
镜像文件的名字。
在物理机(不一定要运行 openEuler)上使用 Git 克隆 mugen 的 repo。这里不需要使用 dep_install.sh
安装依赖。
qemu_test.py
依赖 paramiko
这个 Python 库,可以使用 pip 安装,也可以直接使用操作系统上的包管理器安装。如 Arch Linux 可以使用 pacman 安装 python-paramiko
包。
如果进行网络相关的测试,需要配置好网桥和 TAP 网卡。
首先,安装 bridge-utils
包,使用以下命令添加 br0
网桥:
# brctl addbr br0
再添加 TAP 网卡,需要安装 uml-utilities
包并开启 tun
内核模块。可以使用 tapsetup.sh
脚本批量添加:
$ sudo bash tapsetup.sh 10.198.101.1 50 br0 $(whoami)
其中,10.198.101.1
为网桥的 IP(脚本会自动为 IP 添加 /24
),50
为 TAP 网卡个数,br0
为网桥的网卡名称,最后一个参数为允许使用该 TAP 网卡的用户名,这里使用 $(whoami)
获取当前用户名。
qemu_test.py
脚本的配置文件使用 JSON 格式。以下是一份范例:
{
"workingDir": ".", // 工作目录,即虚拟机文件保存的目录
"bios": "fw_payload_oe_uboot_2304.bin", // 引导加载程序的文件名
"drive": "mugen_ready.qcow2", // 准备好的虚拟机镜像的文件名
"user": "root", // 虚拟机的用户名
"password": "openEuler12#$", // 虚拟机的密码
"threads": 4, // 测试线程数
"cores": 4, // 为虚拟机分配的核心数
"memory": 4, // 为虚拟机分配的内存容量,单位为 GB
"mugenNative": 1, // 是否使用虚拟机上的测试套
"detailed": 1, // 是否在屏幕上打印详细日志
"addDisk": 1, // 添加的磁盘数量
"mugenDir": "/root/mugen", // 在虚拟机中 mugen 所在的目录
"listFile": "lists/list_test", // 需要测试的测试套列表文件
"generate": 1, // 是否将测试套保存到物理机上
"addNic": 1, // 是否添加网卡
"multiMachine": 1, // 是否使用多台机器进行测试
"tap num": 50, // 可以使用的 TAP 网卡数量
"bridge ip": "10.198.101.114" // 用于测试的网桥的 IP
}
需要注意的是,脚本无法处理 JSON 文件的行内注释。为了方便,这里提供一份不含注释的版本:
{
"workingDir": ".",
"bios": "fw_payload_oe_uboot_2304.bin",
"drive": "mugen_ready.qcow2",
"user": "root",
"password": "openEuler12#$",
"threads": 4,
"cores": 4,
"memory": 4,
"mugenNative": 1,
"detailed": 1,
"addDisk": 1,
"mugenDir": "/root/mugen",
"listFile": "lists/list_test",
"generate": 1,
"addNic": 1,
"multiMachine": 1,
"tap num": 50,
"bridge ip": "10.198.101.114"
}
使用以下命令调用脚本,使用 config.json
作为配置文件进行测试:
$ python qemu_test.py -F config.json
在测试刚开始时,脚本会轮询判断虚拟机是否启动,可能会出现 SSH 连接相关的报错,这是正常的。
测试完成后,可以在工作目录下的以下文件夹找到与测试有关的信息:
-
suite2cases_out
:被测试的测试套。 -
exec_log
:测试套在运行时产生的日志。 -
logs
:测试用例在运行时产生的日志。
也可以不使用配置文件,直接使用命令行参数提供配置,参见脚本 --help
参数的输出。
在进行测试的过程中,如果在运行脚本较长时间之后,脚本没有输出测试日志和测试结果,而只是持续输出线程信息(如 Thread 0 is alive
),这个时候可以尝试使用 SSH 连接到配置文件中配置的 SSH 端口(如上文中的 12055
)。也可以使用 top
等工具判断 QEMU 是否正在运行。若均不正常,则说明脚本运行失败。
这个时候可以使用 Ctrl+C 关闭脚本,检查工作目录下的 logs
目录来判断有那些测试套已经测试完毕,再从列表文件中删除已经测试完毕的测试套,最后重新运行脚本开始测试。