Linux C轻量级的白盒测试方法 (shell + c + signal)
简介
- 目的:当我们写好一个程序,往往需要测试及调试以保证功能的正确性,有时借助gtest等单元测试工具进行测试,但也可以自己设计一种简单轻量化的白盒测试方法去做程序逻辑的测试或调试。
- 分析:这其中的关键是将各个触发不同逻辑的参数传入逻辑代码,触发不同的业务执行,观测结果。
- 设计:shell + c + signal, shell脚本将各个参数(参数列表)接收,由于linux下 pkill -USR1 process_name的信号发送命令不方便携带多参数场景,这里写入文件,并通过信号(用户信号USR1)通知c进程,C进程拿到信号后解析文件(文件内容为参数列表),解析出各个参数即可。之后便可根据各个参数做不同的逻辑测试执行逻辑。
源码
- 信号发送脚本 send_signal.sh
bash
#!/bin/bash
#if [ $# -lt 2 ]; then
if [ $# -lt 1 ]; then
# echo "使用方法: $0 <id> <string1> [<string2> ...]"
echo "使用方法: $0 <param1> <param2> [<param3> ...]"
exit 1
fi
#ID=$1
#echo "ID: $ID"
#shift
# 创建一个文件,用于存储字符串参数
OUTPUT_FILE="./signal_params.txt"
echo -n "" > $OUTPUT_FILE
# 写入每个字符串参数
for param in "$@"
do
echo "$param" >> $OUTPUT_FILE
done
# 获取目标 C 程序的进程 ID
TARGET_PID=$(pgrep -f signal_handler)
if [ -z "$TARGET_PID" ]; then
echo "找不到目标程序的进程"
exit 1
fi
echo "发送信号 SIGUSR1 到进程 ID: $TARGET_PID,参数值: $@"
sync
sleep 0.5
# 发送 SIGUSR1 信号到目标进程
kill -SIGUSR1 $TARGET_PID
echo "已发送信号 SIGUSR1 到进程 ID: $TARGET_PID,参数值: $@"
- 信号接收c进程 signal_handler.c
c
/* signal_handler.c */
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_PARAMS 100
#define MAX_PARAM_LENGTH 256
#define SIGNAL_PARAMS_FILE_PATH "./signal_params.txt"
// 全局变量,用于存储字符串参数
static char *global_params[MAX_PARAMS];
static uint16_t global_params_count = 0;
void SignalHandler(int signal)
{
FILE *file = fopen(SIGNAL_PARAMS_FILE_PATH, "r");
if (file == NULL)
{
perror("无法打开参数文件");
return;
}
// 清空之前的参数
for (uint16_t i = 0; i < global_params_count; i++)
{
free(global_params[i]);
}
global_params_count = 0;
// 读取每个字符串参数KF
char buffer[MAX_PARAM_LENGTH];
while (fgets(buffer, sizeof(buffer), file) != NULL)
{
buffer[strcspn(buffer, "\n")] = '\0'; // 移除换行符
global_params[global_params_count] = strdup(buffer);
global_params_count++;
if (global_params_count >= MAX_PARAMS)
{
printf("signal count[%d] out of range[%d]\n", global_params_count, MAX_PARAMS);
break;
}
}
fclose(file);
// 打印接收到的参数
printf("信号[%d]处理程序被调用\n", signal);
printf("接收到的参数个数: %u\n", global_params_count);
for (uint16_t i = 0; i < global_params_count; i++)
{
printf("参数%u: %s\n", i + 1, global_params[i]);
}
}
static void SignalRegister(void)
{
// 绑定信号处理程序到 SIGUSR1 信号
signal(SIGUSR1, SignalHandler);
// 获取并打印当前进程 ID
pid_t pid = getpid();
printf("程序已启动,进程 ID: %d\n", pid);
// 主循环,等待信号
while (1)
{
pause(); // 等待信号
}
// 清理分配的内存
for (uint16_t i = 0; i < global_params_count; i++)
{
free(global_params[i]);
}
}
int main()
{
SignalRegister();
return 0;
}
用法
- 编译并启动c进程等待信号(这里用用户信号)
bash
gcc signal_handler.c -o signal_handler
./signal_handler &
[1] 3508
程序已启动,进程 ID: 3508
- 脚本接收参数并发送信号和参数
bash
chmod +x send_signal.sh
./send_signal.sh
使用方法: ./send_signal.sh <param1> <param2> [<param3> ...]
- 需要带参数,效果:
bash
./send_signal.sh 1 2 3.14 key value
发送信号 SIGUSR1 到进程 ID: 3508,参数值: 1 2 3.14 key value
已发送信号 SIGUSR1 到进程 ID: 3508,参数值: 1 2 3.14 key value
信号处理程序被调用: signal 10
接收到的参数个数: 5
参数1: 1
参数2: 2
参数3: 3.14
参数4: key
参数5: value
- 文件内容 signal_params.txt
bash
1
2
3.14
key
value
- 注 : 后续对参数拿到后的使用略,此为简易方案,效果仅供参考