tests/qtest: defer connection

Add an option to defer making the connecting to the monitor and qtest
sockets when calling qtest_init_with_env.  The client makes the connection
later by calling qtest_connect and qtest_qmp_handshake.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/r/1736967650-129648-20-git-send-email-steven.sistare@oracle.com
[plumb capabilities list into qtest_qmp_handshake]
Signed-off-by: Fabiano Rosas <farosas@suse.de>
This commit is contained in:
Steve Sistare 2025-01-15 11:00:45 -08:00 committed by Fabiano Rosas
parent 903a65120d
commit 5357ef823a
3 changed files with 91 additions and 41 deletions

View File

@ -75,6 +75,8 @@ struct QTestState
{ {
int fd; int fd;
int qmp_fd; int qmp_fd;
int sock;
int qmpsock;
pid_t qemu_pid; /* our child QEMU process */ pid_t qemu_pid; /* our child QEMU process */
int wstatus; int wstatus;
#ifdef _WIN32 #ifdef _WIN32
@ -458,18 +460,19 @@ static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin,
return s; return s;
} }
static char *qtest_socket_path(const char *suffix)
{
return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix);
}
static QTestState *qtest_init_internal(const char *qemu_bin, static QTestState *qtest_init_internal(const char *qemu_bin,
const char *extra_args) const char *extra_args,
bool do_connect)
{ {
QTestState *s; QTestState *s;
int sock, qmpsock, i; int sock, qmpsock, i;
gchar *socket_path; g_autofree gchar *socket_path = qtest_socket_path("sock");
gchar *qmp_socket_path; g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp");
socket_path = g_strdup_printf("%s/qtest-%d.sock",
g_get_tmp_dir(), getpid());
qmp_socket_path = g_strdup_printf("%s/qtest-%d.qmp",
g_get_tmp_dir(), getpid());
/* /*
* It's possible that if an earlier test run crashed it might * It's possible that if an earlier test run crashed it might
@ -501,22 +504,19 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
qtest_client_set_tx_handler(s, qtest_client_socket_send); qtest_client_set_tx_handler(s, qtest_client_socket_send);
s->fd = socket_accept(sock);
if (s->fd >= 0) {
s->qmp_fd = socket_accept(qmpsock);
}
unlink(socket_path);
unlink(qmp_socket_path);
g_free(socket_path);
g_free(qmp_socket_path);
g_assert(s->fd >= 0 && s->qmp_fd >= 0);
s->rx = g_string_new(""); s->rx = g_string_new("");
for (i = 0; i < MAX_IRQ; i++) { for (i = 0; i < MAX_IRQ; i++) {
s->irq_level[i] = false; s->irq_level[i] = false;
} }
s->fd = -1;
s->qmp_fd = -1;
s->sock = sock;
s->qmpsock = qmpsock;
if (do_connect) {
qtest_connect(s);
}
/* /*
* Stopping QEMU for debugging is not supported on Windows. * Stopping QEMU for debugging is not supported on Windows.
* *
@ -531,28 +531,38 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
} }
#endif #endif
/* ask endianness of the target */
s->big_endian = qtest_query_target_endianness(s);
return s; return s;
} }
void qtest_connect(QTestState *s)
{
g_autofree gchar *socket_path = qtest_socket_path("sock");
g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp");
g_assert(s->sock >= 0 && s->qmpsock >= 0);
s->fd = socket_accept(s->sock);
if (s->fd >= 0) {
s->qmp_fd = socket_accept(s->qmpsock);
}
unlink(socket_path);
unlink(qmp_socket_path);
g_assert(s->fd >= 0 && s->qmp_fd >= 0);
s->sock = s->qmpsock = -1;
/* ask endianness of the target */
s->big_endian = qtest_query_target_endianness(s);
}
QTestState *qtest_init_without_qmp_handshake(const char *extra_args) QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
{ {
return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true);
} }
QTestState *qtest_init_with_env_and_capabilities(const char *var, void qtest_qmp_handshake(QTestState *s, QList *capabilities)
const char *extra_args,
QList *capabilities)
{ {
QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args);
QDict *greeting;
/* Read the QMP greeting and then do the handshake */ /* Read the QMP greeting and then do the handshake */
greeting = qtest_qmp_receive(s); QDict *greeting = qtest_qmp_receive(s);
qobject_unref(greeting); qobject_unref(greeting);
if (capabilities) { if (capabilities) {
qtest_qmp_assert_success(s, qtest_qmp_assert_success(s,
"{ 'execute': 'qmp_capabilities', " "{ 'execute': 'qmp_capabilities', "
@ -561,18 +571,37 @@ QTestState *qtest_init_with_env_and_capabilities(const char *var,
} else { } else {
qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }"); qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }");
} }
}
QTestState *qtest_init_with_env_and_capabilities(const char *var,
const char *extra_args,
QList *capabilities,
bool do_connect)
{
QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args,
do_connect);
if (do_connect) {
qtest_qmp_handshake(s, capabilities);
} else {
/*
* If the connection is delayed, the capabilities must be set
* at that moment.
*/
assert(!capabilities);
}
return s; return s;
} }
QTestState *qtest_init_with_env(const char *var, const char *extra_args) QTestState *qtest_init_with_env(const char *var, const char *extra_args,
bool do_connect)
{ {
return qtest_init_with_env_and_capabilities(var, extra_args, NULL); return qtest_init_with_env_and_capabilities(var, extra_args, NULL, true);
} }
QTestState *qtest_init(const char *extra_args) QTestState *qtest_init(const char *extra_args)
{ {
return qtest_init_with_env(NULL, extra_args); return qtest_init_with_env(NULL, extra_args, true);
} }
QTestState *qtest_vinitf(const char *fmt, va_list ap) QTestState *qtest_vinitf(const char *fmt, va_list ap)
@ -1580,7 +1609,7 @@ static struct MachInfo *qtest_get_machines(const char *var)
silence_spawn_log = !g_test_verbose(); silence_spawn_log = !g_test_verbose();
qts = qtest_init_with_env(qemu_var, "-machine none"); qts = qtest_init_with_env(qemu_var, "-machine none", true);
response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }");
g_assert(response); g_assert(response);
list = qdict_get_qlist(response, "return"); list = qdict_get_qlist(response, "return");
@ -1635,7 +1664,7 @@ static struct CpuModel *qtest_get_cpu_models(void)
silence_spawn_log = !g_test_verbose(); silence_spawn_log = !g_test_verbose();
qts = qtest_init_with_env(NULL, "-machine none"); qts = qtest_init_with_env(NULL, "-machine none", true);
response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }"); response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }");
g_assert(response); g_assert(response);
list = qdict_get_qlist(response, "return"); list = qdict_get_qlist(response, "return");

