#include <sys/wait.h>
#include <sys/un.h>
+#include "qapi/error.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/qjson.h"
g_hook_prepend(&abrt_hooks, hook);
}
-QTestState *qtest_init(const char *extra_args)
+QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
{
QTestState *s;
int sock, qmpsock, i;
const char *qemu_binary;
qemu_binary = getenv("QTEST_QEMU_BINARY");
- g_assert(qemu_binary != NULL);
+ if (!qemu_binary) {
+ fprintf(stderr, "Environment variable QTEST_QEMU_BINARY required\n");
+ exit(1);
+ }
s = g_malloc(sizeof(*s));
socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
+ /* It's possible that if an earlier test run crashed it might
+ * have left a stale unix socket lying around. Delete any
+ * stale old socket to avoid spurious test failures with
+ * tests/libqtest.c:70:init_socket: assertion failed (ret != -1): (-1 != -1)
+ */
+ unlink(socket_path);
+ unlink(qmp_socket_path);
+
sock = init_socket(socket_path);
qmpsock = init_socket(qmp_socket_path);
s->irq_level[i] = false;
}
- /* Read the QMP greeting and then do the handshake */
- qtest_qmp_discard_response(s, "");
- qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }");
-
if (getenv("QTEST_STOP")) {
kill(s->qemu_pid, SIGSTOP);
}
return s;
}
+QTestState *qtest_init(const char *extra_args)
+{
+ QTestState *s = qtest_init_without_qmp_handshake(extra_args);
+
+ /* Read the QMP greeting and then do the handshake */
+ qtest_qmp_discard_response(s, "");
+ qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }");
+
+ return s;
+}
+
void qtest_quit(QTestState *s)
{
qtest_instances = g_list_remove(qtest_instances, s);
va_list ap_copy;
QObject *qobj;
+ /* qobject_from_jsonv() silently eats leading 0xff as invalid
+ * JSON, but we want to test sending them over the wire to force
+ * resyncs */
+ if (*fmt == '\377') {
+ socket_send(fd, fmt, 1);
+ fmt++;
+ }
+
/* Going through qobject ensures we escape strings properly.
* This seemingly unnecessary copy is required in case va_list
* is an array type.
*/
va_copy(ap_copy, ap);
- qobj = qobject_from_jsonv(fmt, &ap_copy);
+ qobj = qobject_from_jsonv(fmt, &ap_copy, &error_abort);
va_end(ap_copy);
/* No need to send anything for an empty QObject. */
if (qobj) {
int log = getenv("QTEST_LOG") != NULL;
QString *qstr = qobject_to_json(qobj);
- const char *str = qstring_get_str(qstr);
- size_t size = qstring_get_length(qstr);
+ const char *str;
+
+ /*
+ * BUG: QMP doesn't react to input until it sees a newline, an
+ * object, or an array. Work-around: give it a newline.
+ */
+ qstring_append_chr(qstr, '\n');
+ str = qstring_get_str(qstr);
if (log) {
fprintf(stderr, "%s", str);
}
/* Send QMP request */
- socket_send(fd, str, size);
+ socket_send(fd, str, qstring_get_length(qstr));
QDECREF(qstr);
qobject_decref(qobj);
" 'arguments': {'command-line': %s}}",
cmd);
ret = g_strdup(qdict_get_try_str(resp, "return"));
+ while (ret == NULL && qdict_get_try_str(resp, "event")) {
+ /* Ignore asynchronous QMP events */
+ QDECREF(resp);
+ resp = qtest_qmp_receive(s);
+ ret = g_strdup(qdict_get_try_str(resp, "return"));
+ }
g_assert(ret);
QDECREF(resp);
g_free(cmd);
GDestroyNotify data_free_func)
{
gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str);
-#if GLIB_CHECK_VERSION(2, 34, 0)
g_test_add_data_func_full(path, data, fn, data_free_func);
-#elif GLIB_CHECK_VERSION(2, 26, 0)
- /* back-compat casts, remove this once we can require new-enough glib */
- g_test_add_vtable(path, 0, data, NULL,
- (GTestFixtureFunc)fn, (GTestFixtureFunc) data_free_func);
-#else
- /* back-compat casts, remove this once we can require new-enough glib */
- g_test_add_vtable(path, 0, data, NULL,
- (void (*)(void)) fn, (void (*)(void)) data_free_func);
-#endif
g_free(path);
}
{
return s->big_endian;
}
+
+void qtest_cb_for_every_machine(void (*cb)(const char *machine))
+{
+ QDict *response, *minfo;
+ QList *list;
+ const QListEntry *p;
+ QObject *qobj;
+ QString *qstr;
+ const char *mname;
+
+ qtest_start("-machine none");
+ response = qmp("{ 'execute': 'query-machines' }");
+ g_assert(response);
+ list = qdict_get_qlist(response, "return");
+ g_assert(list);
+
+ for (p = qlist_first(list); p; p = qlist_next(p)) {
+ minfo = qobject_to_qdict(qlist_entry_obj(p));
+ g_assert(minfo);
+ qobj = qdict_get(minfo, "name");
+ g_assert(qobj);
+ qstr = qobject_to_qstring(qobj);
+ g_assert(qstr);
+ mname = qstring_get_str(qstr);
+ cb(mname);
+ }
+
+ qtest_end();
+ QDECREF(response);
+}