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 */ 9 10 #include "qemu/osdep.h" 11 #include "qapi/error.h" 12 #include "qapi/qmp/qbool.h" 13 #include "qapi/qmp/qdict.h" 14 #include "qapi/qmp/qlist.h" 15 #include "qapi/qmp/qnull.h" 16 #include "qapi/qmp/qnum.h" 17 #include "qapi/qmp/qstring.h" 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 } 68 69 g_free(args); 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 qobject_is_equal_null_test(void) 78 { 79 check_unequal(qnull(), NULL); 80 } 81 82 static void qobject_is_equal_num_test(void) 83 { 84 g_autoptr(QNum) u0 = qnum_from_uint(0u); 85 g_autoptr(QNum) i0 = qnum_from_int(0); 86 g_autoptr(QNum) d0 = qnum_from_double(0.0); 87 g_autoptr(QNum) dnan = qnum_from_double(NAN); 88 g_autoptr(QNum) um42 = qnum_from_uint((uint64_t)-42); 89 g_autoptr(QNum) im42 = qnum_from_int(-42); 90 g_autoptr(QNum) dm42 = qnum_from_double(-42.0); 91 92 93 /* Integers representing a mathematically equal number should 94 * compare equal */ 95 check_equal(u0, i0); 96 /* Doubles, however, are always unequal to integers */ 97 check_unequal(u0, d0); 98 check_unequal(i0, d0); 99 100 /* Do not assume any object is equal to itself -- note however 101 * that NaN cannot occur in a JSON object anyway. */ 102 g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) == false); 103 104 /* No unsigned overflow */ 105 check_unequal(um42, im42); 106 check_unequal(um42, dm42); 107 check_unequal(im42, dm42); 108 } 109 110 static void qobject_is_equal_bool_test(void) 111 { 112 g_autoptr(QBool) btrue_0 = qbool_from_bool(true); 113 g_autoptr(QBool) btrue_1 = qbool_from_bool(true); 114 g_autoptr(QBool) bfalse_0 = qbool_from_bool(false); 115 g_autoptr(QBool) bfalse_1 = qbool_from_bool(false); 116 117 check_equal(btrue_0, btrue_1); 118 check_equal(bfalse_0, bfalse_1); 119 check_unequal(btrue_0, bfalse_0); 120 } 121 122 static void qobject_is_equal_string_test(void) 123 { 124 g_autoptr(QString) str_base = qstring_from_str("foo"); 125 g_autoptr(QString) str_whitespace_0 = qstring_from_str(" foo"); 126 g_autoptr(QString) str_whitespace_1 = qstring_from_str("foo "); 127 g_autoptr(QString) str_whitespace_2 = qstring_from_str("foo\b"); 128 g_autoptr(QString) str_whitespace_3 = qstring_from_str("fooo\b"); 129 g_autoptr(QString) str_case = qstring_from_str("Foo"); 130 /* Should yield "foo" */ 131 g_autoptr(QString) str_built = qstring_from_substr("buffoon", 3, 6); 132 133 check_unequal(str_base, str_whitespace_0, str_whitespace_1, 134 str_whitespace_2, str_whitespace_3, str_case); 135 136 check_equal(str_base, str_built); 137 } 138 139 static void qobject_is_equal_list_test(void) 140 { 141 g_autoptr(QList) list_0 = qlist_new(); 142 g_autoptr(QList) list_1 = qlist_new(); 143 g_autoptr(QList) list_reordered = qlist_new(); 144 g_autoptr(QList) list_longer = qlist_new(); 145 g_autoptr(QList) list_shorter = qlist_new(); 146 g_autoptr(QList) list_cloned = NULL; 147 148 qlist_append_int(list_0, 1); 149 qlist_append_int(list_0, 2); 150 qlist_append_int(list_0, 3); 151 152 qlist_append_int(list_1, 1); 153 qlist_append_int(list_1, 2); 154 qlist_append_int(list_1, 3); 155 156 qlist_append_int(list_reordered, 1); 157 qlist_append_int(list_reordered, 3); 158 qlist_append_int(list_reordered, 2); 159 160 qlist_append_int(list_longer, 1); 161 qlist_append_int(list_longer, 2); 162 qlist_append_int(list_longer, 3); 163 qlist_append_null(list_longer); 164 165 qlist_append_int(list_shorter, 1); 166 qlist_append_int(list_shorter, 2); 167 168 list_cloned = qlist_copy(list_0); 169 170 check_equal(list_0, list_1, list_cloned); 171 check_unequal(list_0, list_reordered, list_longer, list_shorter); 172 173 /* With a NaN in it, the list should no longer compare equal to 174 * itself */ 175 qlist_append(list_0, qnum_from_double(NAN)); 176 g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false); 177 } 178 179 static void qobject_is_equal_dict_test(void) 180 { 181 g_autoptr(QDict) dict_cloned = NULL; 182 g_autoptr(QDict) dict_crumpled = NULL; 183 g_autoptr(QDict) dict_0 = qdict_new(); 184 g_autoptr(QDict) dict_1 = qdict_new(); 185 g_autoptr(QDict) dict_different_key = qdict_new(); 186 g_autoptr(QDict) dict_different_value = qdict_new(); 187 g_autoptr(QDict) dict_different_null_key = qdict_new(); 188 g_autoptr(QDict) dict_longer = qdict_new(); 189 g_autoptr(QDict) dict_shorter = qdict_new(); 190 g_autoptr(QDict) dict_nested = qdict_new(); 191 192 qdict_put_int(dict_0, "f.o", 1); 193 qdict_put_int(dict_0, "bar", 2); 194 qdict_put_int(dict_0, "baz", 3); 195 qdict_put_null(dict_0, "null"); 196 197 qdict_put_int(dict_1, "f.o", 1); 198 qdict_put_int(dict_1, "bar", 2); 199 qdict_put_int(dict_1, "baz", 3); 200 qdict_put_null(dict_1, "null"); 201 202 qdict_put_int(dict_different_key, "F.o", 1); 203 qdict_put_int(dict_different_key, "bar", 2); 204 qdict_put_int(dict_different_key, "baz", 3); 205 qdict_put_null(dict_different_key, "null"); 206 207 qdict_put_int(dict_different_value, "f.o", 42); 208 qdict_put_int(dict_different_value, "bar", 2); 209 qdict_put_int(dict_different_value, "baz", 3); 210 qdict_put_null(dict_different_value, "null"); 211 212 qdict_put_int(dict_different_null_key, "f.o", 1); 213 qdict_put_int(dict_different_null_key, "bar", 2); 214 qdict_put_int(dict_different_null_key, "baz", 3); 215 qdict_put_null(dict_different_null_key, "none"); 216 217 qdict_put_int(dict_longer, "f.o", 1); 218 qdict_put_int(dict_longer, "bar", 2); 219 qdict_put_int(dict_longer, "baz", 3); 220 qdict_put_int(dict_longer, "xyz", 4); 221 qdict_put_null(dict_longer, "null"); 222 223 qdict_put_int(dict_shorter, "f.o", 1); 224 qdict_put_int(dict_shorter, "bar", 2); 225 qdict_put_int(dict_shorter, "baz", 3); 226 227 qdict_put(dict_nested, "f", qdict_new()); 228 qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1); 229 qdict_put_int(dict_nested, "bar", 2); 230 qdict_put_int(dict_nested, "baz", 3); 231 qdict_put_null(dict_nested, "null"); 232 233 dict_cloned = qdict_clone_shallow(dict_0); 234 235 check_equal(dict_0, dict_1, dict_cloned); 236 check_unequal(dict_0, dict_different_key, dict_different_value, 237 dict_different_null_key, dict_longer, dict_shorter, 238 dict_nested); 239 240 dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &error_abort)); 241 check_equal(dict_crumpled, dict_nested); 242 243 qdict_flatten(dict_nested); 244 check_equal(dict_0, dict_nested); 245 246 /* Containing an NaN value will make this dict compare unequal to 247 * itself */ 248 qdict_put(dict_0, "NaN", qnum_from_double(NAN)); 249 g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false); 250 } 251 252 static void qobject_is_equal_conversion_test(void) 253 { 254 g_autoptr(QNum) u0 = qnum_from_uint(0u); 255 g_autoptr(QNum) i0 = qnum_from_int(0); 256 g_autoptr(QNum) d0 = qnum_from_double(0.0); 257 g_autoptr(QString) s0 = qstring_from_str("0"); 258 g_autoptr(QString) s_empty = qstring_new(); 259 g_autoptr(QBool) bfalse = qbool_from_bool(false); 260 261 /* No automatic type conversion */ 262 check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL); 263 check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL); 264 check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL); 265 } 266 267 int main(int argc, char **argv) 268 { 269 g_test_init(&argc, &argv, NULL); 270 271 g_test_add_func("/public/qobject_is_equal_null", 272 qobject_is_equal_null_test); 273 g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_test); 274 g_test_add_func("/public/qobject_is_equal_bool", 275 qobject_is_equal_bool_test); 276 g_test_add_func("/public/qobject_is_equal_string", 277 qobject_is_equal_string_test); 278 g_test_add_func("/public/qobject_is_equal_list", 279 qobject_is_equal_list_test); 280 g_test_add_func("/public/qobject_is_equal_dict", 281 qobject_is_equal_dict_test); 282 g_test_add_func("/public/qobject_is_equal_conversion", 283 qobject_is_equal_conversion_test); 284 285 return g_test_run(); 286 } 287