xref: /openbmc/qemu/tests/qtest/qom-test.c (revision 3656e761bcdd207b7759cdcd608212d2a6f9c12d)
1 /*
2  * QTest testcase for QOM
3  *
4  * Copyright (c) 2013 SUSE LINUX Products GmbH
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  */
9 
10 #include "qemu/osdep.h"
11 
12 #include "qobject/qdict.h"
13 #include "qobject/qlist.h"
14 #include "qobject/qstring.h"
15 #include "qemu/cutils.h"
16 #include "libqtest.h"
17 
18 #define RAM_NAME "node0"
19 #define RAM_SIZE 65536
20 
21 static int verbosity_level;
22 
23 /*
24  * Verify that the /object/RAM_NAME 'size' property is RAM_SIZE.
25  */
test_list_get_value(QTestState * qts)26 static void test_list_get_value(QTestState *qts)
27 {
28     QDict *args = qdict_new();
29     g_autoptr(QDict) response = NULL;
30     g_autoptr(QList) paths = qlist_new();
31     QListEntry *entry, *prop_entry;
32     const char *prop_name;
33     QList *properties, *return_list;
34     QDict *obj;
35 
36     qlist_append_str(paths, "/objects/" RAM_NAME);
37     qdict_put_obj(args, "paths", QOBJECT(qlist_copy(paths)));
38     response = qtest_qmp(qts, "{ 'execute': 'qom-list-get',"
39                               "  'arguments': %p }", args);
40     g_assert(response);
41     g_assert(qdict_haskey(response, "return"));
42     return_list = qobject_to(QList, qdict_get(response, "return"));
43 
44     entry = QTAILQ_FIRST(&return_list->head);
45     obj = qobject_to(QDict, qlist_entry_obj(entry));
46     g_assert(qdict_haskey(obj, "properties"));
47     properties = qobject_to(QList, qdict_get(obj, "properties"));
48 
49     QLIST_FOREACH_ENTRY(properties, prop_entry) {
50         QDict *prop = qobject_to(QDict, qlist_entry_obj(prop_entry));
51 
52         g_assert(qdict_haskey(prop, "name"));
53         g_assert(qdict_haskey(prop, "value"));
54 
55         prop_name = qdict_get_str(prop, "name");
56         if (!strcmp(prop_name, "type")) {
57             g_assert_cmpstr(qdict_get_str(prop, "value"), ==,
58                             "memory-backend-ram");
59 
60         } else if (!strcmp(prop_name, "size")) {
61             g_assert_cmpint(qdict_get_int(prop, "value"), ==, RAM_SIZE);
62         }
63     }
64 }
65 
test_list_get(QTestState * qts,QList * paths)66 static void test_list_get(QTestState *qts, QList *paths)
67 {
68     QListEntry *entry, *prop_entry, *path_entry;
69     g_autoptr(QDict) response = NULL;
70     QDict *args = qdict_new();
71     QDict *prop;
72     QList *return_list;
73 
74     if (verbosity_level >= 2) {
75         g_test_message("Obtaining properties for paths:");
76         QLIST_FOREACH_ENTRY(paths, path_entry) {
77             QString *qstr = qobject_to(QString, qlist_entry_obj(path_entry));
78             g_test_message("  %s", qstring_get_str(qstr));
79         }
80     }
81 
82     qdict_put_obj(args, "paths", QOBJECT(qlist_copy(paths)));
83     response = qtest_qmp(qts, "{ 'execute': 'qom-list-get',"
84                               "  'arguments': %p }", args);
85     g_assert(response);
86     g_assert(qdict_haskey(response, "return"));
87     return_list = qobject_to(QList, qdict_get(response, "return"));
88     g_assert(!qlist_empty(return_list));
89 
90     path_entry = QTAILQ_FIRST(&paths->head);
91     QLIST_FOREACH_ENTRY(return_list, entry) {
92         QDict *obj = qobject_to(QDict, qlist_entry_obj(entry));
93         g_assert(qdict_haskey(obj, "properties"));
94         QList *properties = qobject_to(QList, qdict_get(obj, "properties"));
95         bool has_child = false;
96 
97         QLIST_FOREACH_ENTRY(properties, prop_entry) {
98             prop = qobject_to(QDict, qlist_entry_obj(prop_entry));
99             g_assert(qdict_haskey(prop, "name"));
100             g_assert(qdict_haskey(prop, "type"));
101             has_child |= strstart(qdict_get_str(prop, "type"), "child<", NULL);
102         }
103 
104         if (has_child) {
105             /* build a list of child paths */
106             QString *qstr = qobject_to(QString, qlist_entry_obj(path_entry));
107             const char *path = qstring_get_str(qstr);
108             g_autoptr(QList) child_paths = qlist_new();
109 
110             QLIST_FOREACH_ENTRY(properties, prop_entry) {
111                 prop = qobject_to(QDict, qlist_entry_obj(prop_entry));
112                 if (strstart(qdict_get_str(prop, "type"), "child<", NULL)) {
113                     g_autofree char *child_path = g_strdup_printf(
114                         "%s/%s", path, qdict_get_str(prop, "name"));
115                     qlist_append_str(child_paths, child_path);
116                 }
117             }
118 
119             /* fetch props for all children with one qom-list-get call */
120             test_list_get(qts, child_paths);
121         }
122 
123         path_entry = QTAILQ_NEXT(path_entry, next);
124     }
125 }
126 
test_properties(QTestState * qts,const char * path,bool recurse)127 static void test_properties(QTestState *qts, const char *path, bool recurse)
128 {
129     char *child_path;
130     QDict *response, *tuple, *tmp;
131     QList *list;
132     QListEntry *entry;
133     GSList *children = NULL, *links = NULL;
134 
135     if (verbosity_level >= 2) {
136         g_test_message("Obtaining properties of %s", path);
137     }
138     response = qtest_qmp(qts, "{ 'execute': 'qom-list',"
139                               "  'arguments': { 'path': %s } }", path);
140     g_assert(response);
141 
142     if (!recurse) {
143         qobject_unref(response);
144         return;
145     }
146 
147     g_assert(qdict_haskey(response, "return"));
148     list = qobject_to(QList, qdict_get(response, "return"));
149     QLIST_FOREACH_ENTRY(list, entry) {
150         tuple = qobject_to(QDict, qlist_entry_obj(entry));
151         bool is_child = strstart(qdict_get_str(tuple, "type"), "child<", NULL);
152         bool is_link = strstart(qdict_get_str(tuple, "type"), "link<", NULL);
153 
154         if (is_child || is_link) {
155             child_path = g_strdup_printf("%s/%s",
156                                          path, qdict_get_str(tuple, "name"));
157             if (is_child) {
158                 children = g_slist_prepend(children, child_path);
159             } else {
160                 links = g_slist_prepend(links, child_path);
161             }
162         } else {
163             const char *prop = qdict_get_str(tuple, "name");
164             if (verbosity_level >= 3) {
165                 g_test_message("-> %s", prop);
166             }
167             tmp = qtest_qmp(qts,
168                             "{ 'execute': 'qom-get',"
169                             "  'arguments': { 'path': %s, 'property': %s } }",
170                             path, prop);
171             /* qom-get may fail but should not, e.g., segfault. */
172             g_assert(tmp);
173             qobject_unref(tmp);
174         }
175     }
176 
177     while (links) {
178         test_properties(qts, links->data, false);
179         g_free(links->data);
180         links = g_slist_delete_link(links, links);
181     }
182     while (children) {
183         test_properties(qts, children->data, true);
184         g_free(children->data);
185         children = g_slist_delete_link(children, children);
186     }
187 
188     qobject_unref(response);
189 }
190 
test_machine(gconstpointer data)191 static void test_machine(gconstpointer data)
192 {
193     const char *machine = data;
194     QDict *response;
195     QTestState *qts;
196     g_autoptr(QList) paths = qlist_new();
197 
198     qts = qtest_initf("-machine %s -object memory-backend-ram,id=%s,size=%d",
199                       machine, RAM_NAME, RAM_SIZE);
200 
201     if (g_test_slow()) {
202         /* Make sure we can get the machine class properties: */
203         g_autofree char *qom_machine = g_strdup_printf("%s-machine", machine);
204 
205         response = qtest_qmp(qts, "{ 'execute': 'qom-list-properties',"
206                                   "  'arguments': { 'typename': %s } }",
207                              qom_machine);
208         g_assert(response);
209         qobject_unref(response);
210     }
211 
212     test_properties(qts, "/machine", true);
213 
214     qlist_append_str(paths, "/machine");
215     test_list_get(qts, paths);
216     test_list_get_value(qts);
217 
218     response = qtest_qmp(qts, "{ 'execute': 'quit' }");
219     g_assert(qdict_haskey(response, "return"));
220     qobject_unref(response);
221 
222     qtest_quit(qts);
223     g_free((void *)machine);
224 }
225 
add_machine_test_case(const char * mname)226 static void add_machine_test_case(const char *mname)
227 {
228     char *path;
229 
230     path = g_strdup_printf("qom/%s", mname);
231     qtest_add_data_func(path, g_strdup(mname), test_machine);
232     g_free(path);
233 }
234 
main(int argc,char ** argv)235 int main(int argc, char **argv)
236 {
237     char *v_env = getenv("V");
238 
239     if (v_env) {
240         verbosity_level = atoi(v_env);
241     }
242 
243     g_test_init(&argc, &argv, NULL);
244 
245     qtest_cb_for_every_machine(add_machine_test_case, g_test_quick());
246 
247     return g_test_run();
248 }
249