libqtest
Run a single test rather than make check
Run a single test:
pip3 install meson
cd build
meson test -t 0 qtest-x86_64/qos-test
加 -t 0
的原因是默认的 timeout 时间是 60s,有时候并不够 qos-test 完成,-t
表示的是 Define a multiplier for test timeout, for example when running tests in particular conditions they might take more time to execute. (<= 0 to disable timeout)。
How to run a specific test in qos-test
All sub-tests in qos-test
are registered by function qos_add_test()
. 如果我们想要只跑一个 subtest,一个方式是把其他的 qos_add_test()
都注释掉,然后只留下我们想跑的那一个 qos_add_test()
。但这种方式工作量是有一点大的。另一种方式是在文件 tests/qtest/qos-test.c
中,我们 apply 下面的 diff 上去(将 strcmp
里的 path 替换成为你希望 test 的 path):
diff --git a/tests/data/acpi/q35/DMAR.dmar b/tests/data/acpi/q35/DMAR.dmar
index 0dca6e68ad..49fd3deb90 100644
Binary files a/tests/data/acpi/q35/DMAR.dmar and b/tests/data/acpi/q35/DMAR.dmar differ
diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c
index 5da4091ec3..ecb8f3e161 100644
--- a/tests/qtest/qos-test.c
+++ b/tests/qtest/qos-test.c
@@ -292,13 +292,15 @@ static void walk_path(QOSGraphNode *orig_path, int len)
path_vec[1] = path_vec[0];
path_vec[0] = g_string_free(cmd_line, false);
- if (path->u.test.subprocess) {
- gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess",
- qtest_get_arch(), path_str);
- qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test);
- g_test_add_data_func(subprocess_path, path_vec, run_one_test);
- } else {
- qtest_add_data_func(path_str, path_vec, run_one_test);
+ if (!strcmp(path_str, "pc/i440FX-pcihost/pci-bus-pc/pci-bus/virtio-net-pci/virtio-net/virtio-net-tests/vhost-user/migrate")) {
+ if (path->u.test.subprocess) {
+ gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess",
+ qtest_get_arch(), path_str);
+ qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test);
+ g_test_add_data_func(subprocess_path, path_vec, run_one_test);
+ } else {
+ qtest_add_data_func(path_str, path_vec, run_one_test);
+ }
}
g_free(path_str);
libqtest.c
struct QTestState
QEMU
这应该就表示一个 QEMU 进程,或者说一个 VM。
struct QTestState
{
// 网络的 fd,应该是迁移相关的。
int fd;
// 通过这个 fd 来执行 qmp 命令
int qmp_fd;
// qemu 进程的 pid
pid_t qemu_pid; /* our child QEMU process */
int wstatus;
//...
int expected_status;
bool big_endian;
bool irq_level[MAX_IRQ];
GString *rx;
QTestTransportOps ops;
// 这是一个 list,里面的每一个元素表示一个 event,是 QDict 类型的
GList *pending_events;
QTestQMPEventCallback eventCB;
void *eventData;
};
qtest_init()
QEMU
QTestState *qtest_init(const char *extra_args)
{
QTestState *s = qtest_init_without_qmp_handshake(extra_args);
QDict *greeting;
// Read the QMP greeting and then do the handshake
greeting = qtest_qmp_receive(s);
qobject_unref(greeting);
qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"));
return s;
}
qtest_memwrite()
QEMU
void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
{
const uint8_t *ptr = data;
size_t i;
char *enc;
//...
enc = g_malloc(2 * size + 1);
for (i = 0; i < size; i++) {
sprintf(&enc[i * 2], "%02x", ptr[i]);
}
// 先发送 command
qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc);
// 处理 command
qtest_rsp(s);
g_free(enc);
}
qtest_rsp()
QEMU
好像没做什么,就是返回了 command 的 word。
static void qtest_rsp(QTestState *s)
{
gchar **words = qtest_rsp_args(s, 0);
g_strfreev(words);
}
QTest command send and receive
struct QTestClientTransportOps
QEMU
typedef struct QTestClientTransportOps {
// qtest_sendf / qtest_bufwrite
QTestSendFn send; /* for sending qtest commands */
/*
* use external_send to send qtest command strings through functions which
* do not accept a QTestState as the first parameter.
*/
//
ExternalSendFn external_send;
// qtest_rsp_args() // 这个函数中的 rsp 叫做 response
// line = s->ops.recv_line(s);
QTestRecvFn recv_line; /* for receiving qtest command responses */
} QTestTransportOps;
可以看到发送和接收的函数都是传进来的函数。
static void qtest_client_set_tx_handler(QTestState *s, QTestSendFn send)
{
s->ops.send = send;
}
static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv)
{
s->ops.recv_line = recv;
}
发送和接收有两种方式:process 内部直接发送(inproc)和通过 socket。
qtest_client_inproc_recv_line()
/ send_wrapper()
/ qtest_server_inproc_recv()
QEMU
Send 函数是 qtest_server_inproc_recv()
,receive 函数是 qtest_client_inproc_recv_line()
。
// tests/qtest/fuzz/fuzz.c
qtest_setup
qtest_inproc_init(&fuzz_qts, false, fuzz_arch, &qtest_server_inproc_recv);
qts->ops.external_send = send;
qtest_client_set_tx_handler(qts, send_wrapper);
// 可见虽然叫做 recv,其实是 send 函数
// 可能是因为 send 之后就直接开始 receive 然后开始处理了
void qtest_server_inproc_recv(void *dummy, const char *buf)
{
// 这是一个 static 的,说明每次调用都会往后面 append string
static GString *gstr;
if (!gstr) {
gstr = g_string_new(NULL);
}
// 再 append 一些 string,直到最后一个字符是 '\n'
g_string_append(gstr, buf);
// 如果最后一个字符是 '\n'
if (gstr->str[gstr->len - 1] == '\n') {
qtest_process_inbuf(NULL, gstr);
g_string_truncate(gstr, 0);
}
}
qtest_setup
qtest_inproc_init
qtest_client_set_rx_handler(qts, qtest_client_inproc_recv_line);
// 这个函数基于的还是 s->rx-str 这个 string
static GString *qtest_client_inproc_recv_line(QTestState *s)
{
GString *line;
size_t offset;
char *eol;
eol = strchr(s->rx->str, '\n');
offset = eol - s->rx->str;
// 从 '\n' 处截取一个 command line
line = g_string_new_len(s->rx->str, offset);
// 把截取之前的部分清空掉
g_string_erase(s->rx, 0, offset + 1);
return line;
}
qtest_process_inbuf()
/ qtest_process_command()
QEMU
static void qtest_process_inbuf(CharBackend *chr, GString *inbuf)
{
char *end;
// Returns a pointer to the first occurrence of the character '\n'
while ((end = strchr(inbuf->str, '\n')) != NULL) {
size_t offset;
GString *cmd;
gchar **words;
offset = end - inbuf->str;
// cmd 是一个 string,通过这个 offset 得到
cmd = g_string_new_len(inbuf->str, offset);
// 清空 [0, offset] 内容
g_string_erase(inbuf, 0, offset + 1);
// 把这个 command 按照空格分成不同的词
words = g_strsplit(cmd->str, " ", 0);
qtest_process_command(chr, words);
//...
}
}
qtest_add_data_func_full()
QEMU
void qtest_add_data_func_full(const char *str, void *data,
void (*fn)(const void *),
GDestroyNotify data_free_func)
{
gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str);
g_test_add_data_func_full(path, data, fn, data_free_func);
g_free(path);
}
🗞️ Recent Posts