#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"
bool irq_level[MAX_IRQ];
GString *rx;
pid_t qemu_pid; /* our child QEMU process */
+ bool big_endian;
};
static GHookList abrt_hooks;
g_assert_cmpint(ret, !=, -1); \
} while (0)
+static int qtest_query_target_endianness(QTestState *s);
+
static int init_socket(const char *socket_path)
{
struct sockaddr_un addr;
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);
}
+ /* ask endianness of the target */
+
+ s->big_endian = qtest_query_target_endianness(s);
+
+ 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;
}
return words;
}
+static int qtest_query_target_endianness(QTestState *s)
+{
+ gchar **args;
+ int big_endian;
+
+ qtest_sendf(s, "endianness\n");
+ args = qtest_rsp(s, 1);
+ g_assert(strcmp(args[1], "big") == 0 || strcmp(args[1], "little") == 0);
+ big_endian = strcmp(args[1], "big") == 0;
+ g_strfreev(args);
+
+ return big_endian;
+}
+
typedef struct {
JSONMessageParser parser;
QDict *response;
exit(1);
}
- g_assert(qobject_type(obj) == QTYPE_QDICT);
g_assert(!qmp->response);
- qmp->response = (QDict *)obj;
+ qmp->response = qobject_to_qdict(obj);
+ g_assert(qmp->response);
}
QDict *qmp_fd_receive(int fd)
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);
QDECREF(response);
}
-void qtest_qmp_eventwait(QTestState *s, const char *event)
+QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event)
{
QDict *response;
response = qtest_qmp_receive(s);
if ((qdict_haskey(response, "event")) &&
(strcmp(qdict_get_str(response, "event"), event) == 0)) {
- QDECREF(response);
- break;
+ return response;
}
QDECREF(response);
}
}
+void qtest_qmp_eventwait(QTestState *s, const char *event)
+{
+ QDict *response;
+
+ response = qtest_qmp_eventwait_ref(s, event);
+ QDECREF(response);
+}
+
char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap)
{
char *cmd;
" '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);
gchar **args;
size_t i;
+ if (!size) {
+ return;
+ }
+
qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size);
args = qtest_rsp(s, 2);
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);
}
{
const uint8_t *ptr = data;
size_t i;
- char *enc = g_malloc(2 * size + 1);
+ char *enc;
+
+ if (!size) {
+ return;
+ }
+
+ enc = g_malloc(2 * size + 1);
for (i = 0; i < size; i++) {
sprintf(&enc[i * 2], "%02x", ptr[i]);
return ret;
}
-bool qtest_big_endian(void)
+bool qtest_big_endian(QTestState *s)
{
- const char *arch = qtest_get_arch();
- int i;
-
- static const struct {
- const char *arch;
- bool big_endian;
- } endianness[] = {
- { "aarch64", false },
- { "alpha", false },
- { "arm", false },
- { "cris", false },
- { "i386", false },
- { "lm32", true },
- { "m68k", true },
- { "microblaze", true },
- { "microblazeel", false },
- { "mips", true },
- { "mips64", true },
- { "mips64el", false },
- { "mipsel", false },
- { "moxie", true },
- { "or32", true },
- { "ppc", true },
- { "ppc64", true },
- { "ppcemb", true },
- { "s390x", true },
- { "sh4", false },
- { "sh4eb", true },
- { "sparc", true },
- { "sparc64", true },
- { "unicore32", false },
- { "x86_64", false },
- { "xtensa", false },
- { "xtensaeb", true },
- {},
- };
+ return s->big_endian;
+}
- for (i = 0; endianness[i].arch; i++) {
- if (strcmp(endianness[i].arch, arch) == 0) {
- return endianness[i].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);
}
- return false;
+ qtest_end();
+ QDECREF(response);
}