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 <faro...@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(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index de03ef5f60..14ed7d6d83 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -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; diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index a12acf7fa9..1fed533d7d 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -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 diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index bc54b29184..5e021ba078 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -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 diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 57d295a4fe..991efd57a6 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -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); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index aafecb4f02..acb778a8cd 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -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); } -- 2.35.3