qemu/tests/unit/check-qobject.c
<<
>>
Prefs
   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. */
  23static 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 */
  40static 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
  76static void qobject_is_equal_null_test(void)
  77{
  78    check_unequal(qnull(), NULL);
  79}
  80
  81static 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
 109static 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
 121static 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
 138static 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
 178static 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
 244static 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
 259int 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