]>
Commit | Line | Data |
---|---|---|
1b76e838 HR |
1 | /* |
2 | * Generic QObject unit-tests. | |
3 | * | |
4 | * Copyright (C) 2017 Red Hat Inc. | |
5 | * | |
6 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
7 | * See the COPYING.LIB file in the top-level directory. | |
8 | */ | |
1b76e838 | 9 | |
47e6b297 | 10 | #include "qemu/osdep.h" |
609f45ea | 11 | #include "block/qdict.h" |
6b673957 | 12 | #include "qapi/qmp/qbool.h" |
452fcdbc | 13 | #include "qapi/qmp/qdict.h" |
47e6b297 | 14 | #include "qapi/qmp/qlist.h" |
15280c36 MA |
15 | #include "qapi/qmp/qnull.h" |
16 | #include "qapi/qmp/qnum.h" | |
6b673957 | 17 | #include "qapi/qmp/qstring.h" |
1b76e838 HR |
18 | #include "qemu-common.h" |
19 | ||
20 | #include <math.h> | |
21 | ||
22 | /* Marks the end of the test_equality() argument list. | |
23 | * We cannot use NULL there because that is a valid argument. */ | |
24 | static QObject test_equality_end_of_arguments; | |
25 | ||
26 | /** | |
27 | * Test whether all variadic QObject *arguments are equal (@expected | |
28 | * is true) or whether they are all not equal (@expected is false). | |
29 | * Every QObject is tested to be equal to itself (to test | |
30 | * reflexivity), all tests are done both ways (to test symmetry), and | |
31 | * transitivity is not assumed but checked (each object is compared to | |
32 | * every other one). | |
33 | * | |
34 | * Note that qobject_is_equal() is not really an equivalence relation, | |
35 | * so this function may not be used for all objects (reflexivity is | |
36 | * not guaranteed, e.g. in the case of a QNum containing NaN). | |
37 | * | |
38 | * The @_ argument is required because a boolean may not be the last | |
39 | * argument before a variadic argument list (C11 7.16.1.4 para. 4). | |
40 | */ | |
41 | static void do_test_equality(bool expected, int _, ...) | |
42 | { | |
43 | va_list ap_count, ap_extract; | |
44 | QObject **args; | |
45 | int arg_count = 0; | |
46 | int i, j; | |
47 | ||
48 | va_start(ap_count, _); | |
49 | va_copy(ap_extract, ap_count); | |
50 | while (va_arg(ap_count, QObject *) != &test_equality_end_of_arguments) { | |
51 | arg_count++; | |
52 | } | |
53 | va_end(ap_count); | |
54 | ||
55 | args = g_new(QObject *, arg_count); | |
56 | for (i = 0; i < arg_count; i++) { | |
57 | args[i] = va_arg(ap_extract, QObject *); | |
58 | } | |
59 | va_end(ap_extract); | |
60 | ||
61 | for (i = 0; i < arg_count; i++) { | |
62 | g_assert(qobject_is_equal(args[i], args[i]) == true); | |
63 | ||
64 | for (j = i + 1; j < arg_count; j++) { | |
65 | g_assert(qobject_is_equal(args[i], args[j]) == expected); | |
66 | } | |
67 | } | |
87c258cd MAL |
68 | |
69 | g_free(args); | |
1b76e838 HR |
70 | } |
71 | ||
72 | #define check_equal(...) \ | |
73 | do_test_equality(true, 0, __VA_ARGS__, &test_equality_end_of_arguments) | |
74 | #define check_unequal(...) \ | |
75 | do_test_equality(false, 0, __VA_ARGS__, &test_equality_end_of_arguments) | |
76 | ||
77 | static void do_free_all(int _, ...) | |
78 | { | |
79 | va_list ap; | |
80 | QObject *obj; | |
81 | ||
82 | va_start(ap, _); | |
83 | while ((obj = va_arg(ap, QObject *)) != NULL) { | |
cb3e7f08 | 84 | qobject_unref(obj); |
1b76e838 HR |
85 | } |
86 | va_end(ap); | |
87 | } | |
88 | ||
89 | #define free_all(...) \ | |
90 | do_free_all(0, __VA_ARGS__, NULL) | |
91 | ||
92 | static void qobject_is_equal_null_test(void) | |
93 | { | |
94 | check_unequal(qnull(), NULL); | |
95 | } | |
96 | ||
97 | static void qobject_is_equal_num_test(void) | |
98 | { | |
99 | QNum *u0, *i0, *d0, *dnan, *um42, *im42, *dm42; | |
100 | ||
101 | u0 = qnum_from_uint(0u); | |
102 | i0 = qnum_from_int(0); | |
103 | d0 = qnum_from_double(0.0); | |
104 | dnan = qnum_from_double(NAN); | |
105 | um42 = qnum_from_uint((uint64_t)-42); | |
106 | im42 = qnum_from_int(-42); | |
107 | dm42 = qnum_from_double(-42.0); | |
108 | ||
109 | /* Integers representing a mathematically equal number should | |
110 | * compare equal */ | |
111 | check_equal(u0, i0); | |
112 | /* Doubles, however, are always unequal to integers */ | |
113 | check_unequal(u0, d0); | |
114 | check_unequal(i0, d0); | |
115 | ||
116 | /* Do not assume any object is equal to itself -- note however | |
117 | * that NaN cannot occur in a JSON object anyway. */ | |
118 | g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) == false); | |
119 | ||
120 | /* No unsigned overflow */ | |
121 | check_unequal(um42, im42); | |
122 | check_unequal(um42, dm42); | |
123 | check_unequal(im42, dm42); | |
124 | ||
125 | free_all(u0, i0, d0, dnan, um42, im42, dm42); | |
126 | } | |
127 | ||
128 | static void qobject_is_equal_bool_test(void) | |
129 | { | |
130 | QBool *btrue_0, *btrue_1, *bfalse_0, *bfalse_1; | |
131 | ||
132 | btrue_0 = qbool_from_bool(true); | |
133 | btrue_1 = qbool_from_bool(true); | |
134 | bfalse_0 = qbool_from_bool(false); | |
135 | bfalse_1 = qbool_from_bool(false); | |
136 | ||
137 | check_equal(btrue_0, btrue_1); | |
138 | check_equal(bfalse_0, bfalse_1); | |
139 | check_unequal(btrue_0, bfalse_0); | |
140 | ||
141 | free_all(btrue_0, btrue_1, bfalse_0, bfalse_1); | |
142 | } | |
143 | ||
144 | static void qobject_is_equal_string_test(void) | |
145 | { | |
146 | QString *str_base, *str_whitespace_0, *str_whitespace_1, *str_whitespace_2; | |
147 | QString *str_whitespace_3, *str_case, *str_built; | |
148 | ||
149 | str_base = qstring_from_str("foo"); | |
150 | str_whitespace_0 = qstring_from_str(" foo"); | |
151 | str_whitespace_1 = qstring_from_str("foo "); | |
152 | str_whitespace_2 = qstring_from_str("foo\b"); | |
153 | str_whitespace_3 = qstring_from_str("fooo\b"); | |
154 | str_case = qstring_from_str("Foo"); | |
155 | ||
156 | /* Should yield "foo" */ | |
ba891d68 | 157 | str_built = qstring_from_substr("form", 0, 2); |
1b76e838 HR |
158 | qstring_append_chr(str_built, 'o'); |
159 | ||
160 | check_unequal(str_base, str_whitespace_0, str_whitespace_1, | |
161 | str_whitespace_2, str_whitespace_3, str_case); | |
162 | ||
163 | check_equal(str_base, str_built); | |
164 | ||
165 | free_all(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_2, | |
166 | str_whitespace_3, str_case, str_built); | |
167 | } | |
168 | ||
169 | static void qobject_is_equal_list_test(void) | |
170 | { | |
171 | QList *list_0, *list_1, *list_cloned; | |
172 | QList *list_reordered, *list_longer, *list_shorter; | |
173 | ||
174 | list_0 = qlist_new(); | |
175 | list_1 = qlist_new(); | |
176 | list_reordered = qlist_new(); | |
177 | list_longer = qlist_new(); | |
178 | list_shorter = qlist_new(); | |
179 | ||
180 | qlist_append_int(list_0, 1); | |
181 | qlist_append_int(list_0, 2); | |
182 | qlist_append_int(list_0, 3); | |
183 | ||
184 | qlist_append_int(list_1, 1); | |
185 | qlist_append_int(list_1, 2); | |
186 | qlist_append_int(list_1, 3); | |
187 | ||
188 | qlist_append_int(list_reordered, 1); | |
189 | qlist_append_int(list_reordered, 3); | |
190 | qlist_append_int(list_reordered, 2); | |
191 | ||
192 | qlist_append_int(list_longer, 1); | |
193 | qlist_append_int(list_longer, 2); | |
194 | qlist_append_int(list_longer, 3); | |
195 | qlist_append_null(list_longer); | |
196 | ||
197 | qlist_append_int(list_shorter, 1); | |
198 | qlist_append_int(list_shorter, 2); | |
199 | ||
200 | list_cloned = qlist_copy(list_0); | |
201 | ||
202 | check_equal(list_0, list_1, list_cloned); | |
203 | check_unequal(list_0, list_reordered, list_longer, list_shorter); | |
204 | ||
205 | /* With a NaN in it, the list should no longer compare equal to | |
206 | * itself */ | |
207 | qlist_append(list_0, qnum_from_double(NAN)); | |
208 | g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false); | |
209 | ||
210 | free_all(list_0, list_1, list_cloned, list_reordered, list_longer, | |
211 | list_shorter); | |
212 | } | |
213 | ||
214 | static void qobject_is_equal_dict_test(void) | |
215 | { | |
216 | Error *local_err = NULL; | |
217 | QDict *dict_0, *dict_1, *dict_cloned; | |
218 | QDict *dict_different_key, *dict_different_value, *dict_different_null_key; | |
219 | QDict *dict_longer, *dict_shorter, *dict_nested; | |
220 | QDict *dict_crumpled; | |
221 | ||
222 | dict_0 = qdict_new(); | |
223 | dict_1 = qdict_new(); | |
224 | dict_different_key = qdict_new(); | |
225 | dict_different_value = qdict_new(); | |
226 | dict_different_null_key = qdict_new(); | |
227 | dict_longer = qdict_new(); | |
228 | dict_shorter = qdict_new(); | |
229 | dict_nested = qdict_new(); | |
230 | ||
231 | qdict_put_int(dict_0, "f.o", 1); | |
232 | qdict_put_int(dict_0, "bar", 2); | |
233 | qdict_put_int(dict_0, "baz", 3); | |
234 | qdict_put_null(dict_0, "null"); | |
235 | ||
236 | qdict_put_int(dict_1, "f.o", 1); | |
237 | qdict_put_int(dict_1, "bar", 2); | |
238 | qdict_put_int(dict_1, "baz", 3); | |
239 | qdict_put_null(dict_1, "null"); | |
240 | ||
241 | qdict_put_int(dict_different_key, "F.o", 1); | |
242 | qdict_put_int(dict_different_key, "bar", 2); | |
243 | qdict_put_int(dict_different_key, "baz", 3); | |
244 | qdict_put_null(dict_different_key, "null"); | |
245 | ||
246 | qdict_put_int(dict_different_value, "f.o", 42); | |
247 | qdict_put_int(dict_different_value, "bar", 2); | |
248 | qdict_put_int(dict_different_value, "baz", 3); | |
249 | qdict_put_null(dict_different_value, "null"); | |
250 | ||
251 | qdict_put_int(dict_different_null_key, "f.o", 1); | |
252 | qdict_put_int(dict_different_null_key, "bar", 2); | |
253 | qdict_put_int(dict_different_null_key, "baz", 3); | |
254 | qdict_put_null(dict_different_null_key, "none"); | |
255 | ||
256 | qdict_put_int(dict_longer, "f.o", 1); | |
257 | qdict_put_int(dict_longer, "bar", 2); | |
258 | qdict_put_int(dict_longer, "baz", 3); | |
259 | qdict_put_int(dict_longer, "xyz", 4); | |
260 | qdict_put_null(dict_longer, "null"); | |
261 | ||
262 | qdict_put_int(dict_shorter, "f.o", 1); | |
263 | qdict_put_int(dict_shorter, "bar", 2); | |
264 | qdict_put_int(dict_shorter, "baz", 3); | |
265 | ||
266 | qdict_put(dict_nested, "f", qdict_new()); | |
267 | qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1); | |
268 | qdict_put_int(dict_nested, "bar", 2); | |
269 | qdict_put_int(dict_nested, "baz", 3); | |
270 | qdict_put_null(dict_nested, "null"); | |
271 | ||
272 | dict_cloned = qdict_clone_shallow(dict_0); | |
273 | ||
274 | check_equal(dict_0, dict_1, dict_cloned); | |
275 | check_unequal(dict_0, dict_different_key, dict_different_value, | |
276 | dict_different_null_key, dict_longer, dict_shorter, | |
277 | dict_nested); | |
278 | ||
7dc847eb | 279 | dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &local_err)); |
1b76e838 HR |
280 | g_assert(!local_err); |
281 | check_equal(dict_crumpled, dict_nested); | |
282 | ||
283 | qdict_flatten(dict_nested); | |
284 | check_equal(dict_0, dict_nested); | |
285 | ||
286 | /* Containing an NaN value will make this dict compare unequal to | |
287 | * itself */ | |
288 | qdict_put(dict_0, "NaN", qnum_from_double(NAN)); | |
289 | g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false); | |
290 | ||
291 | free_all(dict_0, dict_1, dict_cloned, dict_different_key, | |
292 | dict_different_value, dict_different_null_key, dict_longer, | |
293 | dict_shorter, dict_nested, dict_crumpled); | |
294 | } | |
295 | ||
296 | static void qobject_is_equal_conversion_test(void) | |
297 | { | |
298 | QNum *u0, *i0, *d0; | |
299 | QString *s0, *s_empty; | |
300 | QBool *bfalse; | |
301 | ||
302 | u0 = qnum_from_uint(0u); | |
303 | i0 = qnum_from_int(0); | |
304 | d0 = qnum_from_double(0.0); | |
305 | s0 = qstring_from_str("0"); | |
306 | s_empty = qstring_new(); | |
307 | bfalse = qbool_from_bool(false); | |
308 | ||
309 | /* No automatic type conversion */ | |
310 | check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL); | |
311 | check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL); | |
312 | check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL); | |
313 | ||
314 | free_all(u0, i0, d0, s0, s_empty, bfalse); | |
315 | } | |
316 | ||
317 | int main(int argc, char **argv) | |
318 | { | |
319 | g_test_init(&argc, &argv, NULL); | |
320 | ||
321 | g_test_add_func("/public/qobject_is_equal_null", | |
322 | qobject_is_equal_null_test); | |
323 | g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_test); | |
324 | g_test_add_func("/public/qobject_is_equal_bool", | |
325 | qobject_is_equal_bool_test); | |
326 | g_test_add_func("/public/qobject_is_equal_string", | |
327 | qobject_is_equal_string_test); | |
328 | g_test_add_func("/public/qobject_is_equal_list", | |
329 | qobject_is_equal_list_test); | |
330 | g_test_add_func("/public/qobject_is_equal_dict", | |
331 | qobject_is_equal_dict_test); | |
332 | g_test_add_func("/public/qobject_is_equal_conversion", | |
333 | qobject_is_equal_conversion_test); | |
334 | ||
335 | return g_test_run(); | |
336 | } |