View File

@ -61,13 +61,15 @@ QTestState *qtest_init(const char *extra_args);
* @var: Environment variable from where to take the QEMU binary * @var: Environment variable from where to take the QEMU binary
* @extra_args: Other arguments to pass to QEMU. CAUTION: these * @extra_args: Other arguments to pass to QEMU. CAUTION: these
* arguments are subject to word splitting and shell evaluation. * arguments are subject to word splitting and shell evaluation.
* @do_connect: connect to qemu monitor and qtest socket.
* *
* Like qtest_init(), but use a different environment variable for the * Like qtest_init(), but use a different environment variable for the
* QEMU binary. * QEMU binary.
* *
* Returns: #QTestState instance. * Returns: #QTestState instance.
*/ */
QTestState *qtest_init_with_env(const char *var, const char *extra_args); QTestState *qtest_init_with_env(const char *var, const char *extra_args,
bool do_connect);
/** /**
* qtest_init_with_env_and_capabilities: * qtest_init_with_env_and_capabilities:
@ -75,6 +77,7 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args);
* @extra_args: Other arguments to pass to QEMU. CAUTION: these * @extra_args: Other arguments to pass to QEMU. CAUTION: these
* arguments are subject to word splitting and shell evaluation. * arguments are subject to word splitting and shell evaluation.
* @capabilities: list of QMP capabilities (strings) to enable * @capabilities: list of QMP capabilities (strings) to enable
* @do_connect: connect to qemu monitor and qtest socket.
* *
* Like qtest_init_with_env(), but enable specified capabilities during * Like qtest_init_with_env(), but enable specified capabilities during
* hadshake. * hadshake.
@ -83,7 +86,8 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args);
*/ */
QTestState *qtest_init_with_env_and_capabilities(const char *var, QTestState *qtest_init_with_env_and_capabilities(const char *var,
const char *extra_args, const char *extra_args,
QList *capabilities); QList *capabilities,
bool do_connect);
/** /**
* qtest_init_without_qmp_handshake: * qtest_init_without_qmp_handshake:
@ -94,6 +98,22 @@ QTestState *qtest_init_with_env_and_capabilities(const char *var,
*/ */
QTestState *qtest_init_without_qmp_handshake(const char *extra_args); QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
/**
* qtest_connect
* @s: #QTestState instance to connect
* Connect to qemu monitor and qtest socket, after skipping them in
* qtest_init_with_env. Does not handshake with the monitor.
*/
void qtest_connect(QTestState *s);
/**
* qtest_qmp_handshake:
* @s: #QTestState instance to operate on.
* @capabilities: list of QMP capabilities (strings) to enable
* Perform handshake after connecting to qemu monitor.
*/
void qtest_qmp_handshake(QTestState *s, QList *capabilities);
/** /**
* qtest_init_with_serial: * qtest_init_with_serial:
* @extra_args: other arguments to pass to QEMU. CAUTION: these * @extra_args: other arguments to pass to QEMU. CAUTION: these

View File

@ -196,9 +196,10 @@ static void cleanup(const char *filename)
static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args) static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args)
{ {
QList *capabilities = qlist_new(); QList *capabilities = NULL;
if (args->oob) { if (args->oob) {
capabilities = qlist_new();
qlist_append_str(capabilities, "oob"); qlist_append_str(capabilities, "oob");
} }
return capabilities; return capabilities;
@ -333,7 +334,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
ignore_stderr); ignore_stderr);
if (!args->only_target) { if (!args->only_target) {
*from = qtest_init_with_env_and_capabilities(QEMU_ENV_SRC, cmd_source, *from = qtest_init_with_env_and_capabilities(QEMU_ENV_SRC, cmd_source,
capabilities); capabilities, true);
qtest_qmp_set_event_callback(*from, qtest_qmp_set_event_callback(*from,
migrate_watch_for_events, migrate_watch_for_events,
&src_state); &src_state);
@ -354,7 +355,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
args->opts_target ? args->opts_target : "", args->opts_target ? args->opts_target : "",
ignore_stderr); ignore_stderr);
*to = qtest_init_with_env_and_capabilities(QEMU_ENV_DST, cmd_target, *to = qtest_init_with_env_and_capabilities(QEMU_ENV_DST, cmd_target,
capabilities); capabilities, true);
qtest_qmp_set_event_callback(*to, qtest_qmp_set_event_callback(*to,
migrate_watch_for_events, migrate_watch_for_events,
&dst_state); &dst_state);