aboutsummaryrefslogtreecommitdiff
path: root/tests/qtest/libqtest.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/qtest/libqtest.c')
-rw-r--r--tests/qtest/libqtest.c271
1 files changed, 183 insertions, 88 deletions
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 9d07de1..94526b7 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -30,14 +30,15 @@
#include "libqtest.h"
#include "libqmp.h"
+#include "qemu/accel.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/sockets.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qjson.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qbool.h"
+#include "qobject/qdict.h"
+#include "qobject/qjson.h"
+#include "qobject/qlist.h"
+#include "qobject/qstring.h"
+#include "qobject/qbool.h"
#define MAX_IRQ 256
@@ -75,6 +76,8 @@ struct QTestState
{
int fd;
int qmp_fd;
+ int sock;
+ int qmpsock;
pid_t qemu_pid; /* our child QEMU process */
int wstatus;
#ifdef _WIN32
@@ -215,6 +218,22 @@ static void qtest_check_status(QTestState *s)
#endif
}
+void qtest_system_reset_nowait(QTestState *s)
+{
+ /* Request the system reset, but do not wait for it to complete */
+ qtest_qmp_assert_success(s, "{'execute': 'system_reset' }");
+}
+
+void qtest_system_reset(QTestState *s)
+{
+ qtest_system_reset_nowait(s);
+ /*
+ * Wait for the RESET event, which is sent once the system reset
+ * has actually completed.
+ */
+ qtest_qmp_eventwait(s, "RESET");
+}
+
void qtest_wait_qemu(QTestState *s)
{
if (s->qemu_pid != -1) {
@@ -442,18 +461,19 @@ static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin,
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,
- const char *extra_args)
+ const char *extra_args,
+ bool do_connect)
{
QTestState *s;
int sock, qmpsock, i;
- gchar *socket_path;
- gchar *qmp_socket_path;
-
- 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());
+ g_autofree gchar *socket_path = qtest_socket_path("sock");
+ g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp");
/*
* It's possible that if an earlier test run crashed it might
@@ -485,22 +505,19 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
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("");
for (i = 0; i < MAX_IRQ; i++) {
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.
*
@@ -515,34 +532,69 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
}
#endif
- /* ask endianness of the target */
+ return s;
+}
- s->big_endian = qtest_query_target_endianness(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");
- return s;
+ 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)
{
- 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(const char *var, const char *extra_args)
+void qtest_qmp_handshake(QTestState *s, QList *capabilities)
{
- QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args);
- QDict *greeting;
-
/* 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(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"));
+ if (capabilities) {
+ qtest_qmp_assert_success(s,
+ "{ 'execute': 'qmp_capabilities', "
+ "'arguments': { 'enable': %p } }",
+ qobject_ref(capabilities));
+ } else {
+ qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }");
+ }
+}
+
+QTestState *qtest_init_ext(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;
}
QTestState *qtest_init(const char *extra_args)
{
- return qtest_init_with_env(NULL, extra_args);
+ return qtest_init_ext(NULL, extra_args, NULL, true);
}
QTestState *qtest_vinitf(const char *fmt, va_list ap)
@@ -752,6 +804,7 @@ QDict *qtest_qmp_receive(QTestState *s)
QDict *qtest_qmp_receive_dict(QTestState *s)
{
+ g_assert(s->qmp_fd >= 0);
return qmp_fd_receive(s->qmp_fd);
}
@@ -779,12 +832,14 @@ int qtest_socket_server(const char *socket_path)
void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num,
const char *fmt, va_list ap)
{
+ g_assert(s->qmp_fd >= 0);
qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap);
}
#endif
void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap)
{
+ g_assert(s->qmp_fd >= 0);
qmp_fd_vsend(s->qmp_fd, fmt, ap);
}
@@ -845,6 +900,7 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
{
va_list ap;
+ g_assert(s->qmp_fd >= 0);
va_start(ap, fmt);
qmp_fd_vsend_raw(s->qmp_fd, fmt, ap);
va_end(ap);
@@ -948,15 +1004,62 @@ const char *qtest_get_arch(void)
return end + 1;
}
+static bool qtest_qom_has_concrete_type(const char *parent_typename,
+ const char *child_typename,
+ QList **cached_list)
+{
+ QList *list = cached_list ? *cached_list : NULL;
+ const QListEntry *p;
+ QObject *qobj;
+ QString *qstr;
+ QDict *devinfo;
+ int idx;
+
+ if (!list) {
+ QDict *resp;
+ QDict *args;
+ QTestState *qts = qtest_init("-machine none");
+
+ args = qdict_new();
+ qdict_put_bool(args, "abstract", false);
+ qdict_put_str(args, "implements", parent_typename);
+
+ resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }",
+ args);
+ g_assert(qdict_haskey(resp, "return"));
+ list = qdict_get_qlist(resp, "return");
+ qobject_ref(list);
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+
+ if (cached_list) {
+ *cached_list = list;
+ }
+ }
+
+ for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) {
+ devinfo = qobject_to(QDict, qlist_entry_obj(p));
+ g_assert(devinfo);
+
+ qobj = qdict_get(devinfo, "name");
+ g_assert(qobj);
+ qstr = qobject_to(QString, qobj);
+ g_assert(qstr);
+ if (g_str_equal(qstring_get_str(qstr), child_typename)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool qtest_has_accel(const char *accel_name)
{
- if (g_str_equal(accel_name, "tcg")) {
-#if defined(CONFIG_TCG)
- return true;
-#else
- return false;
-#endif
- } else if (g_str_equal(accel_name, "kvm")) {
+ static QList *list;
+ g_autofree char *accel_type = NULL;
+
+ if (g_str_equal(accel_name, "kvm")) {
int i;
const char *arch = qtest_get_arch();
const char *targets[] = { CONFIG_KVM_TARGETS };
@@ -968,11 +1071,12 @@ bool qtest_has_accel(const char *accel_name)
}
}
}
- } else {
- /* not implemented */
- g_assert_not_reached();
+ return false;
}
- return false;
+
+ accel_type = g_strconcat(accel_name, ACCEL_CLASS_SUFFIX, NULL);
+
+ return qtest_qom_has_concrete_type("accel", accel_type, &list);
}
bool qtest_get_irq(QTestState *s, int num)
@@ -1202,6 +1306,33 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name,
return 0;
}
+static void qtest_rsp_csr(QTestState *s, uint64_t *val)
+{
+ gchar **args;
+ uint64_t ret;
+ int rc;
+
+ args = qtest_rsp_args(s, 3);
+
+ rc = qemu_strtou64(args[1], NULL, 16, &ret);
+ g_assert(rc == 0);
+ rc = qemu_strtou64(args[2], NULL, 16, val);
+ g_assert(rc == 0);
+
+ g_strfreev(args);
+}
+
+uint64_t qtest_csr_call(QTestState *s, const char *name,
+ uint64_t cpu, int csr,
+ uint64_t *val)
+{
+ qtest_sendf(s, "csr %s 0x%"PRIx64" %d 0x%"PRIx64"\n",
+ name, cpu, csr, *val);
+
+ qtest_rsp_csr(s, val);
+ return 0;
+}
+
void qtest_add_func(const char *str, void (*fn)(void))
{
gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str);
@@ -1523,7 +1654,7 @@ static struct MachInfo *qtest_get_machines(const char *var)
silence_spawn_log = !g_test_verbose();
- qts = qtest_init_with_env(qemu_var, "-machine none");
+ qts = qtest_init_ext(qemu_var, "-machine none", NULL, true);
response = qtest_qmp(qts, "{ 'execute': 'query-machines' }");
g_assert(response);
list = qdict_get_qlist(response, "return");
@@ -1578,7 +1709,7 @@ static struct CpuModel *qtest_get_cpu_models(void)
silence_spawn_log = !g_test_verbose();
- qts = qtest_init_with_env(NULL, "-machine none");
+ qts = qtest_init_ext(NULL, "-machine none", NULL, true);
response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }");
g_assert(response);
list = qdict_get_qlist(response, "return");
@@ -1648,7 +1779,9 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine),
/* Ignore machines that cannot be used for qtests */
if (!strncmp("xenfv", machines[i].name, 5) ||
g_str_equal("xenpv", machines[i].name) ||
- g_str_equal("xenpvh", machines[i].name)) {
+ g_str_equal("xenpvh", machines[i].name) ||
+ g_str_equal("vmapple", machines[i].name) ||
+ g_str_equal("nitro-enclave", machines[i].name)) {
continue;
}
if (!skip_old_versioned ||
@@ -1699,45 +1832,8 @@ bool qtest_has_machine(const char *machine)
bool qtest_has_device(const char *device)
{
static QList *list;
- const QListEntry *p;
- QObject *qobj;
- QString *qstr;
- QDict *devinfo;
- int idx;
- if (!list) {
- QDict *resp;
- QDict *args;
- QTestState *qts = qtest_init("-machine none");
-
- args = qdict_new();
- qdict_put_bool(args, "abstract", false);
- qdict_put_str(args, "implements", "device");
-
- resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }",
- args);
- g_assert(qdict_haskey(resp, "return"));
- list = qdict_get_qlist(resp, "return");
- qobject_ref(list);
- qobject_unref(resp);
-
- qtest_quit(qts);
- }
-
- for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) {
- devinfo = qobject_to(QDict, qlist_entry_obj(p));
- g_assert(devinfo);
-
- qobj = qdict_get(devinfo, "name");
- g_assert(qobj);
- qstr = qobject_to(QString, qobj);
- g_assert(qstr);
- if (g_str_equal(qstring_get_str(qstr), device)) {
- return true;
- }
- }
-
- return false;
+ return qtest_qom_has_concrete_type("device", device, &list);
}
/*
@@ -1918,7 +2014,6 @@ void qtest_client_inproc_recv(void *opaque, const char *str)
qts->rx = g_string_new(NULL);
}
g_string_append(qts->rx, str);
- return;
}
void qtest_qom_set_bool(QTestState *s, const char *path, const char *property,