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 19 #include <math.h> 20 21 /* Marks the end of the test_equality() argument list. 22 * We cannot use NULL there because that is a valid argument. */ 23 static QObject test_equality_end_of_arguments; 24 25 /** 26 * Test whether all variadic QObject *arguments are equal (@expected 27 * is true) or whether they are all not equal (@expected is false). 28 * Every QObject is tested to be equal to itself (to test 29 * reflexivity), all tests are done both ways (to test symmetry), and 30 * transitivity is not assumed but checked (each object is compared to 31 * every other one). 32 * 33 * Note that qobject_is_equal() is not really an equivalence relation, 34 * so this function may not be used for all objects (reflexivity is 35 * not guaranteed, e.g. in the case of a QNum containing NaN). 36 * 37 * The @_ argument is required because a boolean may not be the last 38 * argument before a variadic argument list (C11 7.16.1.4 para. 4). 39 */ 40 static void do_test_equality(bool expected, int _, ...) 41 { 42 va_list ap_count, ap_extract; 43 QObject **args; 44 int arg_count = 0; 45 int i, j; 46 47 va_start(ap_count, _); 48 va_copy(ap_extract, ap_count); 49 while (va_arg(ap_count, QObject *) != &test_equality_end_of_arguments) { 50 arg_count++; 51 } 52 va_end(ap_count); 53 54 args = g_new(QObject *, arg_count); 55 for (i = 0; i < arg_count; i++) { 56 args[i] = va_arg(ap_extract, QObject *); 57 } 58 va_end(ap_extract); 59 60 for (i = 0; i < arg_count; i++) { 61 g_assert(qobject_is_equal(args[i], args[i]) == true); 62 63 for (j = i + 1; j < arg_count; j++) { 64 g_assert(qobject_is_equal(args[i], args[j]) == expected); 65 } 66 } 67 68 g_free(args); 69 } 70 71 #define check_equal(...) \ 72 do_test_equality(true, 0, __VA_ARGS__, &test_equality_end_of_arguments) 73 #define check_unequal(...) \ 74 do_test_equality(false, 0, __VA_ARGS__, &test_equality_end_of_arguments) 75 76 static void qobject_is_equal_null_test(void) 77 { 78 check_unequal(qnull(), NULL); 79 } 80 81 static void qobject_is_equal_num_test(void) 82 { 83 g_autoptr(QNum) u0 = qnum_from_uint(0u); 84 g_autoptr(QNum) i0 = qnum_from_int(0); 85 g_autoptr(QNum) d0 = qnum_from_double(0.0); 86 g_autoptr(QNum) dnan = qnum_from_double(NAN); 87 g_autoptr(QNum) um42 = qnum_from_uint((uint64_t)-42); 88 g_autoptr(QNum) im42 = qnum_from_int(-42); 89 g_autoptr(QNum) dm42 = qnum_from_double(-42.0); 90 91 92 /* Integers representing a mathematically equal number should 93 * compare equal */ 94 check_equal(u0, i0); 95 /* Doubles, however, are always unequal to integers */ 96 check_unequal(u0, d0); 97 check_unequal(i0, d0); 98 99 /* Do not assume any object is equal to itself -- note however 100 * that NaN cannot occur in a JSON object anyway. */ 101 g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) == false); 102 103 /* No unsigned overflow */ 104 check_unequal(um42, im42); 105 check_unequal(um42, dm42); 106 check_unequal(im42, dm42); 107 } 108 109 static void qobject_is_equal_bool_test(void) 110 { 111 g_autoptr(QBool) btrue_0 = qbool_from_bool(true); 112 g_autoptr(QBool) btrue_1 = qbool_from_bool(true); 113 g_autoptr(QBool) bfalse_0 = qbool_from_bool(false); 114 g_autoptr(QBool) bfalse_1 = qbool_from_bool(false); 115 116 check_equal(btrue_0, btrue_1); 117 check_equal(bfalse_0, bfalse_1); 118 check_unequal(btrue_0, bfalse_0); 119 } 120 121 static void qobject_is_equal_string_test(void) 122 { 123 g_autoptr(QString) str_base = qstring_from_str("foo"); 124 g_autoptr(QString) str_whitespace_0 = qstring_from_str(" foo"); 125 g_autoptr(QString) str_whitespace_1 = qstring_from_str("foo "); 126 g_autoptr(QString) str_whitespace_2 = qstring_from_str("foo\b"); 127 g_autoptr(QString) str_whitespace_3 = qstring_from_str("fooo\b"); 128 g_autoptr(QString) str_case = qstring_from_str("Foo"); 129 /* Should yield "foo" */ 130 g_autoptr(QString) str_built = qstring_from_substr("buffoon", 3, 6); 131 132 check_unequal(str_base, str_whitespace_0, str_whitespace_1, 133 str_whitespace_2, str_whitespace_3, str_case); 134 135 check_equal(str_base, str_built); 136 } 137 138 static void qobject_is_equal_list_test(void) 139 { 140 g_autoptr(QList) list_0 = qlist_new(); 141 g_autoptr(QList) list_1 = qlist_new(); 142 g_autoptr(QList) list_reordered = qlist_new(); 143 g_autoptr(QList) list_longer = qlist_new(); 144 g_autoptr(QList) list_shorter = qlist_new(); 145 g_autoptr(QList) list_cloned = NULL; 146 147 qlist_append_int(list_0, 1); 148 qlist_append_int(list_0, 2); 149 qlist_append_int(list_0, 3); 150 151 qlist_append_int(list_1, 1); 152 qlist_append_int(list_1, 2); 153 qlist_append_int(list_1, 3); 154 155 qlist_append_int(list_reordered, 1); 156 qlist_append_int(list_reordered, 3); 157 qlist_append_int(list_reordered, 2); 158 159 qlist_append_int(list_longer, 1); 160 qlist_append_int(list_longer, 2); 161 qlist_append_int(list_longer, 3); 162 qlist_append_null(list_longer); 163 164 qlist_append_int(list_shorter, 1); 165 qlist_append_int(list_shorter, 2); 166 167 list_cloned = qlist_copy(list_0); 168 169 check_equal(list_0, list_1, list_cloned); 170 check_unequal(list_0, list_reordered, list_longer, list_shorter); 171 172 /* With a NaN in it, the list should no longer compare equal to 173 * itself */ 174 qlist_append(list_0, qnum_from_double(NAN)); 175 g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false); 176 } 177 178 static void qobject_is_equal_dict_test(void) 179 { 180 g_autoptr(QDict) dict_cloned = NULL; 181 g_autoptr(QDict) dict_0 = qdict_new(); 182 g_autoptr(QDict) dict_1 = qdict_new(); 183 g_autoptr(QDict) dict_different_key = qdict_new(); 184 g_autoptr(QDict) dict_different_value = qdict_new(); 185 g_autoptr(QDict) dict_different_null_key = qdict_new(); 186 g_autoptr(QDict) dict_longer = qdict_new(); 187 g_autoptr(QDict) dict_shorter = qdict_new(); 188 g_autoptr(QDict) dict_nested = qdict_new(); 189 190 qdict_put_int(dict_0, "f.o", 1); 191 qdict_put_int(dict_0, "bar", 2); 192 qdict_put_int(dict_0, "baz", 3); 193 qdict_put_null(dict_0, "null"); 194 195 qdict_put_int(dict_1, "f.o", 1); 196 qdict_put_int(dict_1, "bar", 2); 197 qdict_put_int(dict_1, "baz", 3); 198 qdict_put_null(dict_1, "null"); 199 200 qdict_put_int(dict_different_key, "F.o", 1); 201 qdict_put_int(dict_different_key, "bar", 2); 202 qdict_put_int(dict_different_key, "baz", 3); 203 qdict_put_null(dict_different_key, "null"); 204 205 qdict_put_int(dict_different_value, "f.o", 42); 206 qdict_put_int(dict_different_value, "bar", 2); 207 qdict_put_int(dict_different_value, "baz", 3); 208 qdict_put_null(dict_different_value, "null"); 209 210 qdict_put_int(dict_different_null_key, "f.o", 1); 211 qdict_put_int(dict_different_null_key, "bar", 2); 212 qdict_put_int(dict_different_null_key, "baz", 3); 213 qdict_put_null(dict_different_null_key, "none"); 214 215 qdict_put_int(dict_longer, "f.o", 1); 216 qdict_put_int(dict_longer, "bar", 2); 217 qdict_put_int(dict_longer, "baz", 3); 218 qdict_put_int(dict_longer, "xyz", 4); 219 qdict_put_null(dict_longer, "null"); 220 221 qdict_put_int(dict_shorter, "f.o", 1); 222 qdict_put_int(dict_shorter, "bar", 2); 223 qdict_put_int(dict_shorter, "baz", 3); 224 225 qdict_put(dict_nested, "f", qdict_new()); 226 qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1); 227 qdict_put_int(dict_nested, "bar", 2); 228 qdict_put_int(dict_nested, "baz", 3); 229 qdict_put_null(dict_nested, "null"); 230 231 dict_cloned = qdict_clone_shallow(dict_0); 232 233 check_equal(dict_0, dict_1, dict_cloned); 234 check_unequal(dict_0, dict_different_key, dict_different_value, 235 dict_different_null_key, dict_longer, dict_shorter, 236 dict_nested); 237 238 /* Containing an NaN value will make this dict compare unequal to 239 * itself */ 240 qdict_put(dict_0, "NaN", qnum_from_double(NAN)); 241 g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false); 242 } 243 244 static void qobject_is_equal_conversion_test(void) 245 { 246 g_autoptr(QNum) u0 = qnum_from_uint(0u); 247 g_autoptr(QNum) i0 = qnum_from_int(0); 248 g_autoptr(QNum) d0 = qnum_from_double(0.0); 249 g_autoptr(QString) s0 = qstring_from_str("0"); 250 g_autoptr(QString) s_empty = qstring_new(); 251 g_autoptr(QBool) bfalse = qbool_from_bool(false); 252 253 /* No automatic type conversion */ 254 check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL); 255 check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL); 256 check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL); 257 } 258 259 int main(int argc, char **argv) 260 { 261 g_test_init(&argc, &argv, NULL); 262 263 g_test_add_func("/public/qobject_is_equal_null", 264 qobject_is_equal_null_test); 265 g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_test); 266 g_test_add_func("/public/qobject_is_equal_bool", 267 qobject_is_equal_bool_test); 268 g_test_add_func("/public/qobject_is_equal_string", 269 qobject_is_equal_string_test); 270 g_test_add_func("/public/qobject_is_equal_list", 271 qobject_is_equal_list_test); 272 g_test_add_func("/public/qobject_is_equal_dict", 273 qobject_is_equal_dict_test); 274 g_test_add_func("/public/qobject_is_equal_conversion", 275 qobject_is_equal_conversion_test); 276 277 return g_test_run(); 278 } 279