]> Git Repo - qemu.git/blob - tests/qmp-test.c
tests/libqos: Utilize newer glib spawn check
[qemu.git] / tests / qmp-test.c
1 /*
2  * QMP protocol test cases
3  *
4  * Copyright (c) 2017-2018 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/error.h"
16 #include "qapi/qapi-visit-misc.h"
17 #include "qapi/qmp/qdict.h"
18 #include "qapi/qmp/qlist.h"
19 #include "qapi/qobject-input-visitor.h"
20 #include "qapi/qmp/qstring.h"
21
22 const char common_args[] = "-nodefaults -machine none";
23
24 static const char *get_error_class(QDict *resp)
25 {
26     QDict *error = qdict_get_qdict(resp, "error");
27     const char *desc = qdict_get_try_str(error, "desc");
28
29     g_assert(desc);
30     return error ? qdict_get_try_str(error, "class") : NULL;
31 }
32
33 static void test_version(QObject *version)
34 {
35     Visitor *v;
36     VersionInfo *vinfo;
37
38     g_assert(version);
39     v = qobject_input_visitor_new(version);
40     visit_type_VersionInfo(v, "version", &vinfo, &error_abort);
41     qapi_free_VersionInfo(vinfo);
42     visit_free(v);
43 }
44
45 static bool recovered(QTestState *qts)
46 {
47     QDict *resp;
48     bool ret;
49
50     resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd' }");
51     ret = !strcmp(get_error_class(resp), "CommandNotFound");
52     qobject_unref(resp);
53     return ret;
54 }
55
56 static void test_malformed(QTestState *qts)
57 {
58     QDict *resp;
59
60     /* syntax error */
61     qtest_qmp_send_raw(qts, "{]\n");
62     resp = qtest_qmp_receive(qts);
63     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
64     qobject_unref(resp);
65     g_assert(recovered(qts));
66
67     /* lexical error: impossible byte outside string */
68     qtest_qmp_send_raw(qts, "{\xFF");
69     resp = qtest_qmp_receive(qts);
70     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
71     qobject_unref(resp);
72     g_assert(recovered(qts));
73
74     /* lexical error: funny control character outside string */
75     qtest_qmp_send_raw(qts, "{\x01");
76     resp = qtest_qmp_receive(qts);
77     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
78     qobject_unref(resp);
79     g_assert(recovered(qts));
80
81     /* lexical error: impossible byte in string */
82     qtest_qmp_send_raw(qts, "{'bad \xFF");
83     resp = qtest_qmp_receive(qts);
84     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
85     qobject_unref(resp);
86     g_assert(recovered(qts));
87
88     /* lexical error: control character in string */
89     qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n");
90     resp = qtest_qmp_receive(qts);
91     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
92     qobject_unref(resp);
93     g_assert(recovered(qts));
94
95     /* lexical error: interpolation */
96     qtest_qmp_send_raw(qts, "%%p\n");
97     /* two errors, one for "%", one for "p" */
98     resp = qtest_qmp_receive(qts);
99     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
100     qobject_unref(resp);
101     resp = qtest_qmp_receive(qts);
102     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
103     qobject_unref(resp);
104     g_assert(recovered(qts));
105
106     /* Not even a dictionary */
107     resp = qtest_qmp(qts, "null");
108     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
109     qobject_unref(resp);
110
111     /* No "execute" key */
112     resp = qtest_qmp(qts, "{}");
113     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
114     qobject_unref(resp);
115
116     /* "execute" isn't a string */
117     resp = qtest_qmp(qts, "{ 'execute': true }");
118     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
119     qobject_unref(resp);
120
121     /* "arguments" isn't a dictionary */
122     resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }");
123     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
124     qobject_unref(resp);
125
126     /* extra key */
127     resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }");
128     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
129     qobject_unref(resp);
130 }
131
132 static void test_qmp_protocol(void)
133 {
134     QDict *resp, *q, *ret;
135     QList *capabilities;
136     QTestState *qts;
137
138     qts = qtest_init_without_qmp_handshake(false, common_args);
139
140     /* Test greeting */
141     resp = qtest_qmp_receive(qts);
142     q = qdict_get_qdict(resp, "QMP");
143     g_assert(q);
144     test_version(qdict_get(q, "version"));
145     capabilities = qdict_get_qlist(q, "capabilities");
146     g_assert(capabilities && qlist_empty(capabilities));
147     qobject_unref(resp);
148
149     /* Test valid command before handshake */
150     resp = qtest_qmp(qts, "{ 'execute': 'query-version' }");
151     g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound");
152     qobject_unref(resp);
153
154     /* Test malformed commands before handshake */
155     test_malformed(qts);
156
157     /* Test handshake */
158     resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }");
159     ret = qdict_get_qdict(resp, "return");
160     g_assert(ret && !qdict_size(ret));
161     qobject_unref(resp);
162
163     /* Test repeated handshake */
164     resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }");
165     g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound");
166     qobject_unref(resp);
167
168     /* Test valid command */
169     resp = qtest_qmp(qts, "{ 'execute': 'query-version' }");
170     test_version(qdict_get(resp, "return"));
171     qobject_unref(resp);
172
173     /* Test malformed commands */
174     test_malformed(qts);
175
176     /* Test 'id' */
177     resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }");
178     ret = qdict_get_qdict(resp, "return");
179     g_assert(ret);
180     g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1");
181     qobject_unref(resp);
182
183     /* Test command failure with 'id' */
184     resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }");
185     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
186     g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2);
187     qobject_unref(resp);
188
189     qtest_quit(qts);
190 }
191
192 /* Out-of-band tests */
193
194 char tmpdir[] = "/tmp/qmp-test-XXXXXX";
195 char *fifo_name;
196
197 static void setup_blocking_cmd(void)
198 {
199     if (!mkdtemp(tmpdir)) {
200         g_error("mkdtemp: %s", strerror(errno));
201     }
202     fifo_name = g_strdup_printf("%s/fifo", tmpdir);
203     if (mkfifo(fifo_name, 0666)) {
204         g_error("mkfifo: %s", strerror(errno));
205     }
206 }
207
208 static void cleanup_blocking_cmd(void)
209 {
210     unlink(fifo_name);
211     rmdir(tmpdir);
212 }
213
214 static void send_cmd_that_blocks(QTestState *s, const char *id)
215 {
216     qtest_qmp_send(s, "{ 'execute': 'blockdev-add',  'id': %s,"
217                    " 'arguments': {"
218                    " 'driver': 'blkdebug', 'node-name': %s,"
219                    " 'config': %s,"
220                    " 'image': { 'driver': 'null-co' } } }",
221                    id, id, fifo_name);
222 }
223
224 static void unblock_blocked_cmd(void)
225 {
226     int fd = open(fifo_name, O_WRONLY);
227     g_assert(fd >= 0);
228     close(fd);
229 }
230
231 static void send_oob_cmd_that_fails(QTestState *s, const char *id)
232 {
233     qtest_qmp_send(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id);
234 }
235
236 static void recv_cmd_id(QTestState *s, const char *id)
237 {
238     QDict *resp = qtest_qmp_receive(s);
239
240     g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
241     qobject_unref(resp);
242 }
243
244 static void test_qmp_oob(void)
245 {
246     QTestState *qts;
247     QDict *resp, *q;
248     const QListEntry *entry;
249     QList *capabilities;
250     QString *qstr;
251
252     qts = qtest_init_without_qmp_handshake(true, common_args);
253
254     /* Check the greeting message. */
255     resp = qtest_qmp_receive(qts);
256     q = qdict_get_qdict(resp, "QMP");
257     g_assert(q);
258     capabilities = qdict_get_qlist(q, "capabilities");
259     g_assert(capabilities && !qlist_empty(capabilities));
260     entry = qlist_first(capabilities);
261     g_assert(entry);
262     qstr = qobject_to(QString, entry->value);
263     g_assert(qstr);
264     g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
265     qobject_unref(resp);
266
267     /* Try a fake capability, it should fail. */
268     resp = qtest_qmp(qts,
269                      "{ 'execute': 'qmp_capabilities', "
270                      "  'arguments': { 'enable': [ 'cap-does-not-exist' ] } }");
271     g_assert(qdict_haskey(resp, "error"));
272     qobject_unref(resp);
273
274     /* Now, enable OOB in current QMP session, it should succeed. */
275     resp = qtest_qmp(qts,
276                      "{ 'execute': 'qmp_capabilities', "
277                      "  'arguments': { 'enable': [ 'oob' ] } }");
278     g_assert(qdict_haskey(resp, "return"));
279     qobject_unref(resp);
280
281     /*
282      * Try any command that does not support OOB but with OOB flag. We
283      * should get failure.
284      */
285     resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }");
286     g_assert(qdict_haskey(resp, "error"));
287     qobject_unref(resp);
288
289     /* OOB command overtakes slow in-band command */
290     setup_blocking_cmd();
291     send_cmd_that_blocks(qts, "ib-blocks-1");
292     qtest_qmp_send(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }");
293     send_oob_cmd_that_fails(qts, "oob-1");
294     recv_cmd_id(qts, "oob-1");
295     unblock_blocked_cmd();
296     recv_cmd_id(qts, "ib-blocks-1");
297     recv_cmd_id(qts, "ib-quick-1");
298
299     /* Even malformed in-band command fails in-band */
300     send_cmd_that_blocks(qts, "blocks-2");
301     qtest_qmp_send(qts, "{ 'id': 'err-2' }");
302     unblock_blocked_cmd();
303     recv_cmd_id(qts, "blocks-2");
304     recv_cmd_id(qts, "err-2");
305     cleanup_blocking_cmd();
306
307     qtest_quit(qts);
308 }
309
310 /* Preconfig tests */
311
312 static void test_qmp_preconfig(void)
313 {
314     QDict *rsp, *ret;
315     QTestState *qs = qtest_initf("%s --preconfig", common_args);
316
317     /* preconfig state */
318     /* enabled commands, no error expected  */
319     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }")));
320
321     /* forbidden commands, expected error */
322     g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
323
324     /* check that query-status returns preconfig state */
325     rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
326     ret = qdict_get_qdict(rsp, "return");
327     g_assert(ret);
328     g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig");
329     qobject_unref(rsp);
330
331     /* exit preconfig state */
332     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }")));
333     qtest_qmp_eventwait(qs, "RESUME");
334
335     /* check that query-status returns running state */
336     rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
337     ret = qdict_get_qdict(rsp, "return");
338     g_assert(ret);
339     g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running");
340     qobject_unref(rsp);
341
342     /* check that x-exit-preconfig returns error after exiting preconfig */
343     g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }")));
344
345     /* enabled commands, no error expected  */
346     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
347
348     qtest_quit(qs);
349 }
350
351 int main(int argc, char *argv[])
352 {
353     g_test_init(&argc, &argv, NULL);
354
355     qtest_add_func("qmp/protocol", test_qmp_protocol);
356     qtest_add_func("qmp/oob", test_qmp_oob);
357     qtest_add_func("qmp/preconfig", test_qmp_preconfig);
358
359     return g_test_run();
360 }
This page took 0.044141 seconds and 4 git commands to generate.