@@ -1243,6 +1243,28 @@ void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size)
qtest_rsp(s);
}
+QDict *qtest_vqmp_assert_failure_ref(QTestState *qts,
+ const char *fmt, va_list args)
+{
+ QDict *response;
+ QDict *ret;
+
+ response = qtest_vqmp(qts, fmt, args);
+
+ g_assert(response);
+ if (!qdict_haskey(response, "error")) {
+ g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(response), true);
+ g_test_message("%s", s->str);
+ }
+ g_assert(qdict_haskey(response, "error"));
+ g_assert(!qdict_haskey(response, "return"));
+ ret = qdict_get_qdict(response, "error");
+ qobject_ref(ret);
+ qobject_unref(response);
+
+ return ret;
+}
+
QDict *qtest_vqmp_assert_success_ref(QTestState *qts,
const char *fmt, va_list args)
{
@@ -1305,6 +1327,17 @@ void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds,
}
#endif /* !_WIN32 */
+QDict *qtest_qmp_assert_failure_ref(QTestState *qts, const char *fmt, ...)
+{
+ QDict *response;
+ va_list ap;
+
+ va_start(ap, fmt);
+ response = qtest_vqmp_assert_failure_ref(qts, fmt, ap);
+ va_end(ap);
+ return response;
+}
+
QDict *qtest_qmp_assert_success_ref(QTestState *qts, const char *fmt, ...)
{
QDict *response;
@@ -799,6 +799,34 @@ void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds,
G_GNUC_PRINTF(4, 0);
#endif /* !_WIN32 */
+/**
+ * qtest_qmp_assert_failure_ref:
+ * @qts: QTestState instance to operate on
+ * @fmt: QMP message to send to qemu, formatted like
+ * qobject_from_jsonf_nofail(). See parse_interpolation() for what's
+ * supported after '%'.
+ *
+ * Sends a QMP message to QEMU, asserts that an 'error' key is present in
+ * the response, and returns the response.
+ */
+QDict *qtest_qmp_assert_failure_ref(QTestState *qts, const char *fmt, ...)
+ G_GNUC_PRINTF(2, 3);
+
+/**
+ * qtest_vqmp_assert_failure_ref:
+ * @qts: QTestState instance to operate on
+ * @fmt: QMP message to send to qemu, formatted like
+ * qobject_from_jsonf_nofail(). See parse_interpolation() for what's
+ * supported after '%'.
+ * @args: variable arguments for @fmt
+ *
+ * Sends a QMP message to QEMU, asserts that an 'error' key is present in
+ * the response, and returns the response.
+ */
+QDict *qtest_vqmp_assert_failure_ref(QTestState *qts,
+ const char *fmt, va_list args)
+ G_GNUC_PRINTF(2, 0);
+
/**
* qtest_qmp_assert_success_ref:
* @qts: QTestState instance to operate on
@@ -49,6 +49,28 @@ bool migrate_watch_for_resume(QTestState *who, const char *name,
return false;
}
+void migrate_qmp_with_error(QTestState *who, const char *uri,
+ const char *error_str, const char *fmt, ...)
+{
+ va_list ap;
+ QDict *args, *err;
+
+ va_start(ap, fmt);
+ args = qdict_from_vjsonf_nofail(fmt, ap);
+ va_end(ap);
+
+ g_assert(!qdict_haskey(args, "uri"));
+ qdict_put_str(args, "uri", uri);
+
+ err = qtest_qmp_assert_failure_ref(
+ who, "{ 'execute': 'migrate', 'arguments': %p}", args);
+
+ g_assert(qdict_haskey(err, "desc"));
+ g_assert_cmpstr(qdict_get_str(err, "desc"), ==, error_str);
+
+ qobject_unref(err);
+}
+
/*
* Send QMP command "migrate".
* Arguments are built from @fmt... (formatted like
@@ -27,6 +27,10 @@ G_GNUC_PRINTF(3, 4)
void migrate_incoming_qmp(QTestState *who, const char *uri,
const char *fmt, ...);
+G_GNUC_PRINTF(4, 5)
+void migrate_qmp_with_error(QTestState *who, const char *uri,
+ const char *error_str, const char *fmt, ...);
+
void migrate_set_capability(QTestState *who, const char *capability,
bool value);
@@ -564,6 +564,8 @@ typedef struct {
MIG_TEST_FAIL,
/* This test should fail, dest qemu should fail with abnormal status */
MIG_TEST_FAIL_DEST_QUIT_ERR,
+ /* The QMP command for this migration should fail with an error */
+ MIG_TEST_QMP_ERROR,
} result;
/* Optional: set number of migration passes to wait for, if live==true */
@@ -582,6 +584,7 @@ typedef struct {
/* Postcopy specific fields */
void *postcopy_data;
bool postcopy_preempt;
+ gchar *error_str;
} MigrateCommon;
static int test_migrate_start(QTestState **from, QTestState **to,
@@ -1379,6 +1382,7 @@ static void test_precopy_common(MigrateCommon *args)
{
QTestState *from, *to;
void *data_hook = NULL;
+ g_autofree char *connect_uri = NULL;
if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) {
return;
@@ -1419,13 +1423,17 @@ static void test_precopy_common(MigrateCommon *args)
}
if (!args->connect_uri) {
- g_autofree char *local_connect_uri =
- migrate_get_socket_address(to, "socket-address");
- migrate_qmp(from, local_connect_uri, "{}");
+ connect_uri = migrate_get_socket_address(to, "socket-address");
} else {
- migrate_qmp(from, args->connect_uri, "{}");
+ connect_uri = g_strdup(args->connect_uri);
}
+ if (args->result == MIG_TEST_QMP_ERROR) {
+ migrate_qmp_with_error(from, connect_uri, args->error_str, "{}");
+ goto finish;
+ }
+
+ migrate_qmp(from, connect_uri, "{}");
if (args->result != MIG_TEST_SUCCEED) {
bool allow_active = args->result == MIG_TEST_FAIL;
@@ -1474,6 +1482,7 @@ static void test_precopy_common(MigrateCommon *args)
wait_for_serial("dest_serial");
}
+finish:
if (args->finish_hook) {
args->finish_hook(from, to, data_hook);
}
There is currently no way to write a test for errors that happened in qmp_migrate before the migration has started. Add a version of qmp_migrate that ensures an error happens and tests the error message. To make use of it a test needs to declare: MigrateCommon args = { .result = MIG_TEST_QMP_ERROR, .error_str = "error message" } Signed-off-by: Fabiano Rosas <farosas@suse.de> --- tests/qtest/libqtest.c | 33 +++++++++++++++++++++++++++++++++ tests/qtest/libqtest.h | 28 ++++++++++++++++++++++++++++ tests/qtest/migration-helpers.c | 22 ++++++++++++++++++++++ tests/qtest/migration-helpers.h | 4 ++++ tests/qtest/migration-test.c | 17 +++++++++++++---- 5 files changed, 100 insertions(+), 4 deletions(-)