]>
Commit | Line | Data |
---|---|---|
f66e7ac8 MA |
1 | /* |
2 | * QMP protocol test cases | |
3 | * | |
4 | * Copyright (c) 2017 Red Hat Inc. | |
5 | * | |
6 | * Authors: | |
7 | * Markus Armbruster <[email protected]>, | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "libqtest.h" | |
15 | #include "qapi-visit.h" | |
16 | #include "qapi/error.h" | |
17 | #include "qapi/qobject-input-visitor.h" | |
18 | #include "qapi/visitor.h" | |
19 | ||
20 | const char common_args[] = "-nodefaults -machine none"; | |
21 | ||
22 | static const char *get_error_class(QDict *resp) | |
23 | { | |
24 | QDict *error = qdict_get_qdict(resp, "error"); | |
25 | const char *desc = qdict_get_try_str(error, "desc"); | |
26 | ||
27 | g_assert(desc); | |
28 | return error ? qdict_get_try_str(error, "class") : NULL; | |
29 | } | |
30 | ||
31 | static void test_version(QObject *version) | |
32 | { | |
33 | Visitor *v; | |
34 | VersionInfo *vinfo; | |
35 | ||
36 | g_assert(version); | |
048abb7b | 37 | v = qobject_input_visitor_new(version); |
f66e7ac8 MA |
38 | visit_type_VersionInfo(v, "version", &vinfo, &error_abort); |
39 | qapi_free_VersionInfo(vinfo); | |
40 | visit_free(v); | |
41 | } | |
42 | ||
43 | static void test_malformed(void) | |
44 | { | |
45 | QDict *resp; | |
46 | ||
47 | /* Not even a dictionary */ | |
48 | resp = qmp("null"); | |
49 | g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); | |
50 | QDECREF(resp); | |
51 | ||
52 | /* No "execute" key */ | |
53 | resp = qmp("{}"); | |
54 | g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); | |
55 | QDECREF(resp); | |
56 | ||
57 | /* "execute" isn't a string */ | |
58 | resp = qmp("{ 'execute': true }"); | |
59 | g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); | |
60 | QDECREF(resp); | |
61 | ||
62 | /* "arguments" isn't a dictionary */ | |
63 | resp = qmp("{ 'execute': 'no-such-cmd', 'arguments': [] }"); | |
64 | g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); | |
65 | QDECREF(resp); | |
66 | ||
67 | /* extra key */ | |
68 | resp = qmp("{ 'execute': 'no-such-cmd', 'extra': true }"); | |
69 | g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); | |
70 | QDECREF(resp); | |
71 | } | |
72 | ||
73 | static void test_qmp_protocol(void) | |
74 | { | |
75 | QDict *resp, *q, *ret; | |
76 | QList *capabilities; | |
77 | ||
78 | global_qtest = qtest_init_without_qmp_handshake(common_args); | |
79 | ||
80 | /* Test greeting */ | |
81 | resp = qmp_receive(); | |
82 | q = qdict_get_qdict(resp, "QMP"); | |
83 | g_assert(q); | |
84 | test_version(qdict_get(q, "version")); | |
85 | capabilities = qdict_get_qlist(q, "capabilities"); | |
86 | g_assert(capabilities && qlist_empty(capabilities)); | |
87 | QDECREF(resp); | |
88 | ||
89 | /* Test valid command before handshake */ | |
90 | resp = qmp("{ 'execute': 'query-version' }"); | |
91 | g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); | |
92 | QDECREF(resp); | |
93 | ||
94 | /* Test malformed commands before handshake */ | |
95 | test_malformed(); | |
96 | ||
97 | /* Test handshake */ | |
98 | resp = qmp("{ 'execute': 'qmp_capabilities' }"); | |
99 | ret = qdict_get_qdict(resp, "return"); | |
100 | g_assert(ret && !qdict_size(ret)); | |
101 | QDECREF(resp); | |
102 | ||
103 | /* Test repeated handshake */ | |
104 | resp = qmp("{ 'execute': 'qmp_capabilities' }"); | |
105 | g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); | |
106 | QDECREF(resp); | |
107 | ||
108 | /* Test valid command */ | |
109 | resp = qmp("{ 'execute': 'query-version' }"); | |
110 | test_version(qdict_get(resp, "return")); | |
111 | QDECREF(resp); | |
112 | ||
113 | /* Test malformed commands */ | |
114 | test_malformed(); | |
115 | ||
116 | /* Test 'id' */ | |
117 | resp = qmp("{ 'execute': 'query-name', 'id': 'cookie#1' }"); | |
118 | ret = qdict_get_qdict(resp, "return"); | |
119 | g_assert(ret); | |
120 | g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); | |
121 | QDECREF(resp); | |
122 | ||
123 | /* Test command failure with 'id' */ | |
124 | resp = qmp("{ 'execute': 'human-monitor-command', 'id': 2 }"); | |
125 | g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); | |
126 | g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); | |
127 | QDECREF(resp); | |
128 | ||
129 | qtest_end(); | |
130 | } | |
131 | ||
132 | int main(int argc, char *argv[]) | |
133 | { | |
134 | g_test_init(&argc, &argv, NULL); | |
135 | ||
136 | qtest_add_func("qmp/protocol", test_qmp_protocol); | |
137 | ||
138 | return g_test_run(); | |
139 | } |