From b8e1f74b0a258db394a35272a44d14ec0b6e0be9 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 21 Aug 2018 14:05:16 -0500 Subject: tests/libqos: Utilize newer glib spawn check During development, I got a 'make check' failure that claimed: qemu-img returned status code 32512 ** ERROR:tests/libqos/libqos.c:202:mkimg: assertion failed: (!rc) But 32512 is too big for a normal exit status value, which means we failed to use WEXITSTATUS() to shift the bits to the desired value for printing. However, instead of worrying about how to portably parse g_spawn()'s rc in the proper platform-dependent manner, it's better to just rely on the fact that we now require glib 2.40 (since commit e7b3af815) and can therefore use glib's portable checker instead, where the message under my same condition improves to: Child process exited with code 127 ** ERROR:tests/libqos/libqos.c:192:mkimg: assertion failed: (ret && !err) Signed-off-by: Eric Blake Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/libqos/libqos.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'tests') diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 013ca68581..c514187344 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -185,22 +185,12 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb) cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path, fmt, file, size_mb); ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err); - if (err) { + if (err || !g_spawn_check_exit_status(rc, &err)) { fprintf(stderr, "%s\n", err->message); g_error_free(err); } g_assert(ret && !err); - /* In glib 2.34, we have g_spawn_check_exit_status. in 2.12, we don't. - * glib 2.43.91 implementation assumes that any non-zero is an error for - * windows, but uses extra precautions for Linux. However, - * 0 is only possible if the program exited normally, so that should be - * sufficient for our purposes on all platforms, here. */ - if (rc) { - fprintf(stderr, "qemu-img returned status code %d\n", rc); - } - g_assert(!rc); - g_free(out); g_free(out2); g_free(cli); -- cgit v1.2.3-55-g7522 From ebb4d82d88810c31cb5eee408e1ac6d319f7f9bb Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Thu, 30 Aug 2018 17:58:07 +0200 Subject: tests: add qmp_assert_error_class() This helper will simplify a bunch of code checking for QMP errors and can be shared by various tests. Note that test-qga does check for error description as well, so don't replace the code there for now. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/drive_del-test.c | 5 +--- tests/libqtest.c | 11 ++++++++ tests/libqtest.h | 9 +++++++ tests/qmp-test.c | 73 ++++++++++++++++---------------------------------- tests/test-qga.c | 9 ++----- 5 files changed, 46 insertions(+), 61 deletions(-) (limited to 'tests') diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 673c10140f..4a1a0889c8 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -67,7 +67,6 @@ static void test_after_failed_device_add(void) { char driver[32]; QDict *response; - QDict *error; snprintf(driver, sizeof(driver), "virtio-blk-%s", qvirtio_get_dev_type()); @@ -83,9 +82,7 @@ static void test_after_failed_device_add(void) " 'drive': 'drive0'" "}}", driver); g_assert(response); - error = qdict_get_qdict(response, "error"); - g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError"); - qobject_unref(response); + qmp_assert_error_class(response, "GenericError"); /* Delete the drive */ drive_del(); diff --git a/tests/libqtest.c b/tests/libqtest.c index d635c5bea0..2cd5736642 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -1194,3 +1194,14 @@ bool qmp_rsp_is_err(QDict *rsp) qobject_unref(rsp); return !!error; } + +void qmp_assert_error_class(QDict *rsp, const char *class) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + + g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, class); + g_assert_nonnull(qdict_get_try_str(error, "desc")); + g_assert(!qdict_haskey(rsp, "return")); + + qobject_unref(rsp); +} diff --git a/tests/libqtest.h b/tests/libqtest.h index 36d5caecd4..ed88ff99d5 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -1004,4 +1004,13 @@ void qtest_qmp_device_del(const char *id); */ bool qmp_rsp_is_err(QDict *rsp); +/** + * qmp_assert_error_class: + * @rsp: QMP response to check for error + * @class: an error class + * + * Assert the response has the given error class and discard @rsp. + */ +void qmp_assert_error_class(QDict *rsp, const char *class); + #endif diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 4ae2245484..b3472281ae 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -21,15 +21,6 @@ const char common_args[] = "-nodefaults -machine none"; -static const char *get_error_class(QDict *resp) -{ - QDict *error = qdict_get_qdict(resp, "error"); - const char *desc = qdict_get_try_str(error, "desc"); - - g_assert(desc); - return error ? qdict_get_try_str(error, "class") : NULL; -} - static void test_version(QObject *version) { Visitor *v; @@ -42,15 +33,12 @@ static void test_version(QObject *version) visit_free(v); } -static bool recovered(QTestState *qts) +static void assert_recovered(QTestState *qts) { QDict *resp; - bool ret; resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd' }"); - ret = !strcmp(get_error_class(resp), "CommandNotFound"); - qobject_unref(resp); - return ret; + qmp_assert_error_class(resp, "CommandNotFound"); } static void test_malformed(QTestState *qts) @@ -60,73 +48,61 @@ static void test_malformed(QTestState *qts) /* syntax error */ qtest_qmp_send_raw(qts, "{]\n"); resp = qtest_qmp_receive(qts); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); - g_assert(recovered(qts)); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); /* lexical error: impossible byte outside string */ qtest_qmp_send_raw(qts, "{\xFF"); resp = qtest_qmp_receive(qts); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); - g_assert(recovered(qts)); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); /* lexical error: funny control character outside string */ qtest_qmp_send_raw(qts, "{\x01"); resp = qtest_qmp_receive(qts); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); - g_assert(recovered(qts)); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); /* lexical error: impossible byte in string */ qtest_qmp_send_raw(qts, "{'bad \xFF"); resp = qtest_qmp_receive(qts); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); - g_assert(recovered(qts)); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); /* lexical error: control character in string */ qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n"); resp = qtest_qmp_receive(qts); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); - g_assert(recovered(qts)); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); /* lexical error: interpolation */ qtest_qmp_send_raw(qts, "%%p\n"); /* two errors, one for "%", one for "p" */ resp = qtest_qmp_receive(qts); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); resp = qtest_qmp_receive(qts); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); - g_assert(recovered(qts)); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); /* Not even a dictionary */ resp = qtest_qmp(qts, "null"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* No "execute" key */ resp = qtest_qmp(qts, "{}"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* "execute" isn't a string */ resp = qtest_qmp(qts, "{ 'execute': true }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* "arguments" isn't a dictionary */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* extra key */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); } static void test_qmp_protocol(void) @@ -148,8 +124,7 @@ static void test_qmp_protocol(void) /* Test valid command before handshake */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); - g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - qobject_unref(resp); + qmp_assert_error_class(resp, "CommandNotFound"); /* Test malformed commands before handshake */ test_malformed(qts); @@ -162,8 +137,7 @@ static void test_qmp_protocol(void) /* Test repeated handshake */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); - g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - qobject_unref(resp); + qmp_assert_error_class(resp, "CommandNotFound"); /* Test valid command */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); @@ -182,9 +156,8 @@ static void test_qmp_protocol(void) /* Test command failure with 'id' */ resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); qtest_quit(qts); } diff --git a/tests/test-qga.c b/tests/test-qga.c index f69cdf6c03..3d6377436a 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -244,17 +244,12 @@ static void test_qga_invalid_id(gconstpointer fix) static void test_qga_invalid_oob(gconstpointer fix) { const TestFixture *fixture = fix; - QDict *ret, *error; - const char *class; + QDict *ret; ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); g_assert_nonnull(ret); - error = qdict_get_qdict(ret, "error"); - class = qdict_get_try_str(error, "class"); - g_assert_cmpstr(class, ==, "GenericError"); - - qobject_unref(ret); + qmp_assert_error_class(ret, "GenericError"); } static void test_qga_invalid_args(gconstpointer fix) -- cgit v1.2.3-55-g7522 From 442b09b83d256d1ba4daa62cb939c84c829cc0f3 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Thu, 30 Aug 2018 17:58:08 +0200 Subject: tests: add qmp/object-add-without-props test test_object_add_without_props() tests a bug in qmp_object_add() we fixed in commit e64c75a975. Sadly, we don't have systematic object-add tests. This lone test can go into qmp-cmd-test for want of a better home. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/qmp-cmd-test.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'tests') diff --git a/tests/qmp-cmd-test.c b/tests/qmp-cmd-test.c index c5b70df974..d12cac539c 100644 --- a/tests/qmp-cmd-test.c +++ b/tests/qmp-cmd-test.c @@ -197,6 +197,19 @@ static void add_query_tests(QmpSchema *schema) } } +static void test_object_add_without_props(void) +{ + QTestState *qts; + QDict *resp; + + qts = qtest_init(common_args); + resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':" + " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }"); + g_assert_nonnull(resp); + qmp_assert_error_class(resp, "GenericError"); + qtest_quit(qts); +} + int main(int argc, char *argv[]) { QmpSchema schema; @@ -206,6 +219,11 @@ int main(int argc, char *argv[]) qmp_schema_init(&schema); add_query_tests(&schema); + + qtest_add_func("qmp/object-add-without-props", + test_object_add_without_props); + /* TODO: add coverage of generic object-add failure modes */ + ret = g_test_run(); qmp_schema_cleanup(&schema); -- cgit v1.2.3-55-g7522 From 2b70ea92766f5a1a735a44e28c92cdfba3c4054f Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Thu, 30 Aug 2018 17:58:09 +0200 Subject: tests: add qmp/qom-set-without-value test test_qom_set_without_value() is about a bug in infrastructure used by the QMP core, fixed in commit c489780203. We covered the bug in infrastructure unit tests (commit bce3035a44). I wrote that test earlier, to cover QMP level as well, the test could go into qmp-test. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/qmp-test.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'tests') diff --git a/tests/qmp-test.c b/tests/qmp-test.c index b3472281ae..2b923f02db 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -321,6 +321,19 @@ static void test_qmp_preconfig(void) qtest_quit(qs); } +static void test_qom_set_without_value(void) +{ + QTestState *qts; + QDict *resp; + + qts = qtest_init(common_args); + resp = qtest_qmp(qts, "{'execute': 'qom-set', 'arguments':" + " { 'path': '/machine', 'property': 'rtc-time' } }"); + g_assert_nonnull(resp); + qmp_assert_error_class(resp, "GenericError"); + qtest_quit(qts); +} + int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); @@ -328,6 +341,7 @@ int main(int argc, char *argv[]) qtest_add_func("qmp/protocol", test_qmp_protocol); qtest_add_func("qmp/oob", test_qmp_oob); qtest_add_func("qmp/preconfig", test_qmp_preconfig); + qtest_add_func("qmp/qom-set-without-value", test_qom_set_without_value); return g_test_run(); } -- cgit v1.2.3-55-g7522 From ae6bf766048ecaeef90b85c4fb2b4db2aa0c094c Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Thu, 30 Aug 2018 17:58:10 +0200 Subject: tests: add a qmp success-response test Verify the usage of this schema feature and the API behaviour. This should be the only case where qmp_dispatch() returns NULL. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Signed-off-by: Thomas Huth --- tests/qapi-schema/qapi-schema-test.json | 2 ++ tests/qapi-schema/qapi-schema-test.out | 2 ++ tests/test-qmp-cmds.c | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) (limited to 'tests') diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 11aa4c8f8d..fb03163430 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -137,6 +137,8 @@ 'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' } +{ 'command': 'cmd-success-response', 'data': {}, 'success-response': false } + # Returning a non-dictionary requires a name from the whitelist { 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' }, 'returns': 'int' } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 0da92455da..218ac7d556 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -156,6 +156,8 @@ object q_obj_user_def_cmd2-arg member ud1b: UserDefOne optional=True command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo gen=True success_response=True boxed=False oob=False preconfig=False +command cmd-success-response None -> None + gen=True success_response=False boxed=False oob=False preconfig=False object q_obj_guest-get-time-arg member a: int optional=False member b: int optional=True diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index ab414fa0c9..4ab2b6e5ce 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -32,6 +32,10 @@ void qmp_test_flags_command(Error **errp) { } +void qmp_cmd_success_response(Error **errp) +{ +} + Empty2 *qmp_user_def_cmd0(Error **errp) { return g_new0(Empty2, 1); @@ -153,6 +157,17 @@ static void test_dispatch_cmd_failure(void) qobject_unref(req); } +static void test_dispatch_cmd_success_response(void) +{ + QDict *req = qdict_new(); + QDict *resp; + + qdict_put_str(req, "execute", "cmd-success-response"); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); + g_assert_null(resp); + qobject_unref(req); +} + static QObject *test_qmp_dispatch(QDict *req) { QDict *resp; @@ -289,6 +304,8 @@ int main(int argc, char **argv) g_test_add_func("/qmp/dispatch_cmd", test_dispatch_cmd); g_test_add_func("/qmp/dispatch_cmd_failure", test_dispatch_cmd_failure); g_test_add_func("/qmp/dispatch_cmd_io", test_dispatch_cmd_io); + g_test_add_func("/qmp/dispatch_cmd_success_response", + test_dispatch_cmd_success_response); g_test_add_func("/qmp/dealloc_types", test_dealloc_types); g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial); -- cgit v1.2.3-55-g7